构建优化
1、 noParse(无需解析内部依赖的包)
对于我们引入的一些第三方包,比如jQuery
,在这些包内部是肯定不会依赖别的包,所以根本不需要webpack去解析它内部的依赖关系,可以在webpack
配置文件中的module
属性下加上noParse
属性,它的值是一个正则表达式,用来匹配无需解析的模块,这样可以节约webpack
的打包时间,提高打包效率。
module:{
noParse:/jquery/
}
需要注意的是必须确保添加的包中没有依赖别的包,否则会报依赖错误。
2、DllPlugin(动态链接库)
项目中依赖的一些第三方包比如react
、vue
一般情况下包的内容不会发生改变,而每一次打包都要对它们进行构建显然是不合理的,这会很浪费性能。正确的做法应该是将这些第三方包只打包一次,之后直接引用就可以,直到第三方包需要更新版本时再重新进行构建。这样我们在打包的时候只需要构建我们的业务代码即可。
Dllplugin
插件可以帮助我们把这些不做修改的包抽取为动态链接库,并且会生成一个名为manifest.json的文件,这个文件是用来让DLLReferencePlugin
映射到相关的依赖上去的。
以React
为例抽取Dll
- 首先创建一个将
React
抽取为Dll的webpack
配置文件webpack.react.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'development',
// 入口文件:将要配置成Dll的库放进来
entry: {
react: [
'react'
]
},
// 输出文件
output: {
// 输出文件名
filename: '[name]_dll.js',
// 输出文件路径
path: path.resolve(__dirname, '../dist'),
// 将打包好的结果暴露在全局
library: '[name]_dll'
},
plugins: [
new webpack.DllPlugin({
// dll文件名
name: '[name]_dll',
// 清单文件路径
path: path.resolve(__dirname, '../dist/mainfest.json')
})
]
}
- 配置
DllReferencePlugin
这个插件一般是在webpack
的主配置文件中配置,在plugins
配置下添加如下配置,manifest
文件会将对dll文件的应用映射到模块的id
上,然后在需要的时候来引用Dll
文件。
new webpack.DllReferencePlugin({
// (绝对路径) manifest (或者是内容属性)中请求的上下文
context: __dirname,
// 清单文件路径
manifest: path.resolve(__dirname, '../dist/manifest.json')
})
- 构建
然后运行
webpack.react.js
配置文件,一般情况下可以在package.json中添加运行脚本命令
scripts:{
"build:react": "webpack --config ./build/webpack.react.js"
}
在命令行中运行该命令,这会在dist
目录下生成react
的Dll
文件,还会生成一个mainfest.json
文件,这个文件包含了import
和require
的request
到模块id
的映射。DLLReferencePlugin
也会引用这个文件。然后再进行打包,就不会对react
重复打包,而是直接引用生成的react_dll
文件。
代码优化
代码优化的最终目的是为了用户体验的优化,各种压缩、拆分操作都应该围绕最终目标展开。
1、webpack自带优化
把webpack
的mode
设置为production
,进行生产模式打包的时候webapck
会自动进行以下优化
- tree shaking
webpack
会在打包时移除引入了但是未引用的代码,但是只有通过ES6模块系统中import
语法引入的才会适用tree shaking
,减小生产环境下文件的体积,自动实现最基本的优化
- scope hoisting
分析模块之间的依赖关系,会尽可能的将打散的模块合并到一个函数中去。需要注意的是这种优化方式也是只有通过ES6
模块系统中import
语法引入的模块才适用,并且只有那些只被引用的一次的模块才有可能被合并。
- 代码压缩
所有代码自动使用UglifyJsPlugin
进行压缩。
2、CSS优化
- 将CSS拆分为独立文件
mini-css-extract-plugin
可以将CSS拆分为单独的文件,使CSS文件可以被异步加载,加快页面加载呈现的速度。
使用方法:
- 安装插件
$ npm i mini-css-extract-plugin -D
- 修改主配置文件,使用该插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
...,
module: {
rules: [
...,
{
test: /\.css$/,
// 将原来多有使用style-loader来处理的地方替换为使用MiniCssExtractPlugin.loader
// use: ['style-loader', 'css-loader'],
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
...,
new MiniCssExtractPlugin({
// 生成的文件名
filename: '[name].css'
})
]
}
- 压缩CSS
optimize-css-assets-webpack-plugin
插件可以用来压缩CSS文件,但使用该插件会导致webpack默认的js压缩配置无法生效,所以还需要手动对js代码进行压缩,js压缩使用官方推荐的terser-webpack-plugin
使用方法:
- 安装插件
$ npm i optimize-css-assets-webpack-plugin terser-webpack-plugin -D
- 修改主配置文件,使用该插件
const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
...,
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
}
}
3、 JS优化
- 拆分公共代码
使用方法:只需在主配置文件中添加如下配置即可
Optimization: {
splitChunks: {
Chunks: 'all'
}
}
- IgnorePlugin(忽略插件)
有很多的第三方包内部会做国际化处理,包含很多的语言包,而这些语言包对我们来说时没有多大用处的,只会增大包的体积,我们完全可以忽略掉这些语言包,从而提高构建效率,减小包的体积。
以moment为例,首先找到moment中语言包所在的文件夹,然后在webpack配置文件中添加插件
// 该方法的两个参数都是正则,第一个参数表示要忽略的路径,第二个表示该资源所在目录,在该文件夹下引入的语言包都会被忽略
new webpack.IgnorePlugin(/\.\/locale/, /moment/)
这时候moment使用默认语言英语,如果要使用别的语言,可以手动引入需要使用的语言包。
import moment from 'moment'
import 'moment/locale/zh-cn'
moment.locale('zh-CN')
- 懒加载
在页面中有一些代码块可能并不是在页面初始化的时候就需要使用的,需要用户的某些操作出发才会使用,如果用户不触发这个操作那这个代码块可能永远不会被使用。我们可以将这些某个操作触发才会使用的代码块按需加载进来,而不是在页面创建的时候直接加载,这样可以加快初始页面的加载速度。
// 假设在页面中有一个id为btn的按钮,当点击这个按钮的时候需要对时间进行操作
const btn = document.querySelector('#btn')
btn.onclick = e => import(/* webpackChunkName: "moment" */ 'moment').then(module => {
var moment = module.default;
moment().format('YYYY-MM-DD')
})
但是使用懒加载会影响用户体验,所以在懒加载的同时可以使用魔法注释:Prefetching
,可以在首页资源加载完毕后,空闲时间时,将动态导入的资源加载进来,这样即可以提高页面加载速度又保证了用户体验。
const btn = document.querySelector('#btn')
btn.onclick = e => import(/* webpackChunkName: "moment" *//* webpackPrefetch: true */ 'moment').then(module => {
var moment = module.default;
moment().format('YYYY-MM-DD')
})
保持更新版本
webpack每次版本更新都会在性能上有很大提升,官方也推荐保持版本的更新。