项目打包慢,包的体积大这个问题一直是我的心头大事。
碎碎念
在项目测试阶段,发现问题修改后,打包就需要1小时,无奈只能等着把包打完,经常性的我们下班回去打包。而对于后台来说也是一个问题,每次把包解压到服务器上又是一个漫长的过程,也会因为前端包的版本过多,服务器的内存不够,需要定期的去清理前端包。
对于这件事情我也尝试了很多次,但是都以失败告终,总会因为时间不够用,优化到一半被拉去开会,或者是上线任务紧急,不得以暂时放下这件事。
今年把这件事情提上了日程,终于可以花全部的精力去做这件事情啦!
准备工作
对于react脚手架打包优化的问题,我也在网上翻阅了资料,试过了路由懒加载、Gzip、图片体积,这些优化后,并没有任何的效果。又通过安装webpack-bundle-analyzer插件来分析哪些包或者文件过大,从而进行优化。
分析过后,发现react、antd等等依赖包的体积都比较大,想到这些依赖的版本很少去变动,就决定才去DllPlugin把这些依赖包进行单独构建
DllPlugin原理
DllPlugin 可以将特定的类库提前打包成动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据,把基础模块独立出来打包到单独的动态连接库里,当需要导入的模块在动态连接库里的时候,模块不用再次被打包,而是去动态连接库里获取。
这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。
代码实现
- 在根目录下新建文件webpack.dll.config.js
const path = require('path')
const webpack = require('webpack')
const {
CleanWebpackPlugin,
} = require('clean-webpack-plugin')
// dll文件存放的目录
const dllPath = 'public/vendor'
module.exports = {
entry: {
// 需要提取的库文件
vendor: ['react', 'antd', 'ali-oss', 'react-dom', 'react-router-dom', 'axios', 'less-loader'],
},
output: {
path: path.join(__dirname, dllPath),
filename: '[name].dll.js',
// vendor.dll.js中暴露出的全局变量名
// 保持与 webpack.DllPlugin 中名称一致
library: '[name]_[hash]',
},
plugins: [
// 清除之前的dll文件
new CleanWebpackPlugin(),
// 设置环境变量
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: "'production'",
},
}),
// manifest.json 描述动态链接库包含了哪些内容
new webpack.DllPlugin({
path: path.join(__dirname, dllPath, '[name]-manifest.json'),
// 保持与 output.library 中名称一致
name: '[name]_[hash]',
context: process.cwd(),
}),
],
}
- package.json文件
...
"scripts": {
"start": " react-app-rewired start",
"build": " react-app-rewired --max_old_space_size=15000 build",
"dll": "webpack --mode production --progress --config ./webpack.dll.config.js"
},
-
执行命令:
npm run dll在public文件夹下面生成vendor文件即可 -
在config-overrides.js文件加入
/* eslint-disable */
const path = require('path')
const {
override,
fixBabelImports,
addLessLoader,
addWebpackPlugin,
addWebpackAlias,
} = require('customize-cra')
const rewireReactHotLoader = require('react-app-rewire-hot-loader')
const ProgressBarPlugin = require("progress-bar-webpack-plugin")
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const chalk = require("chalk")
const webpack = require("webpack")
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
const addCustomize = () => config => {
if (process.env.NODE_ENV === 'production') {
config.devtool = false; //去掉map文件
config.plugins.push(
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require('./public/vendor/vendor-manifest.json')
}),
// 将 dll 注入到 生成的 html 模板中
new AddAssetHtmlPlugin({
// dll文件位置
filepath: path.resolve(__dirname, './public/vendor/vendor.dll.js'),
// dll 引用路径
publicPath: './vendor',
// dll最终输出的目录
outputPath: './vendor'
}),
// 释放 可以解析项目
// new BundleAnalyzerPlugin({
// analyzerMode: 'static'
// })
)
}
return config;
}
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true,
}),
addLessLoader({
lessOptions: {
javascriptEnabled: true,
modifyVars: { '@primary-color': '#40A9FF' },
},
}),
addWebpackPlugin(
// 打包进度条:含内容、进度条、进度百分比、消耗时间
new ProgressBarPlugin({
format: ` :msg [:bar] ${chalk.green.bold(":percent")} (:elapsed s)`,
}),
// 为模块提供中间缓存步骤,对二次编译有很大的提升
new HardSourceWebpackPlugin(),
new BundleAnalyzerPlugin()
),
addWebpackAlias({
'@': path.resolve(__dirname, 'src'),
}),
addCustomize(),
(config, env) => {
if (env === 'production') {
config.devtool = false
}
config = rewireReactHotLoader(config, env)
return config
},
)
5.npm run build就成功啦!!!
最终实现了之前打包1小时,现在10分钟。而且包的体积也从380M到67M
总结
实现DllPlugin的过程是艰辛的,因为参考别人的文章时,没有和自己的项目是一模一样的配置,要先理解大致原理,再去结合自己的项目进行修改、配置。要坚信别人可以做到的,你也可以!!!