利用 stats.json 定位 @nrwl/react webpack 配置问题

背景

团队使用NX这一 monorepo 工具来搭建 React 应用。NX 基于 React 应用在 webpack 打包时添加了url-loader的相关配置。但是同事反馈该url-loader针对部分引用的图片文件不起作用。

定位

url-loader 作用

url-loader,简而言之,可以将应用中引用到的一些资源文件(例如图片)转换成 base64 的数据格式,然后嵌入到我们的应用中(例如 HTML 的 img src, css 中的 url 函数),这样便无需针对该资源发起网络请求,节省请求资源。

以下是url-loader的配置举例:

{
   
  "test": "/\\.(png|jpe?g|gif|webp)$/",
  "loader": require.resolve("url-loader"),
  "options": {
   
    "limit": 10000, // 10kB
    "name": "[name].[hash:7].[ext]"
  }
}

上述配置的意思就是针对 10kB 以下大小的一些常见图片格式文件使用url-loader处理,否则使用 fallback loader 处理,url-loader默认的 fallback loader 是file-loader。超过大小的文件会被其处理,相应的 options 会传给file-loader,例如 name,最终处理后的文件名会包括 7 位 hash。

具体针对哪部分文件不起作用?

由于 NX 并不是写好 webpack 配置,再使用 webpack 指令进行打包。而是以Angular CLI builder的形式,引入 webpack 并进行代码编写,拥有高度定制化,因此一开始并没有细看其 builder 的源码,很难找到具体是什么文件不起作用,而哪部分又没有问题。

一开始同事与之前的一个应用对比,发现其打包的一部分图片产物被file-loader处理,没有最终文件;而一部分则有最终文件,但是 hash 位数为默认的 20 位而不是file-loader处理后的 7 位;最后一部分文件则位数正确。

通过一些比对后发现,同一个图片文件,如果在样式文件(例如.scss)中引用则都会生成 20 位 hash 的文件名,而在 j(t)sx 中则配置生效。

查看 webpack config,尤其是样式文件那部分

找到对应的文件后,便需要查找是哪个 loader 处理了该文件,最先想到的是直接输出对应的config.module配置,由于 NX React 应用使用的配置文件为@nrwl/react/plugins/webpack.js,编辑该文件,增加如下部分:

PS: 由于正则表达式 JSON 没有对应的表达形式,因此 loader 的test部分只会是一个{},不便于确认文件类型,因此可以使用其toString()方法作为JSON.stringfy()时的处理函数,以此直观显示匹配的文件类型。

const fs = require('fs');

RegExp.prototype.toJSON = RegExp.prototype.toString;

function getWebpackConfig(config) {
   
  // ...
  fs.writeFile(
    './webpack-config.json',
    JSON.stringify(config.module),
    null,
    () => {
   }
  );
  return config;
}

module.exports = getWebpackConfig;

打印出来的 config 如下(仅提取匹配部分):

{
   
  "rules": [
    {
   
      "test": "/\\.css$|\\.scss$|\\.sass$|\\.less$|\\.styl$/",
      "oneOf": [
        {
   
          "exclude": [
            "/Users/tianzhi/dev/nx-examples/libs/shared/styles/src/index.scss",
            "/Users/tianzhi/dev/nx-examples/libs/shared/header/index.scss",
            "/Users/tianzhi/dev/nx-examples/node_modules/normalize.css/normalize.css"
          ],
          "test": "/\\.scss$|\\.sass$/",
          "use": [
            {
   
              "loader": "/Users/tianzhi/dev/nx-examples/node_modules/@nrwl/web/node_modules/style-loader/dist/index.js"
            },
            {
   
              "loader": "/Users/tianzhi/dev/nx-examples/node_modules/postcss-loader/src/index.js",
              "options": {
    "ident": "embedded", "sourceMap": false }
            },
            {
   
              "loader": "/Users/tianzhi/dev/nx-examples/node_modules/@nrwl/web/node_modules/sass-loader/dist/cjs.js",
              "options": {
   
                "implementation": {
   
                  "info": "dart-sass\t1.26.10\t(Sass Compiler)\t[Dart]\ndart2js\t2.8.4\t(Dart Compiler)\t[Dart]",
                  "types": {
   },
                  "NULL": {
   },
                  "TRUE": {
    "value": true },
                  "FALSE": {
    "value": false }
                },
                "sourceMap": false,
                "sassOptions": {
    "precision": 8, "includePaths": [] }
              }
            }
          ]
        }
      ]
    },
    {
   
      "test": "/\\.(png|jpe?g|gif|webp)$/",
      "loader": "/Users/tianzhi/dev/nx-examples/node_modules/url-loader/dist/cjs.js",
      "options": {
    "limit": 10000, "name": "[name].[hash:7].[ext]" }
    },
    {
   
      "test": "/\\.svg$/",
      "oneOf": [
        {
   
          "issuer": {
    "test": "/\\.[jt]sx?$/" },
          "use": [
            "@svgr/webpack?-svgo,+titleProp,+ref![path]",
            {
   
              "loader": "/Users/tianzhi/dev/nx-examples/node_modules/url-loader/dist/cjs.js",
              "options": {
   
                "limit": 10000,
                "name": "[name].[hash:7].[ext]",
                "esModule": false
              }
            }
          ]
        },
        {
   
          "use": [
            {
   
              "loader": "/Users/tianzhi/dev/nx-examples/node_modules/url-loader/dist/cjs.js",
              "options": {
    "limit": 10000, "name": "[name].[hash:7].[ext]" }
            }
          ]
        }
      ]
    }
  ]
}

可以看到针对.scss 文件处理的 loader 及顺序为:sass-loader -> postcss-loader -> style-loader

最疑惑的地方也就在这,一般情况下,我们使用了postcss-loader后还会使用css-loader进行处理,css-loader会解析@import以及url()语法,将其转化为import/require(),这样一来便能交给其他 loader 例如url-loader进行处理。而对于postcss-loader,是因为它的autoprefixer非常出名。

所以到此为止还是无法确认具体是哪个 loader 处理了url()语法以至于url-loader无法进行处理。但是可以确定的是答案就在这三个 loader 之一,由于这个时候还不了解postcss-loader的插件机制,我甚至怀疑是sass-loader的某个配置使得url()语法被提前解析。但是猜测始终不是办法,需要找到一个更确定的方法,能够知道对应文件在 loader 处理前后的产物文件。

使用 stats.json 定位问题

如果能 debugging webpack 打包这一过程就好了,查找官网,还真发现有针对webpack 打包过程的 debug 方法,有两种方案,我选用了较为简单的日志方案:查看数据报告 stats.json 文件

webpack 使用--json指令可以输出对应文件,在 NX 中则是--statsJson

输出后的日志报告条目很多,关于更多条目可以参考官网介绍,这里只需要搜索对应文件,例如我这里是app.scss,会得到如下信息(仅提取有效信息):

{
   
  "chunks": [
    {
   
      "names": ["main"],
      "files": ["main.48c162c6863d37cba541.es5.js"],
      "hash": "48c162c6863d37cba541",
      "modules": [
        {
   
          &#
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值