Next.js: 从 _app 之外的 node_modules 导入正确范围的 css

创建于 2020-04-21  ·  49评论  ·  资料来源: vercel/next.js

错误报告

描述错误

在页面中无法从包中导入样式表,因为 next 会引发此错误:

Global CSS cannot be imported from files other than your Custom <App>. Please move all global CSS imports to pages/_app.tsx.
Read more: https://err.sh/next.js/css-global

虽然我明白这是从何而来的,但它使代码拆分变得不可能。 如果我从组件库中导入一个组件,我也需要导入 CSS。 可能有些库没有正确确定其选择器的范围,但这不应阻止我覆盖此警告。 从库中导入的 CSS 本质上并不是“全局的”。

再现

  1. import "my-library/index.css"
  2. yarn dev
  3. 我从上面得到错误

预期行为

该文件应该被导入。

我看到这些可能的解决方案:

  • next.config.js全局标志
  • 导入注释注释
  • 警告而不是完全阻止它
  • 检查 CSS 是否只包含作用域选择器

附加上下文

之前已经有过这方面的讨论。

来自博客

由于样式表本质上是全局的,它们必须在自定义零件。 这对于避免全局样式的类名和顺序冲突是必要的。

我不同意这种说法,理由是外部库可以使用 CSS 模块并将它们打包为 CSS 文件进行导入。 这是完全有效和普遍的做法,没有副作用

#10059

此问题已关闭,因为_app的全局导入是那里的正确选择。
评论描述了确切的问题,但由于问题已关闭,因此没有任何回应。 不过,这条评论得到了很多积极的反应,所以我想我不是唯一遇到这个问题的人。

#10975

好像没有关系。

#9830

可能有关系,但我不确定。

我的用例

我写了很多自定义艺术品和交互式插图的长篇文章。 文章使用带有 React 组件的私有 npm 包,这些组件使用相当多的 CSS 呈现 SVG。 这些包使用 CSS 模块并导出index.jsindex.css 。 将所有 CSS 文件添加到_app会导致加载所有 CSS,即使人们在主页、联系表单或任何其他文章上,即使它 100% 未使用。 这也不利于让文件系统处理您的页面,因为几乎每个页面都对应于_app的 CSS 导入。

story 8 feature request

最有用的评论

我们将允许在下周(在 Canary 上)将 CSS 从node_modules导入任何组件文件! 当它准备好测试时,我们会在这里发布。

所有49条评论

我正面临同样的问题,只是尝试使用 Linaria,它限定了自己的类名。 尽管它生成的 css 文件不以.module.css结尾,但它们是“模块”。 我需要一种与库集成的简单方法。

为什么甚至再次切换到nextjs?

我还希望能够在 node_modules 之外使用 GlobalCSS。 这将帮助我们逐步采用 CSS 模块

是的,这是非常重要的! 许多 npm 包不适用于 nextjs,但适用于 CRA 或其他框架

对于任何其他人试图将它与 dart sass 的 js 实现一起用于诸如@use支持和 sass 模块之类的东西,如果您有任何其他依赖于 node-sass 的节点模块,则默认下一个设置将使用 node-sass 而不是 sass。 在本地,我通过执行以下操作修复了它:

// example next.config.js
module.exports = {
webpack(config, options) {
  config.module.rules.forEach(rule => {
          if (rule.oneOf) {
            const nestedScss = rule.oneOf.find((one) => {
              return one.test
                && 'some.scss'.match(one.test) 
                && one.issuer 
                && one.issuer.include 
                && one.issuer.include.includes('_app');
            });
            if (nestedScss) {
              const sassLoader = nestedScss.use.find(u => u.loader.includes('sass-loader'));
              // Set implementation to sass instead of node-sass here.
              sassLoader.options.implementation = require('sass');
            }
          }
        })
  }
}

然后,您需要在_app.js导入您的 scss 文件。

@smurrayatwork这是黑客而不是编码抱歉

此外,对它的唯一限制是 _app.js 有点麻烦。

如果我们不打算在任何地方都支持 CSS 引用,那么我们是否可以让它也可以通过_app的直接依赖项(在其他任何地方都没有引用)来引用 CSS?
IE。 如果_app (而不是其他任何地方)需要它就可以了,这会给 CSS 一个基于导入的确定性顺序。

这并不理想,但是我的用例是我拥有一个由多个应用程序共享的代码库,这些应用程序导入一个导入共享 CSS 的共享模块。 我不想在 _app.js 中为每个应用程序复制那些共享的 CSS 导入。 目前要解决这个问题,我必须做一些花哨的 js 元编程,因为我们不能在其他模块中使用 css。

相反,我希望我目前的工作方法是我有一个“应用程序工厂”,它可以导入所有共享的 CSS。 _app 然后使用工厂并在共享的基础上导入它自己的 CSS。

我正在添加https://github.com/vercel/next.js/discussions/13991,因为我认为它与这个问题有关。

+100 到这个。 我必须将节点模块 css 文件复制并粘贴到我的项目中,并在其上添加 .module.css

这是另一个例子。

在包pdf-viewer-reactjs的情况下,它的依赖项也需要从_app.js导入的 CSS。

这使整个应用程序的 CSS 变得臃肿,我不确定现阶段是否会发生冲突。

导入 'react-quill/dist/quill.snow.css';
导入 'react-image-crop/dist/ReactCrop.css';
导入'../../node_modules/material-design-icons/iconfont/material-icons.css';
导入'../../node_modules/bulma/css/bulma.css';
导入'../../node_modules/bulma-helpers/css/bulma-helpers.min.css';

此外,以下输出到控制台:

警告 - ./node_modules/material-design-icons/iconfont/material-icons.css
不能从 node_modules 中导入全局 CSS。
阅读更多: https :
位置:node_modules/pdf-viewer-reactjs/dist/pdf-viewer-reactjs.js

./node_modules/bulma/css/bulma.css
不能从 node_modules 中导入全局 CSS。
阅读更多: https :
位置:node_modules/pdf-viewer-reactjs/dist/pdf-viewer-reactjs.js

./node_modules/bulma-helpers/css/bulma-helpers.min.css
不能从 node_modules 中导入全局 CSS。
阅读更多: https :
位置:node_modules/pdf-viewer-reactjs/dist/pdf-viewer-reactjs.js

./node_modules/material-design-icons/iconfont/material-icons.css
模块构建失败:错误:最终加载器(./node_modules/next/dist/build/webpack/loaders/error-loader.js)没有返回缓冲区或字符串

./node_modules/bulma/css/bulma.css
模块构建失败:错误:最终加载器(./node_modules/next/dist/build/webpack/loaders/error-loader.js)没有返回缓冲区或字符串

./node_modules/bulma-helpers/css/bulma-helpers.min.css
模块构建失败:错误:最终加载器(./node_modules/next/dist/build/webpack/loaders/error-loader.js)没有返回缓冲区或字符串

你好 ! 有人解决了这个问题吗?如何解决? 因此,我无法导入这么多节点模块。

也许可以通过next.config.js激活在组件中使用全局样式,或者可以显示针对全局样式的丑陋控制台警告,以防担心违反 NextJS 的最佳实践/意见。

但这对于从 CRA > NextJS 转换的用户来说很重要。 这对我们来说是一个障碍,我们不能切换,然后逐步采用 CSS 模块之类的东西。

仍然无法解决这个问题。 为了我自己的需要,我使用了自定义 CSS 处理程序,但这会禁用内置 CSS 支持,但这可能不是所有情况下的好解决方案。 下面是不鼓励的,只在包作者整理出来之前使用

下一个.config.js

const withCSS = require('@zeit/next-css');
const withPlugins = require('next-compose-plugins');
...
module.exports = withPlugins([
...
withCSS,
]);

@abdelrahmantoptal的你知道如何

它似乎适用于 CSS,但在遇到 SASS 导入时会引发错误:

error - ./src/components/layouts/Footer.scss 1:0
Module parse failed: Unexpected character '@' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <strong i="8">@import</strong> 'styles/vars';
| 
| footer {

所以我尝试在使用 withCSS 插件之前向 webpack 配置添加一个 SASS 加载器

      config.module.rules.push({
        test: /\.s[ac]ss$/i,
        use: [
          // Creates `style` nodes from JS strings
          'style-loader',
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Sass to CSS
          'sass-loader'
        ]
      });

但这导致:

error - ./src/components/App.scss
ReferenceError: self is not defined

我也尝试替换@zeit/next-sass ,但这导致了同样的错误:

error - ./src/components/App.scss
ReferenceError: self is not defined

关于如何调整代码以使用 SASS 的任何建议?

我现在已经抛弃了 Gatsby 和 Next.js,因为它们很小,但非常阻塞,自以为是的功能,例如这个。 我现在无法为 CKEditor 5 使用CodeBlock插件,因为我无法解决这个错误。 应该总是有一些方法可以圈出这些配置。

@Timer或 Vercel 的某个人那里获得有关此问题的消息会非常有帮助。 这是 Next.js 的一个大问题。 有计划解决这个问题吗?

同意! 在节点模块中使用 css 是非常普遍的。 作为开发人员,我无法控制其他开发人员如何构建他们的节点模块,其他开发人员也不期望将 css 放入节点模块会破坏 Web 框架。

@OssiPesonen你见过这个吗? 这种解决方法并不理想,但同时为我解决了问题。

@OssiPesonen你见过这个吗? 这种解决方法并不理想,但同时为我解决了问题。

我不明白这有什么帮助? 问题不在于我必须从节点模块手动导入一些 CSS 文件。 问题是 npm 包自己做 CSS 导入。 包含这样一行的包:

import '../theme/stylesheet.css'

将导致 next.js 报复性崩溃。 显然维护者的建议是:

联系维护者并要求他们发布其依赖项的编译版本。

人们生活在什么样的幻想世界里,他们想象你可以联系维护人员,要求他们以非常快的速度为你重新编译他们的包? 这将阻碍任何人数周! 这张票已经开放4个月了。 在处理快速移动的项目时,这是不可接受的。

我们将允许在下周(在 Canary 上)将 CSS 从node_modules导入任何组件文件! 当它准备好测试时,我们会在这里发布。

如果有人在发布之前需要这个,我可以使用next-transpile-modules插件从导入 CSS 的node_modules转换模块。 对我来说就像一种魅力。

@BrandonE似乎 next-transpile-modules 仍然需要具有名为*.module.css的模块。 你找到解决办法了吗?

@rjoaopereira我不能说我对其中的任何一个工作原理有深刻的理解,但是我导入 CSS 的大部分node_modules只能与@zeit/next-css插件一起使用。 只有一个没有,此时转译解决了这个问题。 远不是一个优雅的解决方案,我希望 Next.js 的未来版本能让我们花更少的时间在 Babel / Webpack 炼金术上,而将更多的时间花在制作 Web 应用程序上。

我几乎可以通过以下更改来使用它。

下一个 9.5.3
下一个转译模块 4.1.0
情绪激动的第一方组件。
混合了 css 模块和全局 css 的 3rd 方组件

scopedcomponents将替换为正在使用的第 3 方组件

//next.config.js
const withCustomWebpack = require("./webpack-custom.config");
const withNextCSSOverride = require("./next.config.css");
const withTM = require("next-transpile-modules")(["@scopedcomponents"]);

module.exports = withCustomWebpack(
  withTM(
    withNextCSSOverride({
      poweredByHeader: false
    })
  )
);

///next.config.css.js
const {
  getCssModuleLocalIdent
} = require("next/dist/build/webpack/config/blocks/css/loaders/getCssModuleLocalIdent");
const path = require("path");
/**
 * Stolen from https://stackoverflow.com/questions/10776600/testing-for-equality-of-regular-expressions
 */
const regexEqual = (x, y) => {
  return (
    x instanceof RegExp &&
    y instanceof RegExp &&
    x.source === y.source &&
    x.global === y.global &&
    x.ignoreCase === y.ignoreCase &&
    x.multiline === y.multiline
  );
};

module.exports = (nextConfig = {}) => {
  return Object.assign({}, nextConfig, {
    webpack(config, options) {
      const nextCssLoaders = config.module.rules.find(
        rule => typeof rule.oneOf === "object"
      );

      if (nextCssLoaders) {
        const nextCssLoader = nextCssLoaders.oneOf.find(
          rule =>
            rule.sideEffects === false &&
            regexEqual(rule.test, /\.module\.css$/)
        );

        if (nextCssLoader) {
          /***********************************************************
           * change the rule to match all scopedcomponents css files
           ***********************************************************/
          nextCssLoader.test = /(@scopedcomponents|react\-virtualized)\/.*\.css$/;

          const cssLoader = nextCssLoader.use.find(({ loader }) =>
            loader.includes("css-loader")
          );

          if (cssLoader) {
            /***********************************************************
             * Override the default behaviour for CSS modules discovery
             * auto = true makes webpack search for *.module.css
             * https://webpack.js.org/loaders/css-loader/#auto
             ***********************************************************/
            cssLoader.options.modules.auto = /@scopedcomponents\/.*\.css$/;
            /***********************************************************
             * Nextjs overrides the default mode to "Pure"
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack/config/blocks/css/loaders/modules.ts#L35
             * Put it back to normal
             ***********************************************************/
            cssLoader.options.modules.mode = "local";
            /***********************************************************************************************************************
             * There is a problem when using components built with css-modules with Nextjs.                                        *
             * NextJS will consume code from `lib` on the server side and from `es` on the client.                                 *
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack-config.ts#L374                            *
             * This raises a problem when generating the classes for different environments,                                       *
             * throwing an error of className mismatch due to the hash created being based on the file path                        *
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack/config/blocks/css/loaders/modules.ts#L26  *
             * https://github.com/webpack/loader-utils/blob/v1.4.0/lib/interpolateName.js#L39                                      *
             * To solve this, when generating the classNames for 3rd party components,                                                 *
             * we need to tell cssloader to always use the same path                                                               *                                                                          *
             *                                                                                                                     *
             *  https://github.com/zeit/next-plugins/issues/595                                                                    *
             ***********************************************************************************************************************/
            cssLoader.options.modules.getLocalIdent = (
              context,
              localIdentName,
              localName,
              options
            ) => {
              const newContext = { ...context };
              if (newContext.resourcePath.includes("@scopedcomponents")) {
                newContext.resourcePath = newContext.resourcePath.replace(
                  `${path.sep}es${path.sep}`,
                  `${path.sep}lib${path.sep}`
                );
              }
              return getCssModuleLocalIdent(
                newContext,
                localIdentName,
                localName,
                options
              );
            };
          }
        }
      }

      if (typeof nextConfig.webpack === "function") {
        return nextConfig.webpack(config, options);
      }

      return config;
    }
  });
};

问题:

@Timer 对此有何更新?

我们将允许在下周(在 Canary 上)将 CSS 从node_modules导入任何组件文件! 当它准备好测试时,我们会在这里发布。

修复后是否会从组件动态导入 CSS?

非常感谢@Timer

next@^9.5.4-canary.10现在允许您从node_modules任何位置导入全局 CSS。 这提高了与第三方 React 库的互操作性,这些库需要您导入其 CSS,但不希望它增加整个应用程序的包大小。

@Timer等不及要发布了,真的很感谢你的工作💯 ❤️

谢谢@Timer

目前这对我来说一直是一个阻塞问题,但是当我今天对此进行测试时,仍然看到相同的错误消息。 除了简单地升级到 9.5.4-canary-10 之外,还有什么更重要的吗? 此示例尝试使用3rd 方库@rmwc

image

@johmike您是否使用以下语法导入?

import "@rmwc/avatar/avatar.css";

安装最新版本的next后,您是否尝试重新启动开发服务器?

@Timer 非常感谢这个功能。 非常适合从node_modules文件夹导入 CSS 文件。

import 'prism-themes/themes/prism-darcula.css';

是否有计划支持在node_modules目录之外导入全局 css?

@sasivarnan
这是来自另一个正在导入@rmwc组件的库。 该库正在使用@require("@rmwc/avatar/avatar.css") 。 我正在导入import {Avatar} from "library/Avatar"并且失败了。

@sasivarnan

这是来自另一个正在导入@rmwc组件的库。 该库正在使用@require("@rmwc/avatar/avatar.css") 。 我正在导入import {Avatar} from "library/Avatar"并且失败了。

知道了。 我以为它是直接导入到您的应用程序中的。 我的错。

从这里的评论来看,这实际上没有解决或者已经解决,但这里的很多人报告了一个不同的问题。 许多人仍然无法导入模块,这些模块从包本身导入 CSS(包文件中的import style.css语句)。

修复似乎允许应用程序从node_modules/路径导入 CSS,但是有一个非常简单的方法来解决这个问题:现在只需将 CSS 复制到您的应用程序,直到它被修复。 这不是拦截器级别的问题。 所以它并没有真正解决没有简单解决方案的拦截器问题。 如果您将具有 import 语句的组件导入到包本身包含的 CSS 文件中,则应用程序会崩溃。

@sasivarnan @OssiPesonen你们似乎在谈论一个此 OP 问题中讨论和修复的问题不同的问题。

这特别修复了需要您在应用程序中导入其 CSS 的库,例如:

// components/MySlider.tsx
import { Slider } from "@reach/slider";
import "@reach/slider/styles.css";

function Example() {
  return <Slider min={0} max={200} step={10} />;
}

您所谈论的是#706 和#13282 的重复,或者将node_modules视为第一方代码的能力。

@Timer我刚刚测试了预期的用例,它确实工作正常。

当我直接在组件中导入 css 作为下一个结构的一部分时,它按预期工作,没有错误。
但是,如果我将该组件移动到下一个结构之外的另一个包中,构建,然后安装该包,它会失败回到与以前相同的错误。

可能还有其他事情发生,因为我什Avatar没有在这个例子中使用ButtonAvatar是失败的错误。

image

我还添加了next-transpile-modules因为我们正在使用 monorepo,但这似乎对这个特定问题没有帮助。

我通过一个奇怪的配置文件解决了围绕next-transpile-modules的一系列其他问题。

const withCSS = require("@zeit/next-css");
module.exports = withCSS();
require.extensions[".css"] = () => {
  return;
};

我消除了next-transpile-modules并且这有效。 我不知道为什么,它似乎不应该做任何事情?

我说话太快了! 虽然这对next dev ,但next build失败,并出现来自 CSS 文件之一的unknown token . (点)错误。

@Timer 有什么想法吗? 这应该使用 monorepo 和多个包开箱即用吗? 还是我需要配置其他东西,以便@team/packageA 可以从 node_modules 导入 css,然后导入到 @team/packageB 中?

您可以按照https://github.com/vercel/next.js/issues/13282了解该行为。

使用[email protected]可以在我的应用程序中的任何地方导入 css。 但是对于 scss 文件也可以吗? 我只想导入我在页面上实际使用的 scss 文件。

// 页面/_app.tsx
import '../styles/common.scss'

// pages/index.tsx 我使用一个按钮
import '@mynpm/custom-ui/_Button.scss'

// pages/about.tsx 我使用轮播
import '@mynpm/custom-ui/_Carousel.scss'

https://nextjs.org/docs/basic-features/built-in-css-support 中的示例
Schermata 2020-10-13 alle 16 43 19

返回错误:
错误 - /Users/gp/dev/next-kolumbus/node_modules/@reach/dialog/styles.css
不能从 node_modules 中导入全局 CSS。
阅读更多: https :

https://nextjs.org/docs/basic-features/built-in-css-support 中的示例
Schermata 2020-10-13 alle 16 43 19

返回错误:
错误 - /Users/gp/dev/next-kolumbus/node_modules/@reach/dialog/styles.css
不能从 node_modules 中导入全局 CSS。
阅读更多: https :

确保您使用的是最新版本的 Next.js。

抱歉,我没有在之前的评论中指定它。 我使用的是 9.5.5 版。 刚刚从 npm 更新。

我清除了所有 .next 缓存,现在它按预期工作。

错误仍然存​​在9.5.5 版,在_app --> import "react-gauge-chart-nextjs-support/dist/GaugeChart/style.css";

Screenshot 2020-11-12 at 14 12 11

此页面是否有帮助?
0 / 5 - 0 等级