webpack模块打包机:分析项目结构,找到js模块以及其他 一些浏览器不能直接运行的拓展语言,将其打包成合适的格式以供浏览器使用
主要做以下的工作:
- 代码转换:比如说TS编译成js、scss编译成css
- 文件优化:压缩js、css、html代码,压缩合并图片
- 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
- 模块合并:在采用模块化的项目里会有很多的模块和文件,需要构建功能把模块分类合并成一个文件。
- 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器
- 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统
总结:Chunk是过程中的代码块,Bundle是结果的代码块。
关于webpack的性能优化,主要体现在三个方面:
`构建性能`:是指在开发阶段的构建性能。当构建性能越高,开发效率越高。
`传输性能`:在这方面重点考虑网络中的总传输量、JS文件数量以及浏览器缓存。
`运行性能`:主要是指JS代码在浏览器端运行的速度。
1构建性能
是指在开发阶段的构建性能。当构建性能越高,开发效率高越高
1.1 减少模块解析(模板解析包括:AST抽象语法书分析、依赖分析、模板语法替换,对某个模块不进行解析,可以缩短构建时间)
如果某个模板不做解析,该模板经过Loader处理后的代码就是最终代码。
如果没有loader对该模块进行处理,该模块的源码就是最终打包结果的代码。
module.exports = {
mode: "development",
module: {
noParse: /JQuery/
}
}
1.2 限制loader的应用范围
针对一些第三方库,不使用loader进行处理。例如babel-loader,转换一些本身就是用ES5语法书写的第三方库,反而会浪费构建时间。
因此通过module.rules.exclude或module.rules.include,排除或仅包含需要应用loader的场景。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
//或
// include: /src/,
use: "babel-loader"
}
]
}
}
1.3 开启多线程打包
通过thread-loader会开启一个线程池,它会把后续的loader放到线程池的线程中运行,以提高构建效率。
thread-loader可以通过测试决定放置的位置。
module.exports = {
module: {
rules: [{
test: /\.js$/,
use: [
"thread-loader",
"babel-loader"
]
}]
}
};
1.6 热替换
let webpack = require('webpack')
module.exports = {
devServer: {
open: true,
hot: true //开启HMR
},
module: {
rules: [{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
]
}
module.hot.accept()的作用是让webpack-dev-server通过socket管道,把服务器更新的内容发送到浏览器,然后,将结果交给插件HotModuleReplacementPlugin注入的代码执行插件HotModuleReplacementPlugin会根据覆盖原始代码,然后让代码重新执行。
2 传输性能
重点考虑网络中的总传输量、JS文件数量以及浏览器缓存。
2.1 分包
chunk的默认分包规则:
1. 同一个entry入口模块与他的同步依赖组织成一个chunk
2. 每一个异步模块与他的同步依赖单独组成一个chunk。其中只会包含入口chunk中不存在的同步依赖;若存在同步第三方包,也会被单独打包成一个chunk。
参考博客
buildChunkGraph(是 chunk 生成阶段)的三个子方法按顺序来详解:
1.visitModules:
2.connectChunkGroups:
3.cleanupUnconnectedGroups:
可以使用splitChunks来指定规则,自动分包,下面讲一下splitChunks的用法:
splitChunks主要作用是提取公共代码,防止代码被重复打包,拆分过大的js文件,合并零散的js文件。
chunks 选项,决定要提取哪些模块initial:提取同步加载和异步加载模块;all:不管异步加载还是同步加载的模块都提取出来,打包到一个文件中;vue-admin(github有公开代码)后台管理系统中的分块代码。 config
.optimization.splitChunks({
// 针对哪些chunks进行优化,all(对所有chunk应用分包策略)
// 比如说element-ui也属于基础类库,配置中如果不设置{chunk-ui}.priority的话,会被切分到chunk-libs中
chunks: 'all',
cacheGroups: {
//会将node-modules文件夹下的模块打包进chunk-libs文件夹下。
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,//进入分组的优先级
chunks: 'initial' // only package third parties that are initially dependent
},
elementUI: {
name: 'chunk-elementUI', // 将elementUI拆分为单个包
priority: 20, // 权重需要大于libs和app,否则将打包到libs或app中
//用正则表达式获取node_modules中的element-ui文件夹
test: /[\\/]node_modules[\\/]_?element-ui(.*)/
},
//将src下的components组件打包进chunk-commons文件夹下,且引用超过三次的模块,需要单独拆分。
commons: {
name: 'chunk-commons',
test: resolve('src/components'),
// 拆分前必须共享模板的最小 chunks 数。默认值为1
// 这里主要是判断是否有多个 chunk 共享(引用)了这个 chunk,对于公共组件来说如果只有极少的 chunk 去引用,其实也是不需要进行单独拆分
minChunks: 3,
priority: 5,
//遇到重复报直接引用,不用重新打包
reuseExistingChunk: true
}
}
})
2.3 代码压缩
module.exports = {
optimization:{
minimize:true, //是否启动压缩,默认是只在生产环境自动开启。
minimizer:[
//压缩时使用的插件,当你自动改压缩配置时必须配置相关压缩插件
new TerserPlugin(),//js压缩插件
new OptimizeCSSAssetsPlugin()//css压缩插件
]
}
}
2.6 gzip
const CompressionWebpackPlugin = require('compression-webpack-plugin')
module.export = {
plugin:[
new CompressionWebpackPlugin({
test:/\.js$/ //针对需要预压缩的文件
minRatio:0.5 //压缩比率
})
]
}
在用这两个插件之前都需要先用npm进行下载
1、采用webpack-bundle-analyzer插件
可以看到 bundle 的大小以及具体的 code splitting 之后的分包情况,以便于我们对 bundle 进行量化分析。
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
.......
chainWebpack(config) {
config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin)
}
下面的是我的一个vue项目打包后的一个性能分析。后台运行:npm run build

2、speed-measure-webpack-plugin(可以查看文件打包速度)
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin({
outputFormat: 'human'
})
configureWebpack: smp.wrap({
// ...
})
后台运行:npm run build

vue-admin没有了webpack.config.js文件,是直接在vue.config.js里的chainWebpack方法直接配置,这样做法的好处是用户既可以保留webpack的默认配置,又可以通过chainWebpack设置更具针对性的参数,链式的写法正是避免了用户直接面对webpack多而复杂的配置项。
当用npm run build进行打包的时候,事实上是根据cacheGroups的默认配置进来分包并包的。
1 分包策略的优化
在默认策略的基础上,进行了优化。针对体积大小、公用率、更新频率进行重新划分包。
2 Preload / Prefetch
Prefetch是一种浏览器机制,浏览在下载或者预取用户可能要访问的文档。具体来说是通过 来实现预提取,设置 prefetch 的影响就是会降低首屏打开的速度,因为浏览器去需要加载这个资源。在 webpack4 中是默认开启 preferch 的,在首屏 index.html中会把十几个页面路由文件,一口气下载下来,可以看到打包后的 index.html中加载了非常多没有意义的 js 和 css 文件。
为了提升首屏打开速度,一般会关闭 prefetch 这个功能,这样首屏只会记载当前页面路由的组件。当然如果真的有大概率即将被访问的资源也可以使用 prefetch 配置来提升性能和体验。
module.exports = {
chainWebpack(config) {
// ...
// when there are many pages, it will cause too many meaningless requests
config.plugins.delete('prefetch')
}
}
Preload也是使用来实现预下载.
config.plugins.delete('prefetch')
config.plugin('preload').tap(() => [
{
rel: 'preload',
//限制比如 runtime 等文件可以不用预下载的文件,以提升首屏打开速度。
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
include: 'initial'
}
])
总结: