webpack 加载资源文件
背景
最近用 webpack5 + vue 开发项目时,发现css 中通过 url 引入的文件不能显示。看配置啥的都没写错,几番查询下来,最终搞明白来龙去脉。
当时的配置如下
webpack.config.js
{
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader'],
},
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|eot|ttf|otf)$/,
loader: 'file-loader',
}
]
}
}
vue 文件
在 vue template 里引用的图片可以显示,在less文件中使用 url 就不能显示。
原理
webpack 中的模块
Webpack 是一个基于模块而进行打包的工具,有哪些东西被其视为模块呢?具体如下:
基于 ES6
import
引入的基于 CommonJS
require()
引入的基于 AMD
define/require
引入的
Webpack 原生只能识别 .js|.mjs|.json
的模块,其它未知文件类型模块则需要通过 loaders 进行转化后处理。比如:
.css
样式模块通过的 css-loader 转化为可识别的 js 代码。简而言之就是导出文件内容,类似export default "...css文件内容..."
.ts
TypeScript 模块通过 ts-loader 转化为 js 代码.jpg|png|gif
图片模块通过 file-loader 转化为 js 代码
Webpack 5 新增 Asset Modules
从 v5 开始,webpack 开始原生支持一些资源文件模块,比如图片、字体、图标 svg 等。
module.exports = {
output: {
assetModuleFilename: 'images/[name][ext]', // v5
},
module: {
rules: [{
test: /\.png$/,
// v4
use: {
loader: 'file-loader',
options: {
outputPath: 'images/',
name: '[name].[ext]',
},
},
// v5
type: 'asset/resource',
generator: {
filename: 'static/[name][ext]',
},
}]
},
};
webpack 4 通过 file-loader
来将图片资源转换为js资源,并且修改文件路径
webpack 5 则通过 type: 'asset/resource'
告诉webpack,这类图片通过自己内置的 asset 模块来解析。
什么是**asset**
模块
在 webpack5 之前,可能需要使用 raw-loader、file-loader、url-loader 来加载资源。
raw-loader:将文件作为字符串导入
file-loader:处理文件的路径并输出文件到输出目录
url-loader:有条件将文件转化为 base64 URL,如果文件大于 limit 值,通常交给 file-loader 处理。
在 webpack5+,以上方法已经过时了,webpack5 自己使用了**asset**
来代替以上 loader。
而**asset**
类型有四种。
asset/resource: 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
asset/inline: 导出一个资源的 data URI。之前通过使用 url-loader 实现。
asset/source: 导出资源的源代码。之前通过使用 raw-loader 实现。
asset: 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
总结一下就是,webpack 将原本资源加载的几个 loader 全部自己实现了,纯粹的抢活干。
为什么和 file-loader 功能冲突呢
从上面的介绍可以发现,如果要使用 **asset**
模块,需要给文件类型设置 type: 'asset/resource'
,webpack 才识别为内置的 asset 类型。上面的配置里并没有这个配置。
在查阅官方文档时发现的问题的关键
webpack 不只识别 type: 'asset/resource'
类型
当使用 new URL('./path/to/asset', import.meta.url)
,webpack 也会创建资源模块。
官方文档地址: https://webpack.docschina.org/guides/asset-modules/#url-assets
同时,使用 css-loader 之后,url 引入的文件会被转译成 new URL()
// css
.main{
backgroud: url('@/assets/images/a.png')
}
// css-loader 转译之后
var ___CSS_LOADER_URL_IMPORT_0___ = new URL("@/assets/images/a.png", import.meta.url);
.main {
background: url(${___CSS_LOADER_URL_REPLACEMENT_0___});
}
正好,两个文件处理模块在 new URL 这里同时生效了,导致 file-loader 先处理一波,asset 模块再处理时文件已经不在了,最终有地址没图片内容。
怎么解决
既然两者的冲突无法避免,那就直接 asset 好啦
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource',
}
如果需要使用 url-loader 的功能可以配置为
{
test: /\.(jpe?g|png|svg|gif)/i,
type: 'asset',
generator: {
filename: 'img/[hash][ext][query]' // 局部指定输出位置
},
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 限制于 8kb
}
}
},
将低于8k的文件以base64的格式打包到js里
其他支持的模块类型
除了asset模块,webpack5 还增加了一下类型模块
type |
描述 |
---|---|
javascript/auto |
默认。包括 esm/cjs/amd 写法的文件模块 |
javascript/esm |
以 .mjs 结尾的文件模块 |
javascript/dynamic |
不确定性的 js 模块,比如 externals 里配置的 |
json |
.json 文件模块 |
~~webassembly/*~~ |
实验性 |
asset |
资源文件模块。对应 url-loader 里的 size limit |
asset/source |
同上。对应 raw-loader |
asset/inline |
同上。对应 url-loader |
asset/resource |
同上。对应 file-loader |