本文主要是介绍当vue遇到老的项目启动和打包速度慢怎么办? webpack-低版版本-编译启动速度和打包速度优化方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
webpack优化背景
- 前段时间我很幸运地接了一个古老的项目,webpack的版本还停留在4.0的版本,本来没想优化的,但是由于每次启动需要6分钟,保存一下页面热启动也需要2分钟,直接把我整崩溃了,这种心情 一言难尽
- 我开发了一周,每天大概浪费2个小时在等待页面启动和编译上,我真的崩溃了
- 于是我决定使用一天的时间优化webpack配置
- 开始了我的优化之旅
优化成果
- 第一次启动 10s
- 热启动 5s 之内
- 第一次打包 20s之内
- 再次打包 10s之内
优化前提
- 仔细阅读webpack的开发文档
- 注意 很多api是只有webpack5的版本才支持的
优化的最大痛点
- webpack 的版本过低(4.0),很多优秀的api和属性甚至是打包编译思想都没有用到
突如其来的一道灵光,换壳,将vue-cli3.6的版本直接升级到vue-cli5.0
- 业务代码不变,改变项目的整体壳子和vue-clid等配置
可行性分析
业务代码影响分析
- 都是vue项目并且vue的版本都是用的是vue2.6 的版本,所以业务代码是没有什么风险点的,唯一要改的是配置
配置代码分析
- 由于vue-cli3.6升级到vue-cli5.0之后,webpack 的优秀属性就都可以使用了
升级vue-cli步骤
使用vue-cli5脚手架搭建一个空壳项目
- 使用vue-cli5 搭建一个vue的基础项目
替换全局代码
替换业务代码
- 将src文件夹全部替换
- 将public 文件夹替换
package.json 修改
- 将vue-cli3.6老项目中的全局配置项和vue-cli5新壳子中的配置项目对比,把vue-cli5中出现过的依赖项直接删除,使用vue-cli5默认的
- 将vue-cli3.6老项目中出现的项目依赖项移捞出来放到新壳子中去
- 删除代码中没有引用的依赖项
- 由于老项目中的依赖项可能是其他项目搬过来的,所以必定会出现没有用到的依赖项
- 在代码中src文件夹下全局搜索,判断插件是否有使用到,没有用到的直接删除
.babelrc 文件修改
- 由于vue-cli3.6和vue-cli5.0的babel处理方式不太一样,所以需要修改
- 老的代码就不放了
vue-cli5的.babelrc代码
{"presets": ["@vue/cli-plugin-babel/preset"],"plugins": ["equire",["import",{"libraryName": "view-design","libraryDirectory": "src/components"}]]
}
修改vue.config.js
- 壳子已经替换完毕,现在开始webpack 的配置
配置 vue.config.js
定义环境变量
const isDev = process.env.NODE_ENV == 'development'
transpileDependencies 关闭
- 关闭之后,能够提编译速度
transpileDependencies: isDev ? false : true,//转译依赖
- 默认情况下 babel-loader 会忽略所有 node_modules 中的文件。你可以启用本选项,以避免构建后的代码中出现未转译的第三方依赖。
开启 terser-webpack-plugin 代码压缩
开启参数
- minimize 开启压缩
TerserPlugin 配置
- parallel 最大并行进程
- cache 是否开启缓存
- sourceMap 是否开启 sourceMap
- terserOptions
-
- compress 压缩选项
-
- compress.drop_console 是否删除console 生产环境删除,开发环境保留
-
- compress.drop_debugger 是否删除debugger 生产环境删除,开发环境保留
-
- output.comments 是否删除comments 生产环境删除,,开发环境保留
let minimizeConfig = { minimize: true,minimizer: [new TerserPlugin({parallel: 4,cache: true,sourceMap: false,terserOptions: {compress: {drop_console: isDev ? false : true,drop_debugger: isDev ? false : true,},output: {comments: false,},},})],concatenateModules: false, // 公共代码整合,生产环境下被启用
}
开启摇树优化
- 将项目中重复的代码合并,删除多余的代码
config.optimization = {// runtimeChunk: true,usedExports: isDev ? false : true,//开启要数优化 tree shakingsideEffects: false,splitChunks: {chunks: 'all',minSize: 20000,minRemainingSize: 0,minChunks: 1,maxAsyncRequests: 30,maxInitialRequests: 30,enforceSizeThreshold: 50000,cacheGroups: {//公用模块抽离common: {chunks: 'initial',minSize: 0, //大于0个字节minChunks: 2, //抽离公共代码时,这个代码块最小被引用的次数},//第三方库抽离vendor: {priority: 1, //权重test: /node_modules/,chunks: 'initial',minSize: 0, //大于0个字节minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数},default: {minChunks: 2,priority: -20,reuseExistingChunk: true}}}
}
watchOptions 忽略node_modules
config.watchOptions = {ignored: /node_modules/,//忽略node_modules包文件aggregateTimeout: 600,//多次修改批量更新poll: 1000//每秒检查一次变动
}
devtool配置
config.devtool = isDev ? 'source-map' : false//错误信息
开发环境
- 启用sourcemap
- devtool 设置值为 source-map
生产环境
- 关闭sourcemap
- 注意设置为false,否则无法彻底关闭sourcemap
开启cache缓存
- 缓存是前端优化的常规手段,webpack 中同样可以
- 缓存分内存和磁盘文件缓存,此处是使用文件磁盘缓存
-
- type 使用 filesystem
-
- allowCollectingMemory 收集在反序列化期间分配的未使用的内存,仅当 cache.type 设置为 ‘filesystem’ 时生效。这需要将数据复制到更小的缓冲区中,并有性能成本。
- cacheDirectory 文件缓存的目录
-
- 指定为档期目录下的 .temp_cache 文件夹下
-
- 它下面分development 和 production 文件夹
config.cache = {type: 'filesystem',allowCollectingMemory: true,cacheDirectory: path.resolve(__dirname, '.temp_cache'),
}
- 注意 建议定期删除 temp_cache 文件夹,以免占用过多磁盘空间
小伙伴担忧
是否会卡顿
- 缓存太多是否会导致,内存爆掉甚至于卡顿
- 我在此明确地告诉你,不会,原因很简单
-
- 这个缓存是基于磁盘的,不是内存
-
- 建议一周删除一次缓存文件
output 配置
config.output = {clean: true, // 在生成文件之前清空 output 目录compareBeforeEmit: false,// 当在磁盘中已经存在有相同内容的文件时,webpack 将不会写入输出文件。filename: '[name].[contenthash].bundle.js',// wenpack打包后的文件名chunkFilename: 'js/[name].[contenthash].bundle.js',// 异步加载的模块path: path.join(__dirname, 'testProject'),publicPath: isDev ? '/' : '/testProject/',}
备注 chainWebpack 和 configureWebpack 区别
官方介绍
- chainWebpack 是一个函数,会接收一个基于 webpack-chain 的 ChainableConfig 实例。允许对内部的 webpack 配置进行更细粒度的修改。
- configureWebpack 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中
通俗区别
- chainWebpack用于修改,会和默认配置项合并,追加某个属性值
- configureWebpack用于合并,向原有配置项追加配置,直接添加整个配置项
- 所以,只修改某个属性使用 chainWebpack,添加配置项使用configureWebpack
webpack辅助工具
- 可帮助我我们更好的优化代码
webpack-bundle-analyzer
- 代码分析工具,可分析打包之后的文件
speed-measure-webpack-plugin
- 打包的速度分析,和时间分析插件
config.plugins.push(new BundleAnalyzerPlugin())
config.plugins.push(new WebpackBar({ name: 'PC', color: '#07c160' }))
const {defineConfig} = require('@vue/cli-service')
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path')
const webpack = require('webpack');
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin
const resolve = dir => path.join(__dirname, dir)
const packageName = require('./package.json').name
const isDev = process.env.NODE_ENV == 'development'
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const WebpackBar = require('webpackbar');module.exports = defineConfig({// productionSourceMap: false, // 关闭生产环境的 source mappublicPath: isDev ? '/' : '/testProject/',outputDir: 'testProject',lintOnSave: false,transpileDependencies: isDev ? false : true,//转译依赖chainWebpack: config => {config.plugin('speed-measure-webpack-plugin').use(SpeedMeasurePlugin).end();if (!isDev) {config.plugins.delete('prefetch');// 移除 preload 插件config.plugins.delete('preload');}config.plugin('speed-measure-webpack-plugin').use(SpeedMeasurePlugin).end();},configureWebpack: config => {if (!isDev) {config.entry = "./src/main.js" config.output = {clean: true, // 在生成文件之前清空 output 目录compareBeforeEmit: false,// 当在磁盘中已经存在有相同内容的文件时,webpack 将不会写入输出文件。filename: '[name].[contenthash].bundle.js',// wenpack打包后的文件名chunkFilename: 'js/[name].[contenthash].bundle.js',// 异步加载的模块path: path.join(__dirname, 'testProject'),publicPath: isDev ? '/' : '/testProject/',// qiankun接入配置library: `${packageName}-[name]`,libraryTarget: 'umd', // 把微应用打包成 umd 库格式chunkLoadingGlobal: `webpackJsonp_${packageName}`,//webpack5 output.jsonpFunction 更名为 output.chunkLoadingGlobal}config.plugins.push(new BundleAnalyzerPlugin())config.plugins.push(new WebpackBar({ name: 'PC', color: '#07c160' }))}config.resolve.alias =// 设置路径别名,设置后需保持jsconfig.json内一致{'@': resolve('src'),'_c': resolve('src/components')}let minimizeConfig = { minimize: true,minimizer: [new TerserPlugin({parallel: 4,cache: true,sourceMap: false,terserOptions: {compress: {drop_console: isDev ? false : true,drop_debugger: isDev ? false : true,},output: {comments: false,},},})],concatenateModules: false, // 公共代码整合,生产环境下被启用}config.optimization = {// runtimeChunk: true,usedExports: isDev ? false : true,//开启要数优化 tree shakingsideEffects: false,splitChunks: {chunks: 'all',minSize: 20000,minRemainingSize: 0,minChunks: 1,maxAsyncRequests: 30,maxInitialRequests: 30,enforceSizeThreshold: 50000,cacheGroups: {//公用模块抽离common: {chunks: 'initial',minSize: 0, //大于0个字节minChunks: 2, //抽离公共代码时,这个代码块最小被引用的次数},//第三方库抽离vendor: {priority: 1, //权重test: /node_modules/,chunks: 'initial',minSize: 0, //大于0个字节minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数},default: {minChunks: 2,priority: -20,reuseExistingChunk: true}}}}if (!isDev) {config.optimization = Object.assign(config.optimization, minimizeConfig)console.log(config.module)}config.watchOptions = {ignored: /node_modules/,//忽略node_modules包文件aggregateTimeout: 600,//多次修改批量更新poll: 1000//每秒检查一次变动}config.devtool = isDev ? 'source-map' : false//错误信息config.cache = {type: 'filesystem',allowCollectingMemory: true,cacheDirectory: path.resolve(__dirname, '.temp_cache'),}}
})
优化成果
初次 npm run dev 运行速度
- 所有的缓存文件都清除,运行时间大概在36秒
基于缓存 npm run dev 运行速度
- 只需要 4.7秒
初次 npm run build 打包速度
基于缓存 npm run build 打包速度
- 9.64秒
项目体量
src文件数量和大小
- 770个文件,10MB
项目依赖大小
- 781MB
打包后文件 大小
- 仅有 9.8MB
个人总结
- webpack 本质上也是js,我们不会配置,可能只是不太熟悉,不要有恐惧心理
- 先看仔细阅读文档,重点看他的优化方案,整合下即可结合到项目中
致谢
- 感谢webpack官方文档提供的文档说明
- 感谢我的项目组给了我挑战自己的机会
- 感谢我的导师给予我的帮助
- 感谢您百忙之中抽时间阅读我写的博客,谢谢您的肯定,也希望对您能有所帮助
- 如果您有更好的见解请在评论区留言或者私聊我,期待与您的交流
这篇关于当vue遇到老的项目启动和打包速度慢怎么办? webpack-低版版本-编译启动速度和打包速度优化方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!