背景
因为公司的前端项目在打包构建方面时间属实长得离谱,冷启动大约5min,本地构建打包也大约5min,到线上腾讯云docker构建打包全过程需要16~20min。因此不仅极大影响开发效率,也大大延迟了给测试交付的时间。
优化思路
- 了解项目当前webpack配置
- 构建相关优化
- 打包体积相关优化
- docker相关优化
量化工具
speed-measure-webpack-plugin
介绍:
speed-measure-webpack-plugin npm
通过smp输出的分析可以清楚的了解到webpack构建过程中,每一阶段的loader以及plugin的工作花费的时间。
使用方式:
# Yarn
yarn add -D speed-measure-webpack-plugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
module.exports = {
chainWebpack: config => {
config
.plugin('speed-measure-webpack-plugin')
.use(SpeedMeasurePlugin)
.end()
}
}
在本项目中使用其他的使用都会报error,但是以上的用法似乎不会区分plugin与loader的使用,甚至没有其他plugin的使用情况信息,迷惑~。
webpack-bundle-analyzer
介绍:
webpack-bundle-analyzer npm 用来分析webapck构建打包后的文件,如分包情况,占用体积等参数的分析。
使用方式:
# Yarn
yarn add -D webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
但是在vue-cli中有report命令可以直接调用,然后去dist打包目录打开report.html。
vue-cli-service build --report
or
vue-cli-service build --report-json
查看vue-cli当前webpack配置
介绍
vue-cli脚手架会有webpack的很多默认行为,因此我们得知道基于vue-cli的项目,当前的webpack都配置了啥,然后才能做针对性的分析与优化。
使用方式:
#根据mode,分别生成开发环境、生产环境的配置
vue inspect --mode production > output.js
#输入命令后,在根目录会生产一个output.js文件
如果vue command not found
的错可以全局安装注册一下vue命令npm install -g vue-cli
构建相关优化
hard-source-webpack-plugin
介绍
hard-source-webpack-plugin npm
在启动项目时会针对项目生成缓存,若是项目无package或其他变化,下次就不用花费时间重新构建,直接复用缓存。
使用方式:
#yarn
yarn add -D hard-source-webpack-plugin
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
module.exports = {
configureWebpack: config => {
config.plugin.push(
// 为模块提供中间缓存,缓存路径是:node_modules/.cache/hard-source
// solve Configuration changes are not being detected
new HardSourceWebpackPlugin({
root: process.cwd(),
directories: [],
environmentHash: {
root: process.cwd(),
directories: [],
files: ['package.json', 'yarn.lock', 'vue.config.js']
}
})
// 配置了files的主要原因是解决配置更新,cache不生效了的问题,配置后有包的变化,plugin会重新构建一部分cache
)
}
}
注意:
Could not freeze : Cannot read property 'hash' of undefined
删除node_modules/.cache后,重新启动项目,产生这个问题的原因可能是异步加载模块时编译产生的错误,可参考:
缩小文件检索解析范围
为避免无用的检索与递归遍历,可以使用alias指定引用时候的模块,noParse,对不依赖本地代码的第三方依赖不进行解析。
// 定义getAliasPath方法,把相对路径转换成绝对路径
const getAliasPath = dir => join(__dirname, dir)
module.exports = {
configureWebpack: config => {
config.module.noParse = /^(vu|vue-router|vuex|vuex-router-sync|lodash|echarts|axios|element-ui)$/
}
chainWebpack: config => {
// 添加别名
config.resolve.alias
.set('@', getAliasPath('src'))
.set('assets', getAliasPath('src/assets'))
.set('utils', getAliasPath('src/utils'))
.set('views', getAliasPath('src/views'))
.set('components', getAliasPath('src/components'))
}
// 生产环境禁用eslint
lintOnSave: !process.env.NODE_ENV !== 'production',
}
减少打包体积
image-webpack-plugin 图片压缩
对图片像素要求没很极致的,这个压缩还是可以使用的,压缩率肉眼看起来感觉是没太大区别。 这里注意一下,我没有对svg进行压缩,原因是压缩的svg,再通过构建时被打包成base64时,生成的base64会有问题,无法访问。
module.exports = {
chainWebpack: config => {
// 对图片进行压缩
config.module
.rule('images')
.test(/\.(png|jpe?g|gif)(\?.*)?$/)
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({ bypassOnDebug: true })
.end()
}
}
UglifyJsPlugin删除console 注释
uglifyJsPlugin 用来对js文件进行压缩,减小js文件的大小。其会拖慢webpack的编译速度,建议开发环境时关闭,生产环境再将其打开。 更建议规范团队成员的代码上去解决。
#yarn
yarn add -D uglifyjs-webpack-plugin
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
configureWebpack: config => {
config.plugin.push(
new UglifyJsPlugin({
uglifyOptions: {
// 删除注释
output: {
comments: false
},
// 删除console debugger 删除警告
compress: {
warnings: false,
drop_console: true, //console
drop_debugger: false,
pure_funcs: ['console.log'] //移除console
}
},
sourceMap: false,
parallel: true //使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
})
)
}
}
DLL动态链接库
这个插件是在一个额外的独立的 webpack 设置中创建一个只有 dll 的 bundle(dll-only-bundle)。 这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。
配置DllPlugin,可以分为下面几个步骤:
- 新建webpack.dll.config.js文件(其他命名都可以),配置需要拆分的插件;
- 在package.json文件中新建一条命令来专门打包,
"build:dll":"webpack --config webpack.dll.config.js"
; 运行该命令; - 在vue.config.js 文件中配置
DllReferencePlugin
,主要把dll引用到需要预编译的依赖; - 在index.html手动引入拆分的bundle包(放到cdn的话会更好)
安装:
#yarn
yarn add webpack-cli@^3.2.3 add-asset-html-webpack-plugin@^3.1.3 clean-webpack-plugin@^1.0.1 --dev
// webpack.dll.config.js
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path')
const webpack = require('webpack')
const CleanWebpackPlugin = require('clean-webpack-plugin')
// dll文件存放的目录
const dllPath = 'public/vendor'
module.exports = {
entry: {
// 需要提取的库文件
vendor: ['vue', 'vue-router', 'vuex'],
utils: ['axios', 'lodash']
},
output: {
path: path.join(__dirname, dllPath),
filename: '[name].dll.js',
// vendor.dll.js中暴露出的全局变量名
// 保持与 webpack.DllPlugin 中名称一致
library: '[name]_[hash]'
},
plugins: [
// 清除之前的dll文件
new CleanWebpackPlugin(['*.*'], {
root: path.join(__dirname, dllPath)
}),
// manifest.json 描述动态链接库包含了哪些内容
new webpack.DllPlugin({
path: path.join(__dirname, dllPath, '[name]-manifest.json'),
// 保持与 output.library 中名称一致
name: '[name]_[hash]',
context: process.cwd()
})
]
12
在vue.config.js plugin
中使用
config.plugin.push(
new DllReferencePlugin({
context: process.cwd(),
manifest: require('./public/vendor/vendor-manifest.json')
}),
new DllReferencePlugin({
context: process.cwd(),
manifest: require('./public/vendor/utils-manifest.json')
}),
// 将 dll 注入到 生成的 html 模板中
new AddAssetHtmlPlugin({
// dll文件位置
filepath: getPath('./public/vendor/*.js'),
// dll 引用路径
publicPath: './vendor',
// dll最终输出的目录
outputPath: './vendor'
})
)
splitChunks 分割代码
split-chunks-plugin webpack
- chunks: 表示哪些代码需要优化,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为async
- minSize: 表示在压缩前的最小模块大小,默认为30000
- minChunks: 表示被引用次数,默认为1
- maxAsyncRequests: 按需加载时候最大的并行请求数,默认为5
- maxInitialRequests: 一个入口最大的并行请求数,默认为3
- automaticNameDelimiter: 命名连接符
- name: 拆分出来块的名字,默认由块名和hash值自动生成
- cacheGroups: 缓存组。缓存组的属性除上面所有属性外,还有test, priority, reuseExistingChunk
- test: 用于控制哪些模块被这个缓存组匹配到
- priority: 缓存组打包的先后优先级
- reuseExistingChunk: 如果当前代码块包含的模块已经有了,就不在产生一个新的代码块
config.optimization = {
runtimeChunk: 'single',
splitChunks: {
chunks: 'all', // 表示哪些代码需要优化,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为async
maxInitialRequests: Infinity, // 按需加载时候最大的并行请求数,默认为5
minSize: 30000, // 依赖包超过300000bit将被单独打包
// 缓存组
// priority: 缓存组打包的先后优先级
// minChunks: 表示被引用次数,默认为1
cacheGroups: {
//公共模块
commons: {
name: 'chunk-commons',
test: resolve('src'), // can customize your rules
minSize: 100, //大小超过100个字节
minChunks: 3, // minimum common number
priority: 5,
reuseExistingChunk: true
},
// 第三方库
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial', // only package third parties that are initially dependent
reuseExistingChunk: true,
enforce: true
},
echarts: {
name: 'chunk-echarts',
test: /[\\/]node_modules[\\/]echarts[\\/]/,
chunks: 'all',
priority: 12,
reuseExistingChunk: true,
enforce: true
}
}
}
}
总结
以上是我在公司项目做的一些小优化,对其他项目不一定适用,甚至使用上也不是最优,但目前对本项目很大程度上还是有帮助的。更多webpack优化的思路,我附上个思维导图吧。(仅供学习,记录?)
其他
Vue CLI
vue-cli中的configureWebpack设置
如何使用 docker 部署前端项目
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!