资产模块允许您使用资产文件(字体、图标等)而无需配置其他加载器。
在 webpack 5 之前,通常使用
raw-loader
将文件作为字符串导入url-loader
将文件内联到捆绑包中作为数据 URIfile-loader
将文件输出到输出目录资产模块类型通过添加 4 种新的模块类型来替换所有这些加载器
asset/resource
发出一个单独的文件并导出 URL。以前可以通过使用 file-loader
来实现。asset/inline
导出资产的数据 URI。以前可以通过使用 url-loader
来实现。asset/source
导出资产的源代码。以前可以通过使用 raw-loader
来实现。asset
在导出数据 URI 和发出单独文件之间自动选择。以前可以通过使用带有资产大小限制的 url-loader
来实现。在 webpack 5 中使用旧的资产加载器(即 file-loader
/url-loader
/raw-loader
)以及资产模块时,您可能希望阻止资产模块再次处理您的资产,因为这会导致资产重复。这可以通过将资产的模块类型设置为 'javascript/auto'
来完成。
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
}
},
],
+ type: 'javascript/auto'
},
]
},
}
要从资源加载器中排除来自新 URL 调用的资源,请在加载器配置中添加 dependency: { not: ['url'] }
。
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/i,
+ dependency: { not: ['url'] },
use: [
{
loader: 'url-loader',
options: {
limit: 8192,
},
},
],
},
],
}
}
默认情况下,在幕后,asset
类型执行 __webpack_public_path__ + import.meta
。这意味着在您的配置中设置 output.publicPath
将允许您覆盖 asset
加载的 URL。
如果您在代码中设置 __webpack_public_path__
,为了不破坏 asset
加载逻辑,您需要确保将其作为应用程序中的第一段代码运行,而不是使用函数来执行。一个例子是创建一个名为 publicPath.js
的文件,其内容为
__webpack_public_path__ = 'https://cdn.url.com';
然后在您的 webpack.config.js
中更新您的 entry
字段,使其看起来像
module.exports = {
entry: ['./publicPath.js', './App.js'],
};
或者,您可以在您的 App.js
中执行以下操作,而无需修改您的 webpack 配置。唯一的缺点是您必须在这里强制排序,这可能会与某些 linting 工具冲突。
import './publicPath.js';
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
+ module: {
+ rules: [
+ {
+ test: /\.png/,
+ type: 'asset/resource'
+ }
+ ]
+ },
};
src/index.js
import mainImage from './images/main.png';
img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'
所有 .png
文件将被输出到输出目录,并且它们的路径将被注入到捆绑包中,此外,您可以为它们自定义 outputPath
和 publicPath
。
默认情况下,asset/resource
模块使用 [hash][ext][query]
文件名输出到输出目录。
您可以通过在您的 webpack 配置中设置 output.assetModuleFilename
来修改此模板
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
+ assetModuleFilename: 'images/[hash][ext][query]'
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource'
}
]
},
};
自定义输出文件名的另一个情况是将某种类型的资产输出到指定的目录
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
+ assetModuleFilename: 'images/[hash][ext][query]'
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource'
- }
+ },
+ {
+ test: /\.html/,
+ type: 'asset/resource',
+ generator: {
+ filename: 'static/[hash][ext][query]'
+ }
+ }
]
},
};
使用此配置,所有 html
文件将被输出到输出目录中的 static
目录。
Rule.generator.filename
与 output.assetModuleFilename
相同,并且仅适用于 asset
和 asset/resource
模块类型。
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
- assetModuleFilename: 'images/[hash][ext][query]'
},
module: {
rules: [
{
- test: /\.png/,
- type: 'asset/resource'
+ test: /\.svg/,
+ type: 'asset/inline'
- },
+ }
- {
- test: /\.html/,
- type: 'asset/resource',
- generator: {
- filename: 'static/[hash][ext][query]'
- }
- }
]
}
};
src/index.js
- import mainImage from './images/main.png';
+ import metroMap from './images/metro.svg';
- img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'
+ block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)
所有 .svg
文件将被注入到捆绑包中作为数据 URI。
默认情况下,由 webpack 发出的数据 URI 代表使用 Base64 算法编码的文件内容。
如果你想使用自定义编码算法,你可以指定一个自定义函数来编码文件内容。
webpack.config.js
const path = require('path');
+ const svgToMiniDataURI = require('mini-svg-data-uri');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.svg/,
type: 'asset/inline',
+ generator: {
+ dataUrl: content => {
+ content = content.toString();
+ return svgToMiniDataURI(content);
+ }
+ }
}
]
},
};
现在所有 .svg
文件将由 mini-svg-data-uri
包进行编码。
webpack.config.js
const path = require('path');
- const svgToMiniDataURI = require('mini-svg-data-uri');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
- test: /\.svg/,
- type: 'asset/inline',
- generator: {
- dataUrl: content => {
- content = content.toString();
- return svgToMiniDataURI(content);
- }
- }
+ test: /\.txt/,
+ type: 'asset/source',
}
]
},
};
src/example.txt
Hello world
src/index.js
- import metroMap from './images/metro.svg';
+ import exampleText from './example.txt';
- block.style.background = `url(${metroMap}); // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)
+ block.textContent = exampleText; // 'Hello world'
所有 .txt
文件将按原样注入到捆绑包中。
当使用 new URL('./path/to/asset', import.meta.url)
时,webpack 也会创建一个资产模块。
src/index.js
const logo = new URL('./logo.svg', import.meta.url);
根据你配置中的 target
,webpack 会将上面的代码编译成不同的结果。
// target: web
new URL(
__webpack_public_path__ + 'logo.svg',
document.baseURI || self.location.href
);
// target: webworker
new URL(__webpack_public_path__ + 'logo.svg', self.location);
// target: node, node-webkit, nwjs, electron-main, electron-renderer, electron-preload, async-node
new URL(
__webpack_public_path__ + 'logo.svg',
require('url').pathToFileUrl(__filename)
);
从 webpack 5.38.0 开始,数据 URL 也支持在 new URL()
中使用。
src/index.js
const url = new URL('data:,', import.meta.url);
console.log(url.href === 'data:,');
console.log(url.protocol === 'data:');
console.log(url.pathname === ',');
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
+ test: /\.txt/,
+ type: 'asset',
}
]
},
};
现在 webpack 将根据默认条件自动在 resource
和 inline
之间进行选择:小于 8kb 的文件将被视为 inline
模块类型,否则将被视为 resource
模块类型。
你可以在 webpack 配置的模块规则级别上设置 Rule.parser.dataUrlCondition.maxSize
选项来更改此条件。
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.txt/,
type: 'asset',
+ parser: {
+ dataUrlCondition: {
+ maxSize: 4 * 1024 // 4kb
+ }
+ }
}
]
},
};
你也可以 指定一个函数 来决定是否内联模块。
在资产模块和 Webpack 5 之前,可以使用 内联语法 与上面提到的传统加载器一起使用。
现在建议删除所有内联加载器语法,并使用 resourceQuery 条件来模拟内联语法的功能。
例如,在用 asset/source
类型替换 raw-loader
的情况下。
- import myModule from 'raw-loader!my-module';
+ import myModule from 'my-module?raw';
在 webpack 配置中。
module: {
rules: [
// ...
+ {
+ resourceQuery: /raw/,
+ type: 'asset/source',
+ }
]
},
如果你想排除原始资产被其他加载器处理,请使用否定条件。
module: {
rules: [
// ...
+ {
+ test: /\.m?js$/,
+ resourceQuery: { not: [/raw/] },
+ use: [ ... ]
+ },
{
resourceQuery: /raw/,
type: 'asset/source',
}
]
},
或一个包含规则的 oneOf
列表。这里只应用第一个匹配的规则。
module: {
rules: [
// ...
+ { oneOf: [
{
resourceQuery: /raw/,
type: 'asset/source',
},
+ {
+ test: /\.m?js$/,
+ use: [ ... ]
+ },
+ ] }
]
},
对于像服务器端渲染这样的用例,你可能想要禁用生成资产,这可以通过 Rule.generator
下的 emit
选项来实现。
module.exports = {
// …
module: {
rules: [
{
test: /\.png$/i,
type: 'asset/resource',
generator: {
emit: false,
},
},
],
},
};