vue2旧项目 极速打包实践

news2024/11/13 19:18:09

背景

公司项目的体量较大,每次serve需要1分钟左右,build需要3分多钟,这是在电脑资源空闲时的速度,如果浏览器开了10几个标签啥的,更慢了。每次改点东西打包发测试环境都很难受。


项目技术栈

// package.json

{
	"dependencies": {
		"vue": "^2.6.10",
		"vuex": "^3.1.2",
		"vue-router": "^3.1.3",
		"core-js": "^3.4.3",
	},
	"devDependencies": {
	    "@vue/cli-plugin-babel": "^4.1.0",
	    "@vue/cli-plugin-router": "^4.1.0",
	    "@vue/cli-plugin-vuex": "^4.1.0",
	    "@vue/cli-service": "^4.1.0",
	    "vue-template-compiler": "^2.6.10"
  }
}

项目性能分析

打包耗时分析

// vue.config.js
module.exports = {
	chainWebpack: (config) => {
		 // 分析 webpack 的总打包耗时以及每个 plugin 和 loader 的打包耗时
	      config.plugin('speed-measure')
	        .use(require('speed-measure-webpack-plugin'))
	}
}

打包后文件体积分析

// vue.config.js
module.exports = {
	chainWebpack: (config) => {
		  // 体积分析
	      config.plugin('bundle-analyzer')
	        .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [{analyzerPort: 8880}])
	}
}

优化方向


查看项目的webpack配置

因为vue脚手架隐藏了默认的webpack配置文件,让你在vue.config.js中通过打补丁的方式修改配置。
优化的前提,必须要知道原来的配置和你修改后的配置是否生效,这就必须要把webpack配置文件的导出来


idea终端或者CMD终端中运行命令,会生成最终的webpack配置文件
开发环境:npx vue-cli-service inspect --mode development >> webpack.config.development.js
生产环境:npx vue-cli-service inspect --mode production >> webpack.config.production.js

在产生的 js 文件开头,添加:module.exports =,然后格式化即可查看。


DLL缓存

// vue.config.js

// dll缓存的依赖包
const DllConfig = require('./webpack.dll.config')

// 动态插入资源到html中
const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");

module.exports = {
	configureWebpack: (config) => {
        // 缓存依赖包
	    config.plugins = config.plugins.concat([
	      ...Object.keys(DllConfig.entry).map(key => (
	        new webpack.DllReferencePlugin({
	          context: process.cwd(),
	          manifest: require(`./public/vendor/${key}-manifest.json`)
	        })
	      )),
	
	      // 将 dll 注入到 生成的 html 模板中
	      new AddAssetHtmlPlugin({
	        // dll文件位置
	        filepath: require("path").resolve("./public/vendor/*.js"),
	        // dll 引用路径
	        publicPath: "./vendor",
	        // dll最终输出的目录
	        outputPath: "./vendor",
	      }),
	    ]);
	}
}
// 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: {
    // 需要提取的库文件

    vueMain: ['vue', 'vue-router', 'vuex'],
    vuePlugin: ['vue-print-nb', 'vuedraggable', 'vue-i18n'],
    element: ['element-ui'],
    dll: [
      "file-saver",
      "html2canvas",
      "jszip",
      "qrcodejs2",
      "socket.io-client",
      "viewerjs",
      "cropperjs",
      "laravel-echo",
      "nprogress",
      "tinymce"
    ],
  },

  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: JSON.stringify("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": {
    	"dll": "webpack --progress --config ./webpack.dll.config.js",
	 },
}

DLL是把一些静态资源包提前打包好,放到public/vendor目录下,打包时跳过它们,直接在index.html中的script标签引用


缺点
有了splitChunks后,这个就不怎么好用了,DLL会把资源全量打包然后插入html,不能按需加载,splitChunks可以把重复使用的资源打包到一个chunk里,然后引用它的地址,做到按需导入和避免资源重复打包

最后
打包速度并没有提升多少,所以PASS


splitChunks分包

// vue.config.js

module.exports = {
	chainWebpack: (config) => {
		// 拆包
	    config.optimization.splitChunks({
	       chunks: "all",
	       minSize: 20000, // 允许新拆出 chunk 的最小体积,也是异步 chunk 公共模块的强制拆分体积
	       cacheGroups: {
	         libs: { // 第三方库
	           name: "chunk-libs",
	           test: /[\\/]node_modules[\\/]/,
	           priority: 10,
	           chunks: "initial", // 只打包初始时依赖的第三方
	         },
	         elementUI: { // elementUI 单独拆包
	           name: "chunk-elementUI",
	           test: /[\\/]node_modules[\\/]element-ui[\\/]/,
	           priority: 20 // 权重要大于 libs
	         },
		     components: { // 组件包
	            name: `chunk-components`,
	            test: /[\\/]components[\\/]/,
	            minChunks: 2,
	            priority: 1,
	            reuseExistingChunk: true
	          },
	         commons: { // 公共模块包
	           name: `chunk-commons`,
	           minChunks: 2,
	           priority: 0,
	           reuseExistingChunk: true
	         },
	       }
	     })
	}
}

拆包的目的是,分解体积很大的包,复用重复打包的文件,拆分不常改变的模块,更好的复用webpack的文件缓存,避免文件hash受到影响而使缓存失效


缺点
每次打包都要分析文件依赖,执行拆包的操作
自己一顿操作分包,还没webpack默认配置分的均匀,下面是我的分包和webpack默认的分包对比


在这里插入图片描述

在这里插入图片描述

最后
分包这个还是挑项目的,如果你的项目有什么特殊情况默认配置不能满足的话,或许有点用,但是大部分都不需要另外去配的,webpack团队想的肯定比我们要充分。
这个对打包速度也没有提升,PASS


图片压缩

// vue.config.js
module.exports = {
	chainWebpack: (config) => {
	    // 图片压缩
	    config.module.rule('images')
	           .use('image-webpack-loader')
	           .loader('image-webpack-loader')
	           .options({
	              disable: process.env.NODE_ENV === 'development', // 开发模式下调试速度更快
	            })
	           .end()
	}

缺点

  1. 因为另外引入loader处理,所以打包起来会更慢
  2. 后面开发基本都会把图片放包OSS上,或者用iconfont,不会在本地加图片,所以压缩意义不大

最后
对打包速度没提升,PASS


CDN加载文件

// vue.config.js

const cdn = {
  // 开发环境
  dev: {
    css: [],
    js: [],
  },
  // 生产环境
  build: {
    css: [
      // jsmind
      "./themes/jsmind.css"
      
      // element
      // "http://xx.css"
    ],
    js: [
      // 采用oss
      // element-ui@2.13.2.js
      // "http://xx.js",
      
      // echarts@4.6.0.min.js
      "//xx.js",

      // axios@0.19.0.min.js
      "//xx.js",
     
      // aliyun-oss-sdk-6.3.1.min.js
      "//xx.js",

      // xlsx
      "//xx.js",
      
      // xlsx-style@0.8.13.core.min.js
      "//xx.js",
      
      // jsmind
      "//xx.js",

      // gojs
      "//xx.js",
    ],
  },
};

module.exports = {
	chainWebpack: (config) => {
		 // 注入cdn
	    config.plugin("html").tap((args) => {
	      args[0].cdn = cdn.build;
	      return args;
	    });
	},

	configureWebpack: (config) => {
		    // cdn预加载使用
		    // 告诉 webpack 这些依赖是外部环境提供的,在打包时可以忽略它们,就不会再打到 chunk-vendors.js 中
		    // e.g. <script src="http://lib.baomitu.com/echarts/5.3.2/echarts.min.js"></script>
			 config.externals = {
		      "echarts": "echarts",
		      "xlsx-style": "XLSX",
		      "ali-oss": "OSS",
		      "jsmind": "jsMind",
		      "gojs": "go",
		      "axios": "axios",
		      "xlsx": "ODS"
		    };
	}
}

这里是原来就有的,我不知道为什么一些上CDN,一些又不上,免得出问题背锅,还是不动的好

最后
PASS


happypack多线程处理

// vue.config.js

const HappyPack = require("happypack");
const os = require("os");
const happyThreadPool = HappyPack.ThreadPool({size: os.cpus().length}); // 开辟一个线程池,拿到系统CPU的核数,happypack 将利用所有线程编译

module.exports = {
	chainWebpack: (config) => {
	    // todo plugin的名字要唯一,不然后面同名的会覆盖
	    config.plugin('happypack-babel').use(HappyPack, [{
	      id: 'babel',
	      loaders: ['babel-loader'],
	      threadPool: happyThreadPool
	    }])

	    config.plugin('happypack-styles').use(HappyPack, [{
	      id: 'styles',
	      loaders: [{
	        loader: 'style-resources-loader',
	        options: {
	          patterns: [
	            './public/themes/default/commom/commonFunc.less',
	            './public/themes/global.less'
	          ]
	        }
	      }],
	      threadPool: happyThreadPool
	    }])

	 	const jsRule = config.module.rule('js')
	   	jsRule.uses.delete('thread-loader')
	    jsRule.uses.delete('babel-loader')
	    jsRule.use('happypack').loader('happypack/loader?id=babel')

	 	const oneOfsMap = config.module.rule("less").oneOfs.store;
	   	oneOfsMap.forEach(item => {
	      item.uses.delete('style-resources-loader')
	      item.use("happypack").loader('happypack/loader?id=styles')
    	})
	}
}


最后
重复打包,发现没什么效果,不知道是不是我的姿势不对,PASS


thread-loader 多线程处理

// vue.config.js

module.exports = {
	chainWebpack: (config) => {
		let originUse = config.module.rule('images').toConfig().use
	    let newLoader = { loader: 'thread-loader' }
	    originUse.splice(0, 0, newLoader)
	    config.module.rule('images').uses.clear()
	    config.module.rule('images').merge({ use: originUse })
	}
}

package.json.lock,vue/cli已经内置了thread-loader,
webpack.config.production.js,thread-loader默认被用来处理babel-loader


最后
webpack官网上的【thread-loader】支持的node版本是 >= 16.10.0,但是我们项目统一的node版本是 14.16.0,虽然用起来没报错,但好像也没什么效果,PASS


Gzip压缩

// vue.config.js

module.exports = {
	 configureWebpack: (config) => {
   		// gzip打包压缩
     	// todo nginx需要开启 gzip_static,才能返回前端打包好的.gz文件,不然默认在服务器压缩
     	config.plugins.push(
	       new CompressionWebpackPlugin({
	          filename: "[path][base].gz", // 压缩后的文件名
	          algorithm: "gzip",
	          test: /\.(js|css)(\?.*)?$/i, // 需要压缩的文件正则
	          threshold: 10240,            // 对10K以上的数据进行压缩
	          minRatio: 0.8,               // 只有压缩率小于这个值的资源才会被处理
	
	          // 是否删除原文件(.js),只保留压缩文件(.gz)
	          // 删除原文件后,打包上线 Uncaught SyntaxError: Unexpected token < 【参考 https://stackoverflow.com/questions/54082652/webpack-gzip-bundle-uncaught-syntaxerror-unexpected-token】
	          deleteOriginalAssets: false,
	        })
	      );
	}
}

打包时生成.gz结尾的文件,nginx服务器开启gzip压缩后,如果生产包上有同名的.gz结尾的文件,则直接返回前端压缩好的文件,不然用服务器资源来进行动态压缩生成.gz文件后返回给浏览器。
目的:减少用户请求资源的时间


最后
服务器只配置了gzip: on,没有配置gzip_static,所以服务器总是动态压缩,没有用前端打包好的文件,再者说前端压缩还要拖慢我打包的速度,PASS


hard-source-webpack-plugin

// vue.config.js

module.exports = {
	chainWebpack: (config) => {
	 	// 重复构建,使用缓存,加快构建
	    config.plugin('hard-source')
	           .use(require('hard-source-webpack-plugin'));
	}
}

这个插件第一次build时会在node_modules/.cache下面建缓存,后面重复打包可以读缓存,减少构建时间

最后
重复构建能从3分多到1分多,提升还是挺大的,但是改点东西再打包,又会久点,因为要重新建缓存,但都比原来快,【


升级脚手架

当大部分配置都不能有效提升时,直接升脚手架吧,版本的提升带来的效益是巨大的,毕竟人家是专业的

【vue/cli4 升级 vue/cli5】

cli5内置了webpack5,带来了文件【持久缓存】,解析文件时生成缓存,后续没有改变的文件直接读取快照,不重复执行编译,打包速度从3分多到20多秒,提升巨大


最后
面向未来才是关键,新东西就是好用,旧项目也能焕发第二春

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/659563.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

百万粉丝都在看的Python上手教程----滚雪球学Python

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 今天为大家带来一本书&#xff1a;《滚雪球学Python》 一起来看看吧~ 《滚雪球学Python》之所以这样命名本书&#xff0c;是希望大家用滚雪球的思维学习编程语言&#xff0c;“滚”的第一遍&#xff0c;从全局掌握Python技…

大话Stable-Diffusion-Webui-动手开发一个简单的stable-diffusion-webui(三)

文章目录 原理文生图API组件的输入TypeScript响应式数据文生图API调用Axios安装使用配置代理文生图API调用调用结果处理图片渲染安装swiper代码仓库原理 上一篇内容中,我们已经将文生图功能的整体UI界面设计好了,这一篇内容将通过调用sd的API,使得我们设计的UI与sd进行联动…

多域名实现单点登录详解

Hi I’m Shendi 多域名实现单点登录详解 简介 在很久以前给自己的网站制作了登录系统&#xff0c;但因为个人备案等原因没有需要用到登录的地方&#xff0c;于是就没有特意去完善这部分功能&#xff0c;仅仅是将用户部分抽取出来作为一个微服务 最近编写一个转换工具&#xf…

unittest教程__认识unittest(1)

unittest是python内置的单元测试框架&#xff0c;具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。 使用unittest前需要了解该框架的五个概念: 即test case&#xff0c;test suite&#xff0c;test loader&#xff0c;test runner&#xff0c;test fixture。 …

【多线程】锁策略、CAS、Synchronized

目录 常见的锁策略 乐观锁 vs 悲观锁 悲观锁: 乐观锁&#xff1a; 读写锁 重量级锁 vs 轻量级锁 自旋锁&#xff08;Spin Lock&#xff09; 公平锁 vs 非公平锁 可重入锁 vs 不可重入锁 CAS 什么是 CAS CAS 是怎么实现的 CAS 有哪些应用 1) 实现原子类 2) 实现自…

从0到1使用NodeJS编写后端接口的实战案例(仅供参考)

目录 一、项目简介 1、使用技术 2、实现的主要功能 3、项目结构 二、开发环境准备 1、安装node.js 2、安装 MYSQL 数据库 3、安装 node.js 的 mysql 驱动 4、安装 Express 框架 5、Node 格式化时间模块Silly-datetime 6、安装 nodemon 三、后端代码 1、入口文件 —…

帝国cms城市分站系统开发:首页友情链接和分站友情链接分开调用

第一步&#xff1a;phome_enewslink 增加myarea字段 字段类型&#xff1a;int&#xff0c;长度6&#xff0c;非null字段&#xff0c;默认值0 或者在帝国cms后台执行 sql语句&#xff1a; alter table [!db.pre!]enewslink add myarea int(6) not null; 第二步&#xff1a;修…

二次封装NavLink(React实现)

实现思路&#xff1a; 1、定义一个普通组件 2、普通组件内使用NavLink 3、传递参数给定义的普通组件并实现效果 代码实现&#xff1a; App.jsx import React, { Component } from "react"; import About from "../src/Pages/About"; import Home fro…

Vivado全版本下载分享

Vivado是由Xilinx公司开发的一款用于FPGA设计和开发的综合设计环境。它包括了高层次综合&#xff08;HLS&#xff09;、逻辑设计、约束管理、IP核管理、仿真、综合、实现和调试等功能&#xff0c;支持面向最新FPGA器件的设计。 这里分享一下Vivado的电脑安装配置推荐&#xff…

性能测试调优模型、思想和技术

最近阅读《软件性能测试、分析与调优实践之路》一书&#xff0c;个人认为性能调优章节为整部书的精华&#xff0c;该章节包括了性能测试调优模型、调优思想和调优技术。下面是摘抄整理自书中内容&#xff1a; 调优模型 下图为互联网中常见的用户请求的分层转发和处理的过程&a…

Echarts前端可视化库使用教程

Echarts介绍 ECharts是一个使用 JavaScript 实现的开源可视化库&#xff0c;可以流畅的运行在 PC 和移动设备上&#xff0c;兼容当前绝大部分浏览器&#xff08;IE8/9/10/11&#xff0c;Chrome&#xff0c;Firefox&#xff0c;Safari等&#xff09;&#xff0c;底层依赖矢量图…

XSS跨站脚本攻击及防护

目录 一、初识XSS跨站脚本 1.1 XSS玫击概述 1.2 XSS漏洞攻击本质 1.3 XSS攻击的危害 1.4 XSS玫击原理 1.5 XSS攻击过程 1.6 XSS攻击特点 1.6.1 间接攻击 1.6.2 可更正性 1.6.3 传播性强 二、XSS攻击与防护 2.1 XSS攻击分类 2.1.1 存储型XSS 2.1.2 反射型XSS 2.1…

OpenCV(图像处理)-基于Python-形态学处理-开运算、闭运算、顶帽、黑帽运算

1. 形态学2. 常用接口2.1 cvtColor()2.2 图像二值化threshod()自适应阈值二值化adaptiveThreshod() 2.3 腐蚀与膨胀erode()getStructuringElement()dilate() 2.4开、闭、梯度、顶帽、黑帽运算morphologyEx() 1. 形态学 OpenCV形态学是一种基于OpenCV库的数字图像处理技术&…

React学习笔记(二)组件详解(上)

一、组件的概念&#xff1a; 当你开始学习 React 的时候&#xff0c;你会了解到 React 组件是 React 应用程序的基本构建块。组件是一个隔离的、可重复使用的代码块&#xff0c;由 HTML 元素、其他组件或自定义的 UI 元素组成&#xff0c;组件也就是react的核心思想&#xff0c…

python Flask web项目uwsgi + nginx部署

1.安装python 略 2.虚拟环境 2.1安装vertualenv pip3 install virtualenv2.2创建虚拟环境 创建保存环境的目录&#xff1a; mkdir venvs创建虚拟环境&#xff1a; [rootroot /]# virtualenv /home/xxx/venvs/flask2 --pythonpython3查看虚拟环境&#xff1a; [rootroot…

破万亿!英伟达的市值

文章目录 破万亿 &#x1f928; 英伟达的市值&#x1f928; 英伟达市值几近破万亿&#x1f916; ChatGPT 伪造 6 个法律案例&#x1f5e1;️ AI 巨头的呼吁&#xff0c;是真担心还是想垄断&#xff1f;&#x1f3e0; 硅谷诈骗犯开启女性监狱新家&#x1f4c8; 美国房价春季反弹…

有趣的图(五)(59)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 咱们今天继续学习图的应用&#xff0c;这些算法都是实际问题抽象出来的。 举个例子吧&#xff01; 下面6个城市&a…

第八章ThreadLocal

文章目录 先看面试题什么是ThreadLocal能干嘛基本使用常用方法 使用ThreadLocal来解决使用sychronized解决ThreadLocal与synchronized的区别 强化理解数据隔离的意义为什么线程执行完要remove 运用场景_事务案例场景构建 引入事务JDBC中关于事务的操作的api常规解决方案常规方案…

javascript程序员简历模板(合集)

javascript程序员简历模板一 个人资料 姓 名&#xff1a; 性 别&#xff1a; 女 年 龄&#xff1a; 21 民 族&#xff1a; 汉族 户籍&#xff1a; 湖北 襄樊 最高学历&#xff1a; 本 科 现所在地&#xff1a; 广东 广州 毕业院校&#xff1a; 南京理工大学 所学专业&#xff1…

忘记 localStorage 吧,indexedDB 才是前端存储新宠!

前言 在项目开发过程中&#xff0c;前端需要存储大量的数据。cookie, localstorage 都有存储长度限制。表格一览 特性cookielocalStoragesessionStorageindexedDB数据生命周期一般由服务器生成&#xff0c;可以设置过期时间&#xff1b;前端采用和js-cookie等组件也可以生成除…