目前负责的项目,是未通过脚手架和其他框架搭建的 react 项目。开发过程中,启动项目和热更新速度都不是很理想;打包构建时,速度也比较缓慢且由于单个文件过大,用户在访问时,加载动画的时间也比较长。所以打算对其进行部分优化工作。 本着提升 webpack 性能,最直接的方式就是升级 webpack 版本的思路。对项目中的进行了一系列的升级和优化。升级和优化的结果如下: 编译速度方面:
- 开发启动项目的速度提升了(非修改配置/初次编译)1180%;
- 开发过程中代码热更新速度变化不大,但开发体验更好,浏览器更新进行局部更新而不是 reload;
- 生产环境打包构建的速度提升了(非修改配置/初次编译)201%。
体积方面:
- 项目中的两个大文件体积分别减少了 77%和 86% 左右,大大加快了页面的加载速度。
查看性能相关的插件:
speed-measure-webpack-plugin
测量 plugin、loader 等时间;webpack-bundle-analyzer
打包分析工具。
根据官方迁移文档升级
- 升级Node.js ,至少 10.13.0 版本;
- 升级 webpack 4 及其相关的plugin/loader;
- 升级 webpack 4 至最新版本,升级 webpack-cli 至最新版本;
- 升级使用的 plugin 和 loader 为最新版本。
// 检查过时的依赖包
npm outdated
// 安装最新版本依赖包
npm install package@latest
- 获取堆栈信息中的弃用警告;
// 如果不存在默认的 webpack.config.js 配置,则需要 --config 指定配置文件
node --trace-deprecation node_modules/webpack/bin/webpack.js
- 测试 webpack5 兼容性 添加以下配置选项,检查构建是否正常运行(完成后删除这些配置选项)。
module.exports = {
node: {
Buffer: false,
process: false
},
}
- 升级至 webpack5
npm install webpack@latest -D
- babel 升级到7,升级指南:链接。以及相应的插件。 具体注意事项查看官方迁移指南:To v5 from v4
升级后调整和优化
调整
- 使用
asset
代替file-loader url-loader raw-loader
。 - JavaScript 代码压缩使用 webpack5 内置的
terser-webpack-plugin
。
优化编译和开发速度
cache: filesystem
持久缓存。能够大大提升二次构建速度(修改配置文件除外)。
{
cache: {
type: "filesystem",
buildDependencies: {
// config 添加为 buildDependency,以便在改变 config 时获得缓存无效
config: [__filename],
},
name: process.env.NODE_ENV,
version: "1.0.0",
},
}
- 使用
thread-loader
进行多进程构建。
// worker 启动有一定的消耗,
// 可以预热 worker 池
const jsWorkerPool = {
workerParallelJobs: 80,
poolTimeout: 2000,
};
const cssWorkerPool = {
workerParallelJobs: 10,
poolTimeout: 2000,
};
threadLoader.warmup(jsWorkerPool, ["babel-loader"]);
threadLoader.warmup(cssWorkerPool, ["css-loader", "less-loader"]);
- 开发时,自动保存代码导致构建频繁且会报错,又不想手动保存,则可以开启延迟构建。
devServer: {
...
watchOptions: {
// 延迟构建
aggregateTimeout: 1500,
ignored: /node_modules/,
},
},
- css 模块热替换:css 借助
style-loader
,可以实现模块热替换; - JavaScript 模块热替换:可以使用社区的库或插件实现,否则会通过 reload 的方式刷新。
- 如果符合以下条件,可使用
[react-refresh-webpack-plugin](https://github.com/pmmmwh/react-refresh-webpack-plugin)
插件;
- 如果符合以下条件,可使用
{
plugins: [
new ReactRefreshWebpackPlugin()
]
}
- 若不符合上面的条件,可使用react-hot-loader库
// .babelrc
{
"plugins": [
...,
"react-hot-loader/babel"
]
}
// index.js
import { hot } from 'react-hot-loader';
...
const App = hot(module)(() => ...);
render(<App />, document.getElementById('root'));
优化编译体积
打包后体积优化,主要是使用 Gzip 压缩,将打包后体积较大的文件使用 Gzip 压缩。项目中的两个大文件体积分别减少了 77%和 86% 左右,大大加快了页面的加载速度。
Gzip 压缩
Gzip(GNU- ZIP) 是一种压缩技术。经过压缩的页面能大大加快浏览器端的加载速度。 启用 Gzip 需要服务器端和浏览器端都支持:服务器端压缩(提供压缩的文件),浏览器端解压。 注意:
- 图片/mp3 等不必压缩,压缩率较小;
- 使用 proxy_pass 反向代理页面,gzip——http_version 需要修改为 1.0;
- 静态压缩:gzip_static on;
- 动态压缩:gzip on。
其他
其他体积优化需要修改业务相关代码较多,就暂未进行优化。
遇到坑以及相关解决
DeprecationWarning:[DEP_WEBPACK_COMPILATION_NORMAL_MODULE_LOADER_HOOK] DeprecationWarning: Compilation.hooks.normalModuleLoader was moved to NormalModule.getCompilationHooks(compilation).loader
speed-measure-webpack-plugin
插件兼容性问题,目前还没有完全和 webpack5 兼容,可以在优化完成后移除相关配置。
-
Error: Cannot find module 'webpack-cli/bin/config-yargs
4.X版本的 webpack-cli 移除了yargs包,需要使用
webpack serve
启动webpack-dev-server
。
// package.json
{
"script": {
"dev": "webpack serve --config webpack.dev.js"
}
}
Support for the experimental syntax 'classProperties' isn't currently enabled
@babel/preset-env
不包含小于 Stage 3 的语法提案,需要手动安装相应插件。需要安装插件@babel/plugin-proposal-class-properties
。
//.babelrc
{
"plugin":[
"@babel/plugin-proposal-class-properties"
]
}
Support for the experimental syntax 'decorators-legacy' isn't currently enabled
原因同上,需要安装插件@babel/plugin-proposal-decorators
。
//.babelrc
{
"plugin":[
["@babel/plugin-proposal-decorators", {"legacy": true}]
]
}
expose-loader
升级后,报错:ValidationError: Invalid options object. Expose Loader has been initialized using an options object that does not match the API schema.
// webpack 配置文件
module: {
rules:[
...,
{
use: {
loader: "expose-loader",
options: {
exposes: "videojs"
}
}
}
]
}
// **.js 使用
require('videojs');
webpack.HotModuleReplacementPlugin
和hot: true
共同使用时,无法热更新。
hot: true
会自动添加webpack.HotModuleReplacementPlugin
插件,无需再次添加配置。
7. speed-measure-webpack-plugin
插件在 build 时,无法使用。
8. webpack5 不再为 Node.js 提供自动引入polyfills。
根据错误提示修改即可。
-
生产环境中构建完成,但是未退出命令
thread-loader
插件在生产环境中,预热 worker 池会导致此问题。 -
升级后,webpack 对代码要求更加严格,需要按需修改。