资源模块
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
在 webpack 5 之前,通常使用:
- raw-loader 将文件导入为字符串
- url-loader 将文件作为 data URI 内联到 bundle 中
- file-loader 将文件发送到输出目录
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
- asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
- asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
- asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
- asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。
当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 ‘javascript/auto’ 来解决。
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// 自定义输出文件名
// 默认情况下,asset/resource 模块以 [hash][ext][query] 文件名发送到输出目录。
// 可以通过在 webpack 配置中设置 output.assetModuleFilename 来修改此模板字符串:
assetModuleFilename: 'images/[hash][ext][query]'
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource'
},
{
test: /\.html/,
type: 'asset/resource',
generator: { // 输出到指定目录
filename: 'static/[hash][ext][query]'
}
}
]
},
};
manifest
https://webpack.docschina.org/guides/output-management/#the-manifest
你可能会很感兴趣,webpack 和 webpack 插件似乎“知道”应该生成哪些文件。答案是,webpack 通过 manifest,可以追踪所有模块到输出 bundle 之间的映射。如果你想要知道如何以其他方式来控制 webpack 输出,了解 manifest 是个好的开始。
通过 WebpackManifestPlugin 插件,可以将 manifest 数据提取为一个 json 文件以供使用。
我们不会在此展示一个如何在项目中使用此插件的完整示例,你可以在 manifest 概念页面深入阅读,以及在 缓存 指南中,了解它与长效缓存有何关系。
我们不会在此展示一个如何在项目中使用此插件的完整示例,你可以在 manifest 概念页面深入阅读,以及在 缓存 指南中,了解它与长效缓存有何关系。
代码分离
防止重复
const path = require('path');
// 使用 dependOn 选项你可以与另一个入口 chunk 共享模块:
module.exports = {
mode: 'development',
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',
},
another: {
import: './src/another-module.js',
dependOn: 'shared',
},
shared: 'lodash',
// app: { import: './app.js', dependOn: 'react-vendors' },
// 'react-vendors': ['react', 'react-dom', 'prop-types'],
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
// 如果我们要在一个 HTML 页面上使用多个入口时,
// 还需设置 optimization.runtimeChunk: 'single',否则还会遇到这里所述的麻烦。
// 配置 runtimeChunk 会生成一个 runtime.bundle.js 文件
optimization: {
runtimeChunk: 'single',
},
// SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,
// 或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除
// optimization: {
// splitChunks: {
// chunks: 'all',
// },
// },
};
SplitChunksPlugin 优化拆分代码
https://webpack.docschina.org/guides/caching/
// 通过 SplitChunksPlugin 进行优化
/*
runtime.cc17ae2a94ec771e9221.js 1.42 KiB 0 [emitted] runtime
main.e81de2cf758ada72f306.js 69.5 KiB 1 [emitted] main
*/
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
}
/*
runtime.cc17ae2a94ec771e9221.js 1.42 KiB 0 [emitted] runtime
vendors.a42c3ca0d742766d7a28.js 69.4 KiB 1 [emitted] vendors
main.abf44fedb7d11d4312d7.js 240 bytes 2 [emitted] main
*/
// 每次 vendors 都会变化,不利于优化,当它没有发生变化的时候,应该不变化
// 通过指定 moduleIds: 'deterministic' 来避免 vendors 的不必要变化
optimization: {
moduleIds: 'deterministic',
...
}
library
https://webpack.docschina.org/guides/author-libraries/
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'webpack-numbers.js',
library: "webpackNumbers",
// or
library: {
name: 'webpackNumbers',
type: 'umd',
},
},
// 外部化,指定名称
// 无法通过在 externals 中指定整个 library 的方式,
// 将它们从 bundle 中排除。而是需要逐个或者使用一个正则表达式,来排除它们。
externals: [
'library/one',
'library/two',
// 匹配以 "library/" 开始的所有依赖
/^library\/.+$/,
],
externals: {
lodash: { // 外部化,指定名称
commonjs: 'lodash',
commonjs2: 'lodash',
amd: 'lodash',
root: '_',
},
},
};
// script tag ==============================
<script src="https://example.org/webpack-numbers.js"></script>
<script>
window.webpackNumbers.wordToNum('Five');
</script>
// CommonJS ==============================
const webpackNumbers = require('webpack-numbers');
// ...
webpackNumbers.wordToNum('Two');
// AMD ==============================
require(['webpackNumbers'], function (webpackNumbers) {
// ...
webpackNumbers.wordToNum('Two');
});
// ==============================
bundle 分析(bundle analysis)
一旦开始分离代码,一件很有帮助的事情是,分析输出结果来检查模块在何处结束。 官方分析工具 是一个不错的开始。还有一些其他社区支持的可选项:
- webpack-chart: webpack stats 可交互饼图。
- webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
- webpack-bundle-analyzer:一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。
- webpack bundle optimize helper:这个工具会分析你的 bundle,并提供可操作的改进措施,以减少 bundle 的大小。
- bundle-stats:生成一个 bundle 报告(bundle 大小、资源、模块),并比较不同构建之间的结果。
将模块标记为 ESM
默认情况下,webpack 将自动检测文件是 ESM 还是其他模块系统。
Node.js 通过设置 package.json 中的属性来显式设置文件模块类型。 在 package.json 中设置 "type": "module" 会强制 package.json 下的所有文件使用 ECMAScript 模块。 设置 "type": "commonjs" 将会强制使用 CommonJS 模块。
{
"type": "module"
}
除此之外,文件还可以通过使用 .mjs 或 .cjs 扩展名来设置模块类型。 .mjs 将它们强制置为 ESM,.cjs 将它们强制置为 CommonJs。
- 在使用 text/javascript 或 application/javascript mime type 的 DataURI 中,也将使用 ESM。
- 除了模块格式外,将模块标记为 ESM 还会影响解析逻辑,操作逻辑和模块中的可用符号。
- 导入模块在 ESM 中更为严格,导入相对路径的模块必须包含文件名和文件扩展名。