官方链接:code splitting
SplitChunksPlugin 配置参数:https://webpack.js.org/plugins/split-chunks-plugin/
两篇非常棒的讲代码划分的文章(可以在阅读完下面基础知识后学习):
- https://medium.com/hackernoon/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
- https://itnext.io/react-router-and-webpack-v4-code-splitting-using-splitchunksplugin-f0a48f110312
何为代码分割?
首先假设我们的index.js
代码内容如下:
import _ from 'lodash'; // 假设 lodash 模块有 1mb 大
// 假设下面的逻辑有 1mb 大
console.log(_.join(['hello', 'webpack']));
webpack.config.js
配置内容为:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
devtool: "source-map",
devServer: {
contentBase: './dist',
open: true,
hot: true,
hotOnly: true
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};
执行 yarn build
后(实际执行的是 webpack
),打包出来的文件存放在 dist
目录下,文件名为 bundle.js
。大致内容如下:
/******/ (function(modules) { // webpackBootstrap
...省略
/***/ "./node_modules/lodash/lodash.js":
/*!***************************************!*\
!*** ./node_modules/lodash/lodash.js ***!
\***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
...省略
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
...省略
我们发现打包后的内容除了 webpack
启动代码之外,就是我们的 lodash
模块和我们在 index.js
里面的逻辑代码。
如我们上面的假设,假设 lodash
模块有 1mb 大,我们在 index.js
文件实现的逻辑有 1mb 大。那么如果客户端在请求该页面的时候,就会请求一个 2mb 大的 js 代码,size
较大的 js 文件无疑会影响客户端浏览器的加载效率;另外如果 index.js
文件发生变化,客户端就必须重新请求这个 2mb 的文件(浏览器缓存功能失效)。
所以这里我们就需要做 bundle splitting,将 lodash
和我们在 index.js
文件里的逻辑划分成 2 个打包文件。这样客户端发送请求的时候,就会请求两个 1mb 的文件。另外就是即使我们的 index.js
文件内容发生变化,因为客户端缓存的原因,lodash
打包好的模块使用缓存即可,只需要重新请求 index.js
打包好的模块,这样我们便提高了客户端加载的效率。
代码分割是 webpack 最引人注目的特性之一。该特性允许您将代码分割成不同的包,这些包可以按需加载,也可以并行加载。它可以用于实现更小的包,并控制资源负载优先级,如果使用正确,将对负载时间产生重大影响。
代码分割的三种方法
- Entry points:使用 entry 配置手动划分代码
就拿我们上面的代码例子举例:
添加 lodash.js ,内容如下:
import _ from 'lodash';
window._ = _;
index.js 内容如下:
console.log(_.join(['hello', 'webpack']));
webpack 配置如下:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: { // 入口
app: './src/index.js',
lodash: './src/lodash.js'
},
devtool: "source-map",
devServer: {
contentBase: './dist',
open: true,
hot: true,
hotOnly: true
},
output: { // 出口
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};
重新执行 yarn build
打包(即 webpack
)后,打包内容如下:

这样我们就通过 entry 配置的方式实现了代码分割。(当然这只是个例子,实际思路确实这样)
lodash 的全局化也可以使用 shimming 功能实现,如下图:

- 使用 SplitChunksPlugin 插件去重和分割块(chunks)
有时候,某个公用模块被多个 js 文件导入使用,index.js
和another.index.js
内容如下:
iimport * as _ from 'lodash';
console.log(_.join(['hello', 'webpack']));
webpack
配置如下:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
app: './src/index.js',
app1: './src/another-index.js'
},
devtool: "source-map",
devServer: {
contentBase: './dist',
open: true,
hot: true,
hotOnly: true
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
resolve: {
extensions: ['.js']
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};
这样我们打包出来的文件如下:

但是我们发现
app.bundle.js
和 app1.bundle.js
文件里面都打包了 lodash
这个模块,这就存在了导入重复的问题。
这时候,就需要使用 SqlitChunksPlugin
来帮助我们解决这个问题了。
SplitChunksPlugin
允许我们将公共依赖项提取到现有的条目块(chunk)或全新的块(chunk)中。
修改 webpack.config.js
文件如下:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
app: './src/index.js',
app1: './src/another-index.js'
},
devtool: "source-map",
devServer: {
contentBase: './dist',
open: true,
hot: true,
hotOnly: true
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
resolve: {
extensions: ['.js']
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
},
// 新添加的内容
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
重新执行 yarn build
(即 webpack)后,我们发现打包出来的目录结构变成了下面形式:

lodash
被提取到一个公共的块(chunk)中,名为 vendors-app-app1.bundle.js
。这样我们就是实现了去重。
- 动态导入:通过模块内的内联函数调用拆分代码。
修改index.js
和another-index.js
文件如下:
import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) =>{
console.log(_.join(['hello', 'webpack']));
});
修改 webpack.config.js
文件内容如下:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
app: './src/index.js',
app1: './src/another-index.js'
},
devtool: "source-map",
devServer: {
contentBase: './dist',
open: true,
hot: true,
hotOnly: true
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
resolve: {
extensions: ['.js']
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
};
执行 yarn build
(即 webpack)打包,打包后的结果如下:

没有任何的配置,动态导入的形式自动帮我们实现了代码划分。
如果想修改打包出来公共模块的名字,我们可以这样做:
在 webpack.config.js
下添加 chunkFileName 选项
output: {
filename: '[name].bundle.js',
chunkFilename: "[name].bundle.js",
path: path.resolve(__dirname, 'dist')
},
这里的 name
就是我们动态导入时候设置的注释,如下的 webpackChunkName
:
import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) =>{
console.log(_.join(['hello', 'webpack']));
});
网友评论