写点什么

Webpack5 新特性:使用 Assets Module 处理图片和字体资源

作者:昆吾kw
  • 2022 年 8 月 03 日
  • 本文字数:3293 字

    阅读完需:约 11 分钟

前言

Webpack 是一个 JavaScript 的模块打包器。在 Webpack 的世界,一切等待打包的源码都是模块,一个文件就是一个模块。


Webpack 基于 Nodes.js 开发,采用 CommonJS 模块化规范,所以默认只能处理 .js.json 类型的模块。而项目中经常用到的文件类型有 .csslessscss这类样式文件,也有图片资源比如 .png.jpg等,还有字体文件比如.tffwoff等,对于使用前端框架开发的项目,还有 .vue.jsx 等文件类型。然而这些类型的模块 Webpack 是不认识的,需要对应的 loader 模块加载器来处理一下,从而让 Webpack 识别和打包。


本文主要介绍 Webpack5 的一个新特性——Assets Module。它是一种新的用来处理图片和字体资源模块的方式。

Asset Modules

在 webpack5 以前,通常使用 file-loader 和 url-loader 来处理图片和字体文件等资源,形如:


{  test: /\.(png|jpe?g|gif|webp|svg)$/,  use: [    {      loader: 'url-loader',      options: {        limit: 4096,        fallback: {          loader: 'file-loader',          options: {            name: 'img/[name].[hash:8].[ext]'          }        }      }    }  ]}
复制代码


而 webpack5 提供了一个新特性叫作 Asset Modules,在内部实现了对资源模块的支持。Asset Modules 提供了 4 种资源模块的类型,而无需再额外配置其他的 loader 来对资源模块进行处理,分别是:


  • asset/resource :构建出一个单独的文件并导出 URL。相当于以前的 file-loader

  • asset/inline :导出资源模块的 Data URI 内嵌到 html 或 css 文件。相当于以前的 url-loader

  • asset/source :导出资源模块的源代码。相当于以前的 raw-loader

  • asset :在导出 Data URI 和导出一个单独文件之间自动进行选择。以前可以通过使用 url-loaderlimit 配置项来实现。


下面就这几种 Assets Module 的类型进行介绍。

准备工作

创建一个演示环境:


mkdir assets-module
cd assets-module
pnpm init
复制代码


安装依赖:


pnpm add -D webpack webpack-cli css-loader style-loader
复制代码


准备好的初始文件有:



webpack 的配置文件中对于 .css 做了配置:


{    test: /\.css$/,    use: ['style-loader', 'css-loader'],}
复制代码

asset/resource

该配置相当于以前的 file-loader,用来将模块单独导出一个文件,并提供一个 URL 来使用该文件


在 index.html 中写一个盒子,通过样式设置背景图:


<body>    <div class="guodegang"></div>        <script src="./dist/main.js"></script></body>
复制代码


在 index.css 中设置一个样式类:


.guodegang {  width: 600px;  height: 400px;  background-size: cover;  background-image: url(./assets/images/guodegang.png);}
复制代码


入口文件引入该样式文件:


import './index.css'
复制代码


webpack 配置对图片资源的处理规则:


{  test: /\.(png|jpe?g|gif|webp|svg)$/,  type: 'asset/resource'}
复制代码


执行打包命令:


pnpm webpack
复制代码


打包的结果是生成一个 hash 值命名的图片文件:



浏览器打开 index.html 文件:


asset/inline

该配置相当于以前的 url-loader将文件通过 base64 编码转为 Data URI 的格式 ,并内联到使用它的文件中。


在 index.html 中设置一个新的盒子:


<body>    <div class="lijian"></div            <script src="./dist/main.js"></script></body>
复制代码


在 index.css 中设置一个样式类:


.lijian {  width: 600px;  height: 350px;  background-size: cover;  background-image: url(./assets/images/lijian.webp);}
复制代码


配置模块的打包规则:


{    test: /\.(png|jpe?g|gif|webp|svg)$/,    type: 'asset/inline'}
复制代码


执行打包命令,看看打包的结果,会发现 dist 目录下并没有生成新的文件。但是我们打开 index.html,发现图片文件其实已经被打包好了:



这是因为 asset/inline 的配置,会将源码中引入的模块转译为 Data URI 内嵌到输出文件中。

一个问题

将文件编译为 Data URI 使用,可以节省 HTTP 请求,是一个性能优化的点。但是将图片文件经过 base64 编码转为 Data URI,体积会增加大约 33%。这其中涉及到一些编码的知识,有兴趣的朋友可以亲自尝试一下。所以,对于一些比较大的文件来说,转为 Data URI 会明显增加打包后文件的体积,从而会加大对带宽资源和流量的需求。


前端性能优化中最基本的两条规则就是减少 HTTP 请求,压缩资源减少文件体积。但是这个配置项,刚好是截然相反。能减少 HTTP 请求,但会增加文件体积。


那么该如何均衡这两种方式呢?换句话说,该如何确定什么时候使用 asset/inline ,什么时候使用 asset/resource 呢?


在以前的开发中有一种雪碧图的优化方案,就是将一些小的图标放到一张文件中使用,这样很多个图标就只需要发送一次 HTTP 请求就可以了。


所以, asset/inline 这种方式,更适合于一些打包一些体积较小的文件,将其内联来减少请求的数量。对于较大的文件,还是使用 asset/resource 的方式,打包成单独的文件


那么具体该如何设置,让 webpack 智能的根据文件大小来执行不同的打包方式呢?下面的 asset 就是了。

asset

使用这个配置,默认小于 8kb 的文件将被视为inline模块类型,将转为 Data URI 以内联的方式使用;大于 8kb 的文件将被视为resource 模块类型,打包成单独的文件。


{   test: /\.(png|jpe?g|gif|webp|svg)$/,   type: 'asset'}
复制代码


可以通过 parser.dataUrlCondition.maxSize 属性来修改默认的大小:


{   test: /\.(png|jpe?g|gif|webp|svg)$/,   type: 'asset',   parser: {     // 生成Data URI 的条件     dataUrlCondition: {        // 当资源模块不超过 4kb 时,生成 DataURI,超过 4kb 时,单独打包成文件        maxSize: 4 * 1024 // 4b     }   }}
复制代码


一般,在开发中我们使用默认的配置就好。

aseet/source

这种用的比较少,相当于以前的 raw-loader。示例配置:


{  test: /\.txt/,  type: 'asset/source'}
复制代码

处理字体文件

字体文件和图片同属于资源模块,同样使用上面的配置就好:


{   test: /\.(eot|ttf|otf|woff2?)$/,   type: 'asset'}
复制代码


使用一下看看效果。


index.html 中使用字体图标:


<body>    <div>        <i class="iconfont icon-home"></i>        <i class="iconfont icon-user"></i>    </div>
<script src="./dist/main.js"></script></body>
复制代码


入口文件中引用字体图标的样式类:


import './assets/fonts/iconfont.css'
复制代码


执行打包命令,看下效果:



由于字体文件比较小,不足 8kb,所以字体文件被编码为 Data URI 进行使用了。

打包文件的重命名和路径修改

默认打包后的文件,会统一放到输出目录中,文件名为 hash 值。


当资源文件过多时,比较混乱,不易管理。所以 webpack 还提供了两个配置项和一些占位符,可以实现自定义模块的输出路径和文件名:


  • output.assetModuleFilename:对所有模块生效

  • generator.filename:对具体某个类型的模块生效

  • [name]:模块的原始文件名

  • [hash]:模块文件的哈希值

  • [ext]:模块的原始扩展名


示例配置:


{   test: /\.(png|jpe?g|gif|webp|svg)$/,   type: 'asset',   generator: {     filename: 'img/[name].[hash:8][ext]'   }},
{ test: /\.(eot|ttf|otf|woff2?)$/, type: 'asset', generator: { filename: 'fonts/[name].[hash:8][ext]' }}
复制代码


对于体积较大的文件,将图片文件打包进 dist/img 目录下,将字体文件打包进 dist/fonts 目录下。


再次打包看下效果:


总结

本文介绍了 Webpack5 的一个新特性 Assets Module 资源模块,主要用来代替以前 file-loader、 url-loader 和 raw-loader 来对图片、字体文件等资源模块进行处理。Webpack5 提供了 4 种类型资源模块,用法和区别已经在上文中演示过了,大家可根据需要灵活选用。同时,还可以根据配置自定义模块的输出路径和文件名,方便打包资源的管理。


在阅读文档和使用的过程中,个人感觉,与其说是 Assets Module 提供了四种模块类型,不如说是针对资源模块提供了 4 种不同的打包方式,将 type 理解为方式,而非类型,似乎更能体现 assetasset/resourceasset/inlineasset/raw 的名字上的区别。或许是我思考过度了,你认为呢?欢迎朋友们留言评论,一起讨论!

发布于: 刚刚阅读数: 5
用户头像

昆吾kw

关注

还未添加个人签名 2020.10.21 加入

还未添加个人简介

评论

发布
暂无评论
Webpack5新特性:使用 Assets Module 处理图片和字体资源_前端_昆吾kw_InfoQ写作社区