深入 Webpack5 等构建工具系列三(4) - Asset Modules type

772 阅读5分钟

这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战

前面三篇文章讲了 webpack 加载处理其它资源,主要讲了如何加载图片资源,有两种 loader 可以选择。一个是 file-loader,另一个是 url-loader,相较于 file-loaderurl-loader 有一个 limit 可以设置,然后根据当前文件的大小,决定是否进行 base64 的编码。如果进行 base64 的编码,那么编码成 base64 的字符后就会直接和页面一起请求下来;否则就还是使用 file-loader 将图片打包成图片文件,到时候每个图片文件分别进行单独的 http 请求。

但是,file-loaderurl-loader 是在 webpack 5 之前处理其它资源经常采用的方式,而从 webpack 5 开始,我们其实可以采用另外一种方式来处理其它资源了。

在进入正文之前,我们一个问题做下说明:

  • 前面执行 npm run build 进行打包的时候,每次重新打包前我们都要手动把上一次打包的 build 文件夹删除掉,有没有办法让它在打包的时候先自动删除呢?
    • 有,我们后面就会讲到;

前期准备:

  1. 03_webpack处理其它资源 目录重命名为 03_webpack处理其它资源1,并删除里面的 node_modulesbuild 文件夹,;
  2. 01_learn_webpack 目录下拷贝一份 03_webpack处理其它资源1,并重命名为 04_webpack处理其它资源2
  3. 打开 VS Code 的终端,切换目录到 04_webpack处理其它资源2 下,运行 npm install 安装当前项目所需依赖;

1 Asset Modules type 的介绍

  • 我们当前使用的 webpack 版本是 webpack 5
    • webpack 5 之前,我们常用 raw-loader(可以以字符串形式导入文件)、url-loaderfile-loader 这些 loader 加载资源;
    • webpack 5 之后,我们可以直接使用**资源模块类型(Asset Modules type)**来替代上面的这些 loader
  • 资源模块类型(Asset Modules type)通过添加 4 中新的模块类型替换了所有这些 loader
    • asset/resource 发出一个单独的文件并导出 URL。之前通过使用 file-loader 实现;
    • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现;
    • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现;
    • asset 在导出一个 data URI 和发出一个单独的文件之间自动选择。之前通过使用 url-loader,并配置资源大小限制实现。

2 Asset Modules type 的使用

既然在 webpack 5 中可以用资源模块类型替代 file-loaderurl-loader 了,那我们以后就可以不用这两个 loader 了,所以我们可以在项目中将它们卸载了:

npm uninstall file-loader url-loader

然后,来到 wk.config.js 文件中,找到之前匹配图片文件的规则:

{
  test: /\.(png|jpe?g|gif|svg)$/,
  use: [
    {
      // loader: 'file-loader',
      loader: 'url-loader',
      options: {
        name: 'img/[name].[hash:6].[ext]',
        limit: 100 * 1024
      }
    }
  ]
}

先简单修改如下:

{
  test: /\.(png|jpe?g|gif|svg)$/,
  type: 'asset/resource' // 相当于 file-loader 的效果
}

再来运行 npm run build 命令,就可以成功打包了。并且,此时图片默认打包到输出目录的根目录下:

image-20210830185734233.png

那如何可以自定义文件的输出路径和文件名呢(像前面使用 file-loader 时那样指定文件的存放位置,把所有图片放到一个文件夹里)?有两种方式:

  1. 针对所有的资源模块进行设置。在顶级的 output 中配置 assetModuleFilename,比如如果想要把所有资源模块都放到 img 目录下:

    module.exports = {
      // ...
      output: {  
        // ...
        assetModuleFilename: 'img/[name].[hash:6][ext]'
      }
      // ...
    }
    

    这里同样可以使用 placeholders,但与 file-loaderplaceholders 中的 [ext] 不同,这里的 [ext] 生成的扩展名中本身就会带有 .,所以 [ext] 前不需要再加 .

    配置完后先删除项目目录下的 build 目录,再来运行 npm run build,结果如下:

    image-20210830192853399.png

  2. 针对模块的某一规则单独进行设置。配置 Rule.generator.filename

    {
      test: /\.(png|jpe?g|gif|svg)$/,
      type: 'asset/resource', // 相当于 file-loader 的效果
      generator: {
        filename: 'img/[name].[hash:6][ext]'
      }
    }
    

    配置完后先删除项目目录下的 build 目录,再来运行 npm run build,结果和上面配置 output.assetModuleFilename 后的结果是一样的。

如果想要实现使用了 url-loader 的效果,可以这样设置规则:

{
  test: /\.(png|jpe?g|gif|svg)$/,
  type: 'asset/inline' // 相当于 url-loader 的效果
}

注意:因为是 asset/inline,导出的是资源的 data URI,而不再会导出单独的文件,所以不需要再配置 Rule.generator.filename 了。

配置完后先删除项目目录下的 build 目录,再来运行 npm run build,结果如下:

image-20210830230209780.png

对于 asset/source,可以用来导出 txt 文件、json 文件,但一般用的较少,这里就不做演练了。最后再来说下 asset,前面我们在讲 url-loader 时说过,如果有的资源比较大,可以把它打包成对应的单独的文件,而如果有的资源比较小,再打包成单独的文件做单独的请求可能有点浪费资源,这时就可以转换成 base64 的编码。那么对于资源模块类型,想要实现这个需求(url-loaderlimit 效果),就可以将资源模块类型设置为 asset 类型:

{
  test: /\.(png|jpe?g|gif|svg)$/,
  // type: 'asset/resource', // 相当于 file-loader 的效果
  // type: 'asset/inline', // 相当于 url-loader 的效果
  type: 'asset',
  generator: {
    filename: 'img/[name].[hash:6][ext]'
  },
  parser: {
    dataUrlCondition: {
      maxSize: 100 * 1024 // 100kb
    }
  }
}

即如果资源的大小小于 100kb,就将其当做 asset/inline 类型处理(导出资源的 data URI),否则当做 asset/resource 类型处理(打包成单独的文件并导出 URL)。

配置完后先删除项目目录下的 build 目录,再来运行 npm run build,结果如下:

image-20210830233138611.png

可以看到,大小大于 100kb 的那张图片被单独打包了,而大小小于 100kb 的那张图片已经被打包成了 base64 编码放进了 bundle.js 文件中。

总结:在 webpack 5 中,关于加载资源文件,我们不再需要单独去安装 file-loaderurl-loader 了,我们直接去使用 webpack 5 提供的 Asset Modules type 就可以了。