当vue遇到老的项目启动和打包速度慢怎么办? webpack-低版版本-编译启动速度和打包速度优化方案

本文主要是介绍当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的基础项目

替换全局代码

替换业务代码

  1. 将src文件夹全部替换
  2. 将public 文件夹替换

package.json 修改

  1. 将vue-cli3.6老项目中的全局配置项和vue-cli5新壳子中的配置项目对比,把vue-cli5中出现过的依赖项直接删除,使用vue-cli5默认的
  2. 将vue-cli3.6老项目中出现的项目依赖项移捞出来放到新壳子中去
  3. 删除代码中没有引用的依赖项
  • 由于老项目中的依赖项可能是其他项目搬过来的,所以必定会出现没有用到的依赖项
  • 在代码中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-低版版本-编译启动速度和打包速度优化方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/754346

相关文章

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

SQLite3 在嵌入式C环境中存储音频/视频文件的最优方案

《SQLite3在嵌入式C环境中存储音频/视频文件的最优方案》本文探讨了SQLite3在嵌入式C环境中存储音视频文件的优化方案,推荐采用文件路径存储结合元数据管理,兼顾效率与资源限制,小文件可使用B... 目录SQLite3 在嵌入式C环境中存储音频/视频文件的专业方案一、存储策略选择1. 直接存储 vs

如何在Spring Boot项目中集成MQTT协议

《如何在SpringBoot项目中集成MQTT协议》本文介绍在SpringBoot中集成MQTT的步骤,包括安装Broker、添加EclipsePaho依赖、配置连接参数、实现消息发布订阅、测试接口... 目录1. 准备工作2. 引入依赖3. 配置MQTT连接4. 创建MQTT配置类5. 实现消息发布与订阅

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

前端如何通过nginx访问本地端口

《前端如何通过nginx访问本地端口》:本文主要介绍前端如何通过nginx访问本地端口的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、nginx安装1、下载(1)下载地址(2)系统选择(3)版本选择2、安装部署(1)解压(2)配置文件修改(3)启动(4)

怎么用idea创建一个SpringBoot项目

《怎么用idea创建一个SpringBoot项目》本文介绍了在IDEA中创建SpringBoot项目的步骤,包括环境准备(JDK1.8+、Maven3.2.5+)、使用SpringInitializr... 目录如何在idea中创建一个SpringBoot项目环境准备1.1打开IDEA,点击New新建一个项

HTML中meta标签的常见使用案例(示例详解)

《HTML中meta标签的常见使用案例(示例详解)》HTMLmeta标签用于提供文档元数据,涵盖字符编码、SEO优化、社交媒体集成、移动设备适配、浏览器控制及安全隐私设置,优化页面显示与搜索引擎索引... 目录html中meta标签的常见使用案例一、基础功能二、搜索引擎优化(seo)三、社交媒体集成四、移动

HTML input 标签示例详解

《HTMLinput标签示例详解》input标签主要用于接收用户的输入,随type属性值的不同,变换其具体功能,本文通过实例图文并茂的形式给大家介绍HTMLinput标签,感兴趣的朋友一... 目录通用属性输入框单行文本输入框 text密码输入框 password数字输入框 number电子邮件输入编程框