demo5源码地址
package.json文件所用依赖,npm install 安装:
{"script": {"dev": "webpack --mode development","build": "webpack --mode production"},"devDependencies": {"clean-webpack-plugin": "^2.0.0","webpack": "^4.29.6","webpack-cli": "^3.2.3"},"dependencies": {"lodash": "^4.17.11"}}
我们在src/文件夹下创建index.js文件
import _ from 'lodash'console.log(_.join(['a','b','c']))
目录结构为:
配置webpack.config.js文件
let path = require('path');const CleanWebpackPlugin = require('clean-webpack-plugin');module.exports = {entry: {main:'./src/index.js', // 入口文件},output: {publicPath: __dirname + '/dist/', // js 引用的路径或者 CDN 地址path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录filename: '[name].bundle.js', // 代码打包后的文件名chunkFilename: '[name].js' // 代码拆分后的文件名},plugins: [new CleanWebpackPlugin() // 默认情况下,此插件将删除 webpack output.path中所有的文件,以及每次重建后所有未使用的webpack的产品。]}
运行 npm run build 打包
在index.html中使用打包后的文件
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><script src="./dist/main.bundle.js"></script></body></html>
使用浏览器打开 index.html 文件,进入控制台,可以看到如下信息:a,b,c
如果我们再改动业务代码,将 index.js 中的代码改为
import _ from 'lodash'
console.log(_.join(['a', 'b', 'c'], '***'))
再打包,刷新页面可以看到 a***b*****c**
我们引用的第三方框架和我们的业务代码一起被打包,这样会存在着什么问题
假设lodash为11M,业务代码也为11M,打包后就是2M
浏览器每次打开页面,都要先加载2M的文件,才能显示业务逻辑,这样会使得加载时间变长。
业务代码更新会比较频繁,第三方代码基本不会改变,这样重新打包后,假设为2M用户重新打开网页后,又会加载2M的文件
浏览器是有缓存的,如果文件没有变动的话,就不用去再发送http请求,直接从缓存中取,这样在刷新页面或者是第二次进入的时候可以加快网页加载的速度。
怎么解决呢?可以利用webpack中代码分割
在webpack4.0之前是使用commonsChunkPlugin来拆分公共代码,4.0之后废弃,并用 splitChunksPlugins
在使用 splitChunksPlugins 之前,首先要知道 splitChunksPlugins 是 webpack 主模块中的一个细分模块,无需 npm 引入**
现在我们来配置webpack.config.js文件
let path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
main:'./src/index.js', // 入口文件
},
output: {
publicPath: __dirname + '/dist/', // js 引用的路径或者 CDN 地址
path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录
filename: '[name].bundle.js', // 代码打包后的文件名
chunkFilename: '[name].js' // 代码拆分后的文件名
},
optimization: {
splitChunks: {
chunks: 'all'
}
},
plugins: [
new CleanWebpackPlugin() // 默认情况下,此插件将删除 webpack output.path中所有的文件,以及每次重建后所有未使用的webpack的产品。
]
}
上面高亮的代码段就是告诉webpack,要做代码分割了,这里的chunk:‘all’是分割所有的代码,包括同步代码和异步代码,webpack默认是chunks: ‘async’分割异步代码。
我们使用npm run dev来打包开发环境下的代码,这样代码就不会压缩,方便我们来观察,可以看到代码被分割成两个文件了
打开dist/main.bundle.js文件,在最底部可以看到src/index.js,里面放的是业务逻辑代码,但是并没有lodash的代码
打开dist/vendors~main.js文件,在最上面可以看到lodash模块
再次打开页面,控制台也输出了内容,这样就实现了Code Splitting(代码分割)
其实没有webpack的时候,也是有代码分割的,不过是需要我们自己手动的分割,而现在使用了webpack,通过这种配置项的方式,它会自动帮我们去做代码分割
仔细看分割完的代码名称,vendors~main.js,我们对分割完的名称进行更改
还是在splitChunks的配置项中,添加cacheGroups对象
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
name: 'vendors'
}
}
}
}
再次打包就可以看到效果了,cacheGroups 的默认配置会定义venders和default
test: /[\/]node_modules[\/]/,使用正则过滤,只有node_modules引入第三方库才会被分割
为了验证默认配置,我们将splitChunks属性设置为空对象,再次打包
打包完发现只有一个文件,这是为什么?
因为chunks默认为async,只会分割异步的代码,而我们之前写的都是同步的代码,先import lodash,再去写业务逻辑,现在使用异步的方式来做,将index.js中的代码改为如下:
function getComponent() {
// 使用 异步的形式导入 lodash,default: _ 表示用 _ 代指 lodash
return import('lodash').then(({ default: _ }) => {
var element = document.createElement('div')
element.innerHTML = _.join(['hello', 'world'], '-')
return element
})
}
getComponent().then(element => {
document.body.appendChild(element)
})

这里分割出了0.js和main.bundle.js,0是以id为编号来命名
所以一般我们设置chunk是为all,异步,同步代码都在打包
现在我们将webpack官网上的默认配置拷贝到我们的webpack.config.js中来分析一下
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
webpack 代码分割的配置是这样的,比如我们要分割 jQuery 和 lodash 这样的第三方库,它会先经过 chunks、minSize、maxSize、minChunks 等等,满足条件后生成 jQuery 和 lodash 两个文件,然后放入 cacheGroup 中缓存着,再根据你在 cacheGroup 中配置的组来决定是将两个文件整合到一个文件打包,还是单独分开打包,比如上面代码中的 vendors,就是将 node_modules 中所有的第三方库都打包到 vendors.js 文件中,如果你还想继续分割可以这么做
cacheGroups: {
lodash: {
name: 'lodash',
test: /[\\/]node_modules[\\/]lodash[\\/]/,
priority: 5 // 优先级要大于 vendors 不然会被打包进 vendors
},
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
再次打包,就可以看到lodash被分割出来了,以后使用第三方库都可以用这种配置来单独分割成一个 js 文件,比如 element-ui,注意设置 priority 的值很重要,优先级越高的会越先被打包
如果 index.js 引入了 A.js 和 B.js,同时 A、B 又引入了 common,common 被引入了两次,可以被称为公共模块
目录结构为:
代码如下:
// a,js
import './common'
console.log('A')
export default 'A'
// b.js
import './common'
console.log('B')
export default 'B'
// common.js
console.log('公共模块')
export default 'common'
// index.js
import './a.js'
import './b.js'
// 异步代码
function getComponent() {
// 使用异步的形式导入 lodash,default: _ 表示用 _ 代指 lodash
return import('lodash').then(({ default: _ }) => {
var element = document.createElement('div')
element.innerHTML = _.join(['hello', 'world'], '-')
return element
})
}
getComponent().then(element => {
document.body.appendChild(element)
})
上面那种异步的写法可能比较绕,现在精简一下,并且 webpack 对异步代码通过注释可以直接修改打包后的名称,以下代码全部以异步的形式引入
// 异步代码
import(/* webpackChunkName: 'a'*/ './a').then(function(a) {
console.log(a)
})
import(/* webpackChunkName: 'b'*/ './b').then(function(b) {
console.log(b)
})
import(/* webpackChunkName: 'use-lodash'*/ 'lodash').then(function(_) {
console.log(_.join(['1', '2']))
})
将 minChunks 设置为 2,最小公用 2 次才分割
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
lodash: {
name: 'lodash',
test: /[\\/]node_modules[\\/]lodash[\\/]/,
priority: 10
},
commons: {
name: 'commons',
minSize: 0, //表示在压缩前的最小模块大小,默认值是 30kb
minChunks: 2, // 最小公用次数
priority: 5, // 优先级
reuseExistingChunk: true // 公共模块必开启
},
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}

这里分割出了lodash和我们在注释中定义的use-lodash,前者是第三方库,后者是用第三库写的业务代码,也能被分割出来。

这里之所以会自动引入分割后的依赖,可以查看打包后的 main.bundle.js 文件
常用的配置项在下面的表格中,更多配置详情见官网
