管理资源
包括加载CSS、加载图片、加载字体、加载数据。按需选择。
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
{
test: /\.(csv|tsv)$/i,
use: ['csv-loader'],
},
{
test: /\.xml$/i,
use: ['xml-loader'],
},
],
},
};
管理输出
清理 ./dist 文件夹
webpack.config.js
clean: true
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
manifest
webpack 通过 manifest 追踪所有模块到输出的 bundle 之间的映射。了解 manifest 将会帮助了解如何以其他方式控制 webpack 输出。
WebpackManifestPlugin 插件可以将 manifest 数据提取为 json 文件。
参阅 manifest 概念页面以深入了解如何在项目中使用此插件,同时 缓存 指南介绍了其与长效缓存之间的关系。
开发环境
使用source map
目的:在代码报错时,不会定位到打包后的 bundle.js 中,而是定位到打包前的具体某一 js 代码。
webpack.config.js
devtool: 'inline-source-map',
选择一个开发工具
目的:当代码发生改变时,自动编译。
安装依赖:
npm install --save-dev webpack-dev-server
webpack.config.js 添加
devServer: {
static: './dist',
},
代码分离
代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后便能按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle、控制资源加载优先级,如果使用合理,会极大减小加载时间。
常用的代码分离方法有三种:
- 入口起点:使用 entry 配置手动地分离代码。
- 防止重复:使用 入口依赖 或者 SplitChunksPlugin 去重和分离 chunk。
- 动态导入:通过模块的内联函数调用分离代码。
主要介绍第二种方法:防止重复
入口依赖
在配置文件中配置 dependOn 选项,以在多个 chunk 之间共享模块:
webpack.config.js
entry: {
index: {
import: './src/index.js',
dependOn: 'shared',
},
another: {
import: './src/another-module.js',
dependOn: 'shared',
},
shared: 'lodash',
},
如果想要在一个 HTML 页面上使用多个入口起点,还需设置 optimization.runtimeChunk: 'single'
。
webpack.config.js
optimization: {
runtimeChunk: 'single',
},
构建结果如下:
...
[webpack-cli] Compilation finished
asset shared.bundle.js 549 KiB [compared for emit] (name: shared)
asset runtime.bundle.js 7.79 KiB [compared for emit] (name: runtime)
asset index.bundle.js 1.77 KiB [compared for emit] (name: index)
asset another.bundle.js 1.65 KiB [compared for emit] (name: another)
Entrypoint index 1.77 KiB = index.bundle.js
Entrypoint another 1.65 KiB = another.bundle.js
Entrypoint shared 557 KiB = runtime.bundle.js 7.79 KiB shared.bundle.js 549 KiB
runtime modules 3.76 KiB 7 modules
cacheable modules 530 KiB
./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
./src/another-module.js 84 bytes [built] [code generated]
./src/index.js 257 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 249 ms
可以看到,除了 shared.bundle.js
,index.bundle.js
和 another.bundle.js
之外,还生成了一个 runtime.bundle.js
文件。
尽管 webpack 允许每个页面使用多个入口起点,但在可能的情况下,应该避免使用多个入口起点,而使用具有多个导入的单个入口起点:entry: { page: ['./analytics', './app'] }
。这样可以获得更好的优化效果,并在使用异步脚本标签时保证执行顺序一致。
SplitChunksPlugin
SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件去除之前示例中重复的 lodash
模块:
webpack.config.js
optimization: {
splitChunks: {
chunks: 'all',
},
},
使用 optimization.splitChunks 配置选项后构建,将会发现 index.bundle.js
和 another.bundle.js
已经移除了重复的依赖模块。从插件将 lodash
分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了 bundle 大小。执行 npm run build
查看效果:
...
[webpack-cli] Compilation finished
asset vendors-node_modules_lodash_lodash_js.bundle.js 549 KiB [compared for emit] (id hint: vendors)
asset index.bundle.js 8.92 KiB [compared for emit] (name: index)
asset another.bundle.js 8.8 KiB [compared for emit] (name: another)
Entrypoint index 558 KiB = vendors-node_modules_lodash_lodash_js.bundle.js 549 KiB index.bundle.js 8.92 KiB
Entrypoint another 558 KiB = vendors-node_modules_lodash_lodash_js.bundle.js 549 KiB another.bundle.js 8.8 KiB
runtime modules 7.64 KiB 14 modules
cacheable modules 530 KiB
./src/index.js 257 bytes [built] [code generated]
./src/another-module.js 84 bytes [built] [code generated]
./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 241 ms
以下是由社区提供,对代码分离很有帮助的 plugin 和 loader:
- mini-css-extract-plugin:用于将 CSS 从主应用程序中分离。
模块热替换
模块热替换(HMR)是 webpack 提供的最有用的功能之一。它能帮助在运行时不完全刷新页面的情况下更新所有类型的模块。
警告
HMR 不适用于生产环境,而应当用于开发环境。
启用HMR
从 webpack-dev-server
v4.0.0 开始,模块热替换是默认开启的。
webpack.config.js
devServer: {
static: './dist',
hot: true,
},
提示:可以通过以下命令修改 webpack-dev-server 配置。
webpack serve --hot-only
通过 Node.js API 启用 HMR
在 Node.js API 中使用 webpack dev server 时,不要将 dev server 选项放在 webpack 配置对象中。而是在创建时, 将其作为第二个参数传递。例如:
new WebpackDevServer(options, compiler)
想要启用 HMR,还需要修改 webpack 配置对象,使其包含 HMR 入口起点。这是关于如何使用的一个基本示例:
dev-server.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const webpackDevServer = require('webpack-dev-server');
const config = {
mode: 'development',
entry: [
// 模块热替换的运行时代码
'webpack/hot/dev-server.js',
// 用于 web 套接字传输、热重载逻辑的 web server 客户端
'webpack-dev-server/client/index.js?hot=true&live-reload=true',
// 你的入口起点
'./src/index.js',
],
devtool: 'inline-source-map',
plugins: [
// 模块热替换的插件
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
title: 'Hot Module Replacement',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
const compiler = webpack(config);
// 由于手动添加了 `hot` 与 `client` 参数,其将被禁用
const server = new webpackDevServer({ hot: false, client: false }, compiler);
(async () => {
await server.start();
console.log('dev server 正在运行');
})();
参阅 webpack-dev-server 的 Node.js API 的完整文档 以了解更多。
HMR 加载样式
借助 style-loader
后,使用模块热替换加载 CSS 实际上极其简单。此 loader 在幕后使用了 module.hot.accept
,在 CSS 依赖模块更新之后,会将其修补到 <style>
标签中。
首先安装两个 loader:
npm install --save-dev style-loader css-loader
然后更新配置文件,使用这两个 loader
webpack.config.js
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
懒加载
懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。
举例
我们在代码分离中的例子基础上,进一步做些调整来说明这个概念。那里的代码确实会在脚本运行的时候产生一个分离的代码块 lodash.bundle.js
,在技术概念上“懒加载”它。问题是加载这个包并不需要用户的交互 - 意思是每次加载页面的时候都会请求它。这样做并没有对我们有很多帮助,还会对性能产生负面影响。
警告:注意当调用 ES6 模块的 import()
方法(引入模块)时,必须指向模块的 .default
值,因为它才是 promise 被处理后返回的实际的 module
对象。
框架
许多框架和类库对于如何用它们自己的方式来实现(懒加载)都有自己的建议。这里有一些例子: