You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
|- next-style-loader `pitch`|- requested module is picked up as a dependency
|- postcss-loader normal execution
|- css-loader normal execution
|- next-style-loader normal execution
Rule.issuer: A Condition to match against the module that issued the request. In the following example, the issuer for the a.js request would be the path to the index.js file.
// next/dist/build/webpack/config/blocks/css/index.jsmarkRemovable({// This should only be applied to CSS filesissuer: regexLikeCss,// Exclude extensions that webpack handles by defaultexclude: [/\.(js|mjs|jsx|ts|tsx)$/,/\.html$/,/\.json$/,/\.webpack\[[^\]]+\]$/,],// `asset/resource` always emits a URL reference, where `asset`// might inline the asset as a data URItype: 'asset/resource'}),
问题简述
开发同学反馈 Next.js 项目 next dev 命令启动后浏览器访问页面 css 样式有丢失,表现为 style 标签的内容不是 css 样式而是一个 url 😨
面对这个略显奇葩的问题该如何排查了 ?
问题排查
Step1 确认是否配置了错误的 loader
同时根据 Rule.oneOf 的定义确认了只会从 oneOf 数组中找到一个匹配到的规则就会停止, 那么 loader 应该是都被正确设置了
Step2 处理 css 文件的 loader 有 bug ?
loader 正常是按从下到上, 从右到左的顺序执行, 即如上配置会按从 postcss-loader > css-loader > next-style-loader 来依次处理。
但是由于 next-style-loader 导出了 pitch 属性, loader 的顺序将会变成首先运行 pitch 函数, 相关文档见 Pitching Loader
那么我们先从 next-style-loader 的 pitch 函数开始排查, 然后发现了如下语句
上面语句补充上变量的值后相当于如下
♻️ 这里的依赖关系先简单理一下, 比如 _app.tsx 引用了 globals.css
globals.css 模块的内容被 next-style-loader 处理后的内容类似于下面这样
所以 style 标签里面的 content 的源头其实是 require 的 !!xxx/css-loader/src/index.js??xxx/postcss-loader/src/index.js??ruleSet[1].rules[3].oneOf[6].use[2]!./globals.css 模块提供
那么对于 !!xxx/css-loader/src/index.js??xxx/postcss-loader/src/index.js??ruleSet[1].rules[3].oneOf[6].use[2]!./globals.css 这个路径上带有 loader 的模块其文件后缀依然为 .css, 那么不还得命中 oneOf 数组索引为 6 的规则么 🤔 ?
经过 debug webpack 的代码发现结果是意外的 ❌, 它命中的是 oneOf 数组索引为 8 的规则
让我们回头查看一下为什么没有命中索引为 6 的规则, 发现索引为 6 规则非常重要的一点是要求引用该文件的 issuer 必须只能是 pages/_app.tsx, 见下图红圈的 issuer.and 字段
而 !!xxx/css-loader/src/index.js??xxx/postcss-loader/src/index.js??ruleSet[1].rules[3].oneOf[6].use[2]!./globals.css 模块其实是被 next-style-loader 代理后的 globals.css 所 require, 所以它的 issuer 竟然为 styles/globals.css 🤯
至此 !!xxx/css-loader/src/index.js??xxx/postcss-loader/src/index.js??ruleSet[1].rules[3].oneOf[6].use[2]!./globals.css 模块经过 postcss-loader > css-loader 处理后, 最后还要被 webpack 内置的文件类型 asset/resource 处理(相当于 file-loader), 最终处理完成它的 module.exports 其实为如下👇
所以被 next-style-loader 处理后的 styles/globals.css 模块最后 append 了一个 url 字符串到了 style 标签中造成了本次 css 样式的异常 !!!
疑惑解答
为什么 Rule.oneOf 貌似 pick 了多次规则?
对于 styles/globals.css 模块而言只选择了一次即 oneOf 数组索引为 6 的规则, 而经过 next-style-loader 的代理修改后的 require 语句又产生了新的模块 !!xxx/css-loader/src/index.js??xxx/postcss-loader/src/index.js??ruleSet[1].rules[3].oneOf[6].use[2]!./globals.css, 于是新的模块又进行了一次规则 pick, 所以并不算违背了 Rule.oneOf 的定义
Next.js 的 oneOf 数组索引为 8 的规则真实意图是干什么的?
根据如下 Next.js 对应的代码可知, Next.js 的预期是 issuer 为 *.css 的模块将要被 asset/resource(类似于 file-loader) 给处理, 比如 globals.css 有一行代码为 background-image: url("./xxx.png"), 那么此时 xxx.png 就要被 asset/resource 给处理
问题解决
Next.js 没想到被 next-style-loader 修改后还存在 .css 文件 require .css 文件的情况, .css 文件被 asset/resource 处理后返回一个 url 就导致了本次的 bug 🐛
其实 .css 中 require 的图片、字体等文件被 asset/resource 处理是符合预期, 如果被 require 的是 .css 文件就得排除, 更多见next.js/pull/42283 与 webpack/pull/16477
issuer: regexLikeCss, // Exclude extensions that webpack handles by default exclude: [ + /\.(css|sass|scss)$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/,
The text was updated successfully, but these errors were encountered: