一、缩小文件搜索范围
1、优化loader的配置
由于loader对文件的转换操作很耗时,我们需要尽可能少的文件被loader处理
一般配置有两种:
- include:处理包含的文件
-
配置
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // node_modules文件夹下的js文件不需要用loader处理
loader: "babel-loader",
options: {
presets: [["@babel/preset-env"]]
},
include: path.resolve(__dirname, 'src') // loader只处理src目录下的js文件
}
]
}
}
优化指标
打包时间减少了
- 1056ms =》815ms
2、优化resolve.modules配置
resolve.modules用于配置webpack去哪些目录下寻找第三方模块resolve.modules
的默认值是['node_modules']
,含义是先去当前目录下的./node_modules
目录下去找想找的模块,如果没找到就去上一级目录../node_modules
中找,再没有就去../../node_modules
中找,以此类推,这和 Node.js 的模块寻找机制很相似。
当安装的第三方模块都放在项目根目录下的./node_modules
目录下时,没有必要按照默认的方式去一层层的寻找,可以指明存放第三方模块的绝对路径,以减少寻找。配置
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'node_modules')]
}
}
- 1056ms =》815ms
使用绝对路径指明第三方模块存放的位置,减少搜索步骤
-
优化指标
-
3、resolve.mainFields配置
resolve.mainFields
用于配置第三方模块使用哪个入口文件。
如果有明确的第三方模块入口文件,可以设置尽量少的搜索配置
module.exports = {
resolve: {
mainFields: ['main']
}
}
该优化方案需要注意:需要考虑到所有运行时依赖的第三方模块的入口文件描述字段,就算有一个模块搞错了都可能会造成构建出的代码无法正常运行。
优化指标
-
4、优化resolve.alias配置
resolve.alias
配置项通过别名来把原导入路径映射成一个新的导入路径。
在实战项目中为了解析某个东西,需要将某个某个东西指定文件解析配置
module.exports = {
resolve: {
// 使用 alias 把导入 react 的语句换成直接使用单独完整的 react.min.js 文件,
// 减少耗时的递归解析操作
alias: {
'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js'), // react15
// 'react': path.resolve(__dirname, './node_modules/react/umd/react.production.min.js'), // react16
}
},
};
优化指标
-
5、优化resolve.extensions配置
配置
在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试询问文件是否存在。
resolve.extensions
用于配置在尝试过程中用到的后缀列表,默认是:module.exports = {
resolve: {
// 尽可能的减少后缀尝试的可能性
extensions: ['js'],
},
};
6、优化module.noParse配置
忽略对一些没有采用模块化的库文件进行递归解析处理。
“不去解析jquery中的依赖库”配置
module.exports = {
module: {
// 独完整的 `react.min.js` 文件就没有采用模块化,忽略对 `react.min.js` 文件的递归解析处理
noParse: [/react\.min\.js$/],
},
};
7、new webpack.IgnorePlugin
配置
```javascript new webpack.IgnorePlugin({ checkResource(resource) { // do something with resource return true | false; }, });
<a name="mQoP4"></a>
## 二、使用DllPlugin
动态链接库<br />可以先把react 等库打包到dll中,下次就不会重新打包了
```javascript
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '')
},
plugins: [
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, 'dist', 'manifest.json')
}),
]
三、多线程/多实例构建
HappyPack不维护了,
4之后的版本使用:thread-loader
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
use: [
"thread-loader",
"expensive-loader"
]
}
]
}
}
有缺点,使用需谨慎,请仅在耗时的 loader 上使用。
把这个 loader 放置在其他 loader 之前, 放置在这个 loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行
在 worker 池(worker pool)中运行的 loader 是受到限制的。例如:
- 这些 loader 不能产生新的文件。
- 这些 loader 不能使用定制的 loader API(也就是说,通过插件)。
-
优化指标
-
四、并行压缩js代码
配置
npm i -D webpack-parallel-uglify-plugin
```javascript const ParalleUglifyPlugin = require(‘webpack-parallel-uglify-plugin’);
module.exports = {
plugins: [
// 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
new ParalleUglifyPlugin({
uglifyJs:{
output: {
beautify:false, // 最紧凑的输出
comments:true, // 删除所有的注释
},
compress:{
warnings: false, //在UglifyJs删除没有用到的代码时不输出警告
drop_console:true, // 删除所有的 console
语句,可以兼容ie浏览器
collapse_vars:true, // 内嵌定义了但是只用到一次的变量
reduce_vars:true // 提取出出现多次但是没有定义成变量去引用的静态值
}
}
})
]
}
<a name="BvJxJ"></a>
#### 优化指标
- 输出文件变小了
<a name="P1ENL"></a>
## 五、使用自动刷新
<a name="kZAmP"></a>
### 文件监听
```javascript
module.export = {
// 只有在开启监听模式时,watchOptions 才有意义
// 默认为 false,也就是不开启
watch: true,
// 监听模式运行时的参数
// 在开启监听模式时,才有意义
watchOptions: {
// 不监听的文件或文件夹,支持正则匹配
// 默认为空
ignored: /node_modules/,
// 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
// 默认为 300ms
aggregateTimeout: 300,
// 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
// 默认每隔1000毫秒询问一次
poll: 1000
}
}
文件监听工作原理
优化文件监听性能
module.export = {
watchOptions: {
// 不监听的 node_modules 目录下的文件
ignored: /node_modules/,
}
}
自动刷新浏览器
监听到文件更新后的下一步是去刷新浏览器,webpack 模块负责监听文件,webpack-dev-server 模块则负责刷新浏览器。
自动刷新浏览器优化
六、开启热更新
const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');
module.exports = {
entry:{
// 为每个入口都注入代理客户端
main:['webpack-dev-server/client?http://localhost:8080/', 'webpack/hot/dev-server','./src/main.js'],
},
plugins: [
// 该插件的作用就是实现模块热替换,实际上当启动时带上 `--hot` 参数,会注入该插件,生成 .hot-update.json 文件。
new HotModuleReplacementPlugin(),
],
devServer:{
// 告诉 DevServer 要开启模块热替换模式
hot: true,
}
}
devServer: {
clientLogLevel: "none", // 可选的值:none,error,waring 或者 info(默认值)
hot: true, // 启用webpack的模块替换热更新特性,这个需要配合“webpack.HotModuleReplacementPlugin”插件
contentBase: path.join(__dirname, "dist"), // 告诉服务器从哪里提供内容,默认情况下使用当前工作目录
compress: true, // 一切服务都启用gzip 压缩:
host: "0.0.0.0", // 指定使用一个 host。默认是 localhost。如果你希望服务器外部可访问,指定如下:
port: 8080, // 端口
open: true, // 是否打开浏览器
overlay: { // 出现错误或者警告的时候,是否覆盖页面线上错误信息
warnings: true,
errors: true
},
publicPath: "/", // 此路径下的打包文件可在浏览器中访问
proxy: { // 设置代理
"/api": { // 访问api开头的请求,会跳到下面的路径
target: "http://localhost:3000",
pathRewrite: {"^/api" : ""}
}
},
quiet: true, // 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。
watchOptions: { // 与监视文件相关的控制选项。
poll: true, // 主动去拉取文件, webpack 使用文件系统获取文件改动的通知,
ignored: /node_modules/, // 忽略监听
aggregateTimeout: 300, // 默认值,当第一个文件更改,会在重新构建钱增加延迟,这就是事件防抖
}
},
plugins: [
new webpack.HotModuleReplacementPlugin(), // 替换插件
new webpack.NamedModulesPlugin() // 更容易查看patch的依赖
]
七、区分环境
八、代码压缩
压缩js和css
const uglify = require('uglifyjs-webpack-plugin');
plugins: [
new uglify()
]
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
rules: [
{
test: /\.css$/,
// css-loader 用来解析处理css文件中的url路径
// style-loader 可以把css文件变成style标签插入到header中
// 多个loader是有顺序要求的,从右往左写,因为转换的时候是从右往左转换
// loader: ['style-loader', 'css-loader']
use: [
//添加loader,用于生产模式,不能有 style-loader
{
loader:MiniCssExtractPlugin.loader,
options:{
publicPath:'../'
}
},
"css-loader"
]
}
]
plugins: [
// 将css抽离出来
new MiniCssExtractPlugin({
filename: "css/[name].css",
chunkFilename: "[id].css"
}),
// 压缩css代码
new OptimizeCSSAssetsPlugin({}),
]
优化指标
- 代码小了
-
九、CDN加速
十、Tree Shaking
Tree Shaking 可以用来剔除 JavaScript 中用不上的死代码。
purgecess-webpack-plugin和mini-css-extract-plugin配合使用
- 打包过程中检测工程中没有引用过的模块进行标记,在资源压缩时将他们从最终的bundle中去掉(只能对ES6module生效)开发中尽可能使用Es6 Module的模块,提高tree shaking的效率
禁用babel-loader的模块依赖解析,否则webpack接收到的就都是转换过的,无法进行tree shake
十一、开启Scope hoisting
Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行的更快, 它又译作 “作用域提升”
好处:
代码体积更小,因为函数申明语句会产生大量代码;
- 代码在运行时因为创建的函数作用域更少了,内存开销也随之变小。
原理
分析出模块之间的依赖关系,尽可能的把打散的模块合并到一个函数中去,但前提是不能造成代码冗余。 因此只有那些被引用了一次的模块才能被合并。
由于 Scope Hoisting 需要分析出模块之间的依赖关系,因此源码必须采用 ES6 模块化语句,不然它将无法生效。配置
要在 Webpack 中使用 Scope Hoisting 非常简单,因为这是 Webpack 内置的功能,只需要配置一个插件 ```javascript const ModuleConcatenationPlugin = require(‘webpack/lib/optimize/ModuleConcatenationPlugin’);
module.exports = { plugins: [ // 开启 Scope Hoisting new ModuleConcatenationPlugin(), ], // 为了充分发挥scope hoisting的作用, // 需要配置mainFields对第三方模块优先采用jsnext:main中指向的ES6模块化语法 resolve: { // 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件 mainFields: [‘jsnext:main’, ‘browser’, ‘main’] }, };
<a name="VusEz"></a>
## 十二、按需加载
```javascript
'@babel/plugin-syntax-dynamic-import'
十三、提取公共资源
十四、图片压缩
- 使用基于Node库的imagemin(很多定制项、可以处理多种图片格式)
-
十五、动态Polyfill
建议采用polyfill-service只给用户返回需要的polyfill
- @babel-parset-env中通过useBuiltlins:usage参数来动态加载polyfill
十六、分析打包
speed-measure-webpack-plugin
简称:SMP,分析出webpack打包过程中Loader、Plugin的耗时,有助于构建过程中的性能瓶颈。
官方可视化分析工具webpack-bundle-analyzer
是另一个可视化分析工具, 它虽然没有官方那样有那么多功能,但比官方的要更加直观。十七、代码抽离
module.exports = {
optimization: {
splitChunks: { // 分割代码块
cacheGroups: { // 缓存组
common: { // 公共的模块
chunks: 'initial',
minSize: 0,
minChunks: 2
},
vendor: {
priority: 1,
test: /node_modules/,
chunks: 'initial',
minSize: 0,
minChunks: 2
}
}
}
}
}