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

简介: 本文介绍了 Webpack5 的 Assets Module ,是其内置的用来处理图片字体文件等资源模块的新功能。相比与过去通过 loader 的方式去处理,更加方便和简洁。

前言

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

准备好的初始文件有:

image-20220803120126307

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 值命名的图片文件:

image-20220803120745235

浏览器打开 index.html 文件:

image-20220803120707093

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,发现图片文件其实已经被打包好了:

image-20220803121048153

这是因为 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 的名字上的区别。或许是我思考过度了,你认为呢?欢迎朋友们留言评论,一起讨论!

目录
相关文章
|
1月前
|
JavaScript 前端开发
webpack成长指北第9章---webpack如何对icon字体进行打包
webpack成长指北第9章---webpack如何对icon字体进行打包
31 1
|
1月前
|
JavaScript 前端开发
webpack成长指北第6章---webpack的图片引入
webpack成长指北第6章---webpack的图片引入
21 0
|
7月前
|
JavaScript 前端开发
[Node] Node.js Webpack打包图片-Js-Vue
[Node] Node.js Webpack打包图片-Js-Vue
|
前端开发 JavaScript
第三章 webpack5处理图片资源
介绍如何处理图片资源
292 0
webpack基础篇(八):资源解析--解析字体
webpack基础篇(八):资源解析--解析字体
82 0
webpack基础篇(八):资源解析--解析字体
webpack基础篇(七):资源解析--解析图片
webpack基础篇(七):资源解析--解析图片
85 0
webpack基础篇(七):资源解析--解析图片
|
前端开发
前端学习案例5-webpack中使用url-loader打包图片
前端学习案例5-webpack中使用url-loader打包图片
55 0
前端学习案例5-webpack中使用url-loader打包图片
webpack打包图片资源,图片不显示和index.html,HtmlWebpackPlugin
webpack打包图片资源,图片不显示和index.html,HtmlWebpackPlugin
245 0
webpack打包图片资源,图片不显示和index.html,HtmlWebpackPlugin
|
前端开发
webpack4--图片,字体处理
webpack4--图片,字体处理
webpack4--图片,字体处理
|
JSON 开发框架 前端开发
webpack 中使用 url-loader 处理字体文件| 学习笔记
快速学习 webpack 中使用 url-loader 处理字体文件
250 0