性能优化分为两个方面:打包产物优化、开发体验优化
打包产物优化
打包产物优化主要遵循以下三点
- 减小打包的整体体积
- Code Splitting: 按需加载,优化页面首次加载体积。
- Bundle Splitting: 分包,根据模块更改频率分层次打包,重复利用缓存
减小打包的整体体积
- 代码压缩。webpack自作主张在生产环境中默认就把这件事情给做了
- 移除不必要的模块。引入一个模块,虽然没有使用,但是webpack仍然会给打包进去,比如:
// 仅仅引入而未在代码中使用,该模块仍然会被打包
import _ from'lodash'
- 选择可替代的体积较小的模块。比如使用
day.js代替moment.js - 按需引入模块,而不是整体全部引入。 比如:
import DatePicker from'antd/es/date-picker'; // for js
import get from 'lodash.get'
- 开启
tree-shaking
Code Splitting以按需加载
- 使用动态加载模块:
import(), 比如:
// multiple possible targets
import(
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
`./locale/${language}`
);
- lodable-component 动态加载路由,比如:
import React from 'react'
import { Route } from 'react-router-dom'
import { loadable } from 'react-common-lib'
const Test = loadable({
loader: () => import('./test'),
})
const AppRouter = () => (
<>
<Route path="/test" exact component={Test} />
</>
)
Bundle Splitting
该操作是为了优化缓存,当修改文件后,造成最小范围的缓存失效,这样便能够更充分的利用缓存。 比如将react/loash等常用第三方,打包在一起。 可以使用 webpack配置项 splitChunks 进行分包:
splitChunks: {
chunks: "async",
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
开发体验优化
优化webpack一系列配置,主要是减少webpack工作量
1. 优化 Loader 配置
由于 Loader 对文件的转换操作很耗时,所以需要让尽可能少的文件被 Loader 处理。可以通过 test include exclude 三个配置项来命中 Loader 要应用规则的文件。
2. 优化 resolve.modules 配置
resolve.modules 的默认值是['node_modules'],含义是先去当前目录的 node_modules 目录下去找我们想找的模块,如果没找到就去上一级目录 ../node_modules 中找,再没有就去 ../../node_modules 中找,以此类推。 这和 Node.js 的模块寻找机制很相似。
当安装的第三方模块都放在项目根目录的 node_modules 目录下时,就没有必要按照默认的方式去一层层地寻找,可以指明存放第三方模块的绝对路径,以减少寻找.
module.exports = {
resolve: {
modules: [path.resolve( __dirname,'node modules')]
},
}
3. 优化 resolve.alias 配置
resolve.alias 配置项通过别名来将原导入路径映射成一个新的导入路径。
4. 优化 resolve.extensions 配置
后缀尝试列表要尽可能小,不要将项目中不可能存在的情况写到后缀尝试列表中。频率出现最高的文件后缀要优先放在最前面.
module.exports = {
resolve : {
//尽可能减少后缀尝试的可能性
extensions : ['js'],
}
}
5. 使用 HappyPack
Webpack 是单线程模型的,也就是说 Webpack 需要一个一个地处理任务,不能同时处理多个任务。HappyPack将任 务分解给多个子进程去并发执行,子进程处理完后再将结果发送给主进程,从而发挥多核 CPU 电脑的威力。
const HappyPack = require('happypack')
const os = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
{
test: /\.js$/,
// loader: 'babel-loader',
loader: 'happypack/loader?id=happy-babel-js', // 增加新的HappyPack构建loader
include: [resolve('src')],
exclude: /node_modules/,
}
plugins: [
new HappyPack({
id: 'happy-babel-js',
loaders: ['babel-loader?cacheDirectory=true'],
threadPool: happyThreadPool
})
]
6. 优化文件监昕的性能
在开启监听模式时,默认情况下会监听配置的 Entry 文件和所有 Entry 递归依赖的文件,在这些文件中会有很多存在于 node_modules 下,因为如今的 Web 项目会依赖大量的第三方模块, 所以在大多数情况下我们都不可能去编辑 node_modules 下的文件,而是编辑自己建立的源码文件,而一个很大的优化点就是忽略 node_modules 下的文件,不监听它们。
module.export = {
watchOptions : {
//不监听的 node_modules 目录下的文件
ignored : /node_modules/,
}
}