Vue项目构建优化

本文作者为 360 奇舞团前端开发工程师 宁航

在开发大型前端项目时,往往是一个需求对应一个分支,当完成需求后,就需要将代码打包、部署。代码通常需要部署到多个环境中,这些环境包括:日常环境、测试环境、回归环境和生产环境。回归环境用于在发布前进行测试,生产环境是用户访问的版本。随着时间的推移,项目中会不断引入许多新的依赖(如第三方库、插件等)和图片资源,代码数量也会逐渐增多,从而导致构建项目更加耗时,这也意味着部署项目需要消耗更长的时间。

我负责的项目构建需要 66 秒,有时将代码部署到日常环境后,还需要临时修改重新部署;随后再依次部署到测试环境、回归环境,验证无误后,才能部署到线上环境。如果碰到要紧急上线的任务,这一过程无疑是十分费时的。因此,我决定针对项目构建时间过长的问题进行优化,以此来提高工作效率。本文将对如何优化项目构建速度进行详细介绍,具体过程如下:

一、前期准备

当前的构建工具有很多种,例如Rollup、Webpack、Vite等,在进行优化工作前,首先要明确项目使用的是哪个构建工具,我的项目是使用Vue CLI 3创建的,Vue CLI 在创建项目时会默认使用 Webpack 来构建项目,但在 Vue CLI中,默认情况下是不直接暴露 webpack 配置的,只能通过 vue.config.js 文件来修改配置。

其次,由于webpack在不断更新,新版本会增加许多优化策略,因此,还要明确项目使用的webpack版本,再基于这个版本,采用更有针对性的优化方法。经查询,发现Vue CLI 3对应webpack4,后续的优化方法将围绕该版本展开。

最后,我们还需要对构建过程进行详细分析,以便制定合理的优化策略。当我们运行如下命令,就会开始构建:

yarn build

yarn build会执行package.json中定义的构建脚本,在我的项目中,实际上运行了vue-cli-service build,该命令会进行如下操作:

(1)检查配置文件: Vue CLI首先会查找并解析项目中的配置文件vue.config.js,以获取构建配置和其他相关的配置信息。

(2)代码转译和打包: Vue CLI会使用webpack和相关的加载器(例如babel-loader)对项目的源代码进行转译和打包。这包括将Vue单文件组件转换为JavaScript、处理CSS预处理器(如SassLess)等。

(3)静态资源处理: Vue CLI会处理项目中的静态资源,如图片、字体等文件。这可能包括复制这些文件到输出目录,并在构建过程中引入适当的路径。

(4)压缩和优化: 构建过程还涉及到对输出的JavaScript、CSS和其他资源文件进行压缩和优化,以减小文件大小并提高应用性能。

核心步骤如下图所示:

f88066e794dc15a57756a9f1b659a462.png

现在,我们可以思考下可以在哪个阶段进行优化了。“源代码打包”是最先开始的操作,我首先想到的是这一阶段消耗的时间必然与代码体积呈正相关,即代码体积越大,需要编译的时间就越长,大致如下图所示。因此,如果能减少需要打包的代码体积,就可以节省一部分时间了。

d52f92b1419d1c7a839e8ae78268b62c.png

此外,“源代码打包”阶段还会使用配置的loader对代码进行处理,在一个项目中可以配置多个loader,例如用vue-svg-inline-loader将 SVG 文件转换为 Vue 组件中的内联 SVG ,用babel-loader来转译js文件。但webpack为单线程模式,只能依次使用每个loader处理代码,如果遇到耗时较长的loader,后续loader就只能等待。因此,如果能找到耗时较长的loader,让它们同时运行,也能节省一些时间。

构建时还会引入项目的图片,如果大尺寸的图片过多,也会影响构建性能。所以,我们还需要将大尺寸的图片进行替换,以提升构建速度。

针对以上3个优化点,我寻找了多个方案进行尝试,最终生效的方案如下图所示,后续将会对前两个方案进行详细介绍。

9d05c6784bd278b7d43be1bc36e60edb.png

二、提前编译第三方库

引入webpack-bundle-analyzer插件分析项目体积

Webpack Bundle Analyzer 插件是一个用于分析 Webpack 打包结果的工具,它提供了一个直观的可视化界面,展示了项目打包后的文件结构,各个模块的大小、占比、依赖关系等信息。我们可以根据分析结果,针对性地优化文件大小,减少不必要的资源占用。

  1. 安装

yarn add -D webpack-bundle-analyzer
  1. 使用

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;module.exports = {configureWebpack: {plugins: [new BundleAnalyzerPlugin()]}
}

该插件将以树状图的形式展示项目打包后的内容, 从中可以看出每个文件的体积大小。

8965893876ef615016f655eee2dac112.gif

文件的体积参数有以下3种:

  • stat表示文件未压缩的原始体积

  • parsed表示文件经过插件处理后的体积,假如项目中使用了Uglify插件,那么parsed为文件压缩后的体积

  • gzip表示文件经过gzip压缩后的体积

  1. 项目体积分析

我在项目中引入webpack-bundle-analyzer插件后,得到了如下的分析图:

cc9cab524c59380baf761bd310387fcf.png

通过观察该树状图,可知项目的总体积为 28 MB,其中3个最大的文件分别为index.js(8.71 MB), preview.js(7.5 MB), 和survey.js(7.45 MB)。这三个文件都引用了element-ui、moment等第三方库。第三方库的代码往往比较稳定,不会频繁变化。如果将这些第三方库打包成一个动态链接库,并在相关页面引入,那么在每次构建主项目时就不需要重新构建这些库了,从而使得源代码体积减少,打包速度加快。

使用动态链接库技术

动态链接库(Dynamic Link Library,DLL)是一种在Windows操作系统中常见的技术,可以用来在程序运行时加载共享的代码和资源。动态链接库中的函数、变量和资源可以被多个程序共享使用。这意味着不同的程序可以同时使用同一个动态链接库,从而减少磁盘空间和内存占用。

Webpack提供了两个插件DllPluginDllReferencePlugin来配置动态链接库,DllPlugin用于将第三方库(例如 VueReact 等)打包到一个或多个独立的动态链接库(DLL)中。DllReferencePlugin用于在主项目中引用预先打包好的动态链接库。以下是使用这 2 个插件的基本步骤:

  1. 创建一个用于打包第三方库的配置文件

首先,在项目根目录下创建 webpack.dll.config.js 文件,用于配置 DllPlugin

// 导入 path 和 webpack 模块
const path = require('path');
const webpack = require('webpack');// 导出配置对象
module.exports = {// 指定 webpack 模式为生产模式mode: 'production', // 入口配置,将需要打包的第三方库列出entry: {vendor: ['vue', 'vue-router', 'vuex', /* 此处可以继续添加其他第三方库 */ ]},// 输出配置,指定生成的动态链接库文件名称和路径output: {filename: '[name].dll.js', // 动态链接库文件名,[name] 表示入口名称path: path.resolve(__dirname, 'public/dll'), // 动态链接库文件输出目录library: '[name]' // 将动态链接库导出的内容赋值给变量名 [name]},// 插件配置,使用 webpack.DllPlugin 插件plugins: [new webpack.DllPlugin({name: '[name]', // 全局变量名称,保持与 output.library 一致path: path.resolve(__dirname, 'public/dll/[name].manifest.json') // 动态链接库清单文件路径})]
};
  1. 创建npm脚本

在 package.json 文件中,添加一个新的 npm 脚本,用于运行上述 webpack 配置文件:

"scripts": {// ...其他脚本"dll": "webpack --config webpack.dll.config.js"
}

运行yarn dll,会在public/dll文件夹下生产vendor.dll.jsvendor.manifest.json2个文件。

  • vendor.dll.js文件文件包含了指定的第三方库(例如 VueVue RouterVuex 等)的代码和资源,以及这些库的导出信息。

  • vendor.manifest.json 文件是一个清单文件,记录了vendor.dll.js文件包含了哪些模块,以及每个模块的路径、ID 等信息。主项目在构建时会通过这个清单文件来确定如何引用动态链接库中的模块,以确保正确地加载和使用这些模块。

  1. 在 vue.config.js 文件引入 DllReferencePlugin插件

在 configureWebpack 配置中引入 DllReferencePlugin

// 导入 webpack 模块
const webpack = require('webpack');// 导出配置对象
module.exports = {// 其他配置...// 配置 webpackconfigureWebpack: {// 插件配置plugins: [// 使用 webpack.DllReferencePlugin 插件new webpack.DllReferencePlugin({// 指定上下文路径为当前工作目录context: process.cwd(),// 指定动态链接库清单文件的路径manifest: require('./public/dll/vendor.manifest.json')})]}
};
  1. html文件中引入DLL文件

配置完成后,运行yarn build命令,得到如下结果。

20dd7387737e7356dad8dead4c689970.png

从图中可以看出,项目的总体积减少到了 7.35 MB,其中3个最大的文件分别减少到 2.74 MB(index.js), 1.74 MB( preview.js), 和 1.68 MB (survey.js)。构建时间由 66 秒缩短到了 43 秒,显著加快。

如果项目中引入了新的第三方库,则需要将该库添加到 webpack.dll.config.js 的 entry 中,并重新运行yarn dll即可。

三、为耗时loader开启多线程

引入speed-measure-webpack-plugin插件分析loader耗时

speed-measure-webpack-plugin 是一个用于测量 Webpack 打包速度的插件,包括各个阶段的耗时情况,例如初始化、加载、编译、优化、打包等;也可以分析每个 loader 和插件在打包过程中的耗时情况。这有助于我们找到影响性能的具体原因,进行针对性的优化。

  1. 安装

yarn add -D speed-measure-webpack-plugin
  1. 使用

我们需要创建一个 SpeedMeasurePlugin 的实例,并使用它来包装配置对象。

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();module.exports = smp.wrap({configureWebpack: {plugins: [new BundleAnalyzerPlugin()]}
});

如下图所示,运行yarn build后,就可以看到每个pluginloader分别花费了多少时间。

774efb3d2b2b0906778ec5123ae5fadb.png

使用thread-loader开启多线程

thread-loader可以将指定的 loader 放在 worker 池的子线程中运行,这样能充分利用多核 CPU 的性能,从而实现并行处理。thread-loader 适用于任何耗时的 loader,特别是那些需要大量计算的 loader,例如 Babel、TypeScript 等。每个 worker 是一个单独的 Node.js 进程。

在我的项目中,babel-loadervue-svg-inline-loader耗时较多,因此我把thread-loader 放置到这些loader之前,为这些loader开辟了单独的线程池。具体配置如下:

module.exports = {// 配置 webpackchainWebpack: config => {// 针对 .js 文件的规则配置config.module.rule('js') // 添加一个规则命名为 'js'.test(/\.js$/) // 匹配文件后缀为 .js 的文件.exclude // 排除特定目录.add(/node_modules/) // 添加排除目录为 node_modules.end() .use('thread-loader') // 使用 thread-loader 处理 .js 文件.loader('thread-loader') // 指定 thread-loader 作为 loader.end() .use('babel-loader') // 使用 babel-loader 处理 .js 文件.loader('babel-loader') // 指定 babel-loader 作为 loader.end() // 针对 .vue 文件的规则配置config.module.rule('vue') // 添加一个规则命名为 'vue'.use('thread-loader') // 使用 thread-loader 处理 .vue 文件.loader('thread-loader') // 指定 thread-loader 作为 loader.options({ workers: 2 // 指定 worker 数量为 2}).end() .use('vue-loader') // 使用 vue-loader 处理 .vue 文件.loader('vue-loader') // 指定 vue-loader 作为 loader.end() .use('vue-svg-inline-loader') // 使用 vue-svg-inline-loader 处理 .vue 文件.loader('vue-svg-inline-loader') // 指定 vue-svg-inline-loader 作为 loader.end() }
}

如果不配置thread-loader,各个loader的加载过程如图:

25f08ba7a159da084cd3449678d175ae.png

为耗时loader开启单独线程后,加载过程如图:

7758a0d857fe7d898dca9503c0c335df.png

通过为耗时的loader开启多线程,使得项目构建时间从 43 秒减少到了 34 秒。

四、小结

通过使用DllPluginDllReferencePlugin插件将第三方库打包成动态链接库,引入thread-loader将耗时较长的loader放入单独的线程池中加载,替换项目中的大图片,使得项目构建时间从 66 秒减少到了 34 秒,总共减少 32 秒,约 48% 。

项目构建优化是需要不断尝试的,许多方案都不通用,以上3个优化点在本项目中起到了作用,下面我还将记录没有生效的方案,希望能为读者提供一些不同的思路,或许这些方案对你的项目有帮助。

  1. HardSourceWebpackPlugin插件进行缓存

在优化工作开始后,我最先想到的方案是:在第一次构建时,将没有变化的模块(如第三方库)的打包结果缓存下来,后续构建时直接读取缓存,就可以节省很多时间了。经过一番查找,发现HardSourceWebpackPlugin插件很适合用来执行这项工作。

Webpack提供了HardSourceWebpackPlugin插件来为模块提供中间缓存。如前文所述,Webpack在构建时,会解析项目中的每个模块,并根据需要对其进行转换和编译。在这一过程中,该插件会把编译结果保存下来。在下一次构建时,HardSourceWebpackPlugin插件会比较当前的模块和缓存中的模块是否一致,如果没有变化,就直接使用缓存结果。

引入该插件后,在本地的测试时发现:第一次构建花费的时间与之前相同,后续的构建速度却显著提升。但是,由于我的项目是使用部门统一的工作台部署,每次都需要重新执行yarn install安装依赖,所以该插件并不能产生作用。如果你的项目不是这种工作模式,那我推荐你使用该插件。

  1. 压缩代码

Webpack4默认情况下会对输出的JavaScriptCSS和其他资源文件进行压缩,但是我们还可以通过一些插件自定义压缩行为。

  • 压缩JS代码

我们可以使用terser-webpack-plugin插件来删除空格、注释及未使用的代码,使得压缩后的代码体积更小;此外,它还支持并行压缩。

  • 样式文件压缩

mini-css-extract-plugin插件可以将打包生成的css代码从JavaScript bundle 中提取出来,在多页面应用中,如果多个页面共享一些css样式,使用该插件可以避免重复打包这些共享的样式。此外,分离出的css文件也可以与JavaScript文件同时加载,从而提高页面加载速度。

需要注意的是插件本身也需一定的时间来加载,因此,我们还应比较引入插件的时间是否高于压缩文件后节省的时间,合理使用。

五、参考资料

  • webpack-bundle-analyzer插件:https://github.com/webpack-contrib/webpack-bundle-analyzer

  • speed-measure-webpack-plugin插件:https://github.com/stephencookdev/speed-measure-webpack-plugin

  • **DllPluginDllReferencePlugin:**https://webpack.js.org/plugins/dll-plugin/

  • **thread-loader: **https://webpack.js.org/loaders/thread-loader/

  • **terser-webpack-plugin:**https://www.npmjs.com/package/terser-webpack-plugin

  • **mini-css-extract-plugin:**https://www.npmjs.com/package/mini-css-extract-plugin


- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

55cf6e317a268e74052f2c5e3e0a7171.png

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【操作技巧】如何给Jetson Orin Nano的ubuntu port换镜像源

学习《OpenCV应用开发:入门、进阶与工程化实践》一书 做真正的OpenCV开发者,从入门到入职,一步到位! 换镜像源 因为工作需要,我想把之前Jetson Orin Nano上OpenCV4.5.4 升级到OpenCV4.8。先到这里下载脚本 wget htt…

搜维尔科技:OptiTrack 提供了性能最佳的动作捕捉平台

OptiTrack 动画 我们的 Prime 系列相机和 Motive 软件相结合,产生了世界上最大的捕获量、最精确的 3D 数据和有史以来最高的相机数量。OptiTrack 提供了性能最佳的动作捕捉平台,具有易于使用的制作工作流程以及运行世界上最大舞台所需的深度。 无与伦比…

【mysql 数据库事务】开启事务操作数据库,写入失败后,不回滚,会有问题么? 这里隐藏着大坑,复试,面试时可以镇住面试老师!!!!

建表字段: CREATE TABLE user (id INT(11) NOT NULL AUTO_INCREMENT,nickname VARCHAR(32) NOT NULL COLLATE utf8mb4_general_ci,email VARCHAR(32) NOT NULL COLLATE utf8mb4_general_ci,status SMALLINT(6) UNSIGNED NULL DEFAULT NULL,password VARCHAR(256) NULL DEFAULT…

P2141 [NOIP2014 普及组] 珠心算测验

AC代码&#xff1a; #include<iostream> #include<map>using namespace std;const int N 110; int a[N];int main() {int n;cin >> n;for(int i0;i<n;i) cin >> a[i];map<int,bool> mp;for(int i0;i<n;i){for(int ji1;j<n;j){mp[a[i…

Django配置静态文件

Django配置静态文件 目录 Django配置静态文件静态文件配置调用方法 一般我们将html文件都放在默认templates目录下 静态文件放在static目录下 static目录大致分为 js文件夹css文件夹img文件夹plugins文件夹 在浏览器输入url能够看到对应的静态资源&#xff0c;如果看不到说明…

在linux上不依赖于Nignx等服务器部署ASP.NET Core 7.0 WebAPI

笔者近期需要部署一款基于B/S架构的后端程序在linux的Debian发行版上&#xff0c;本文章以本次部署遇到的问题为线索&#xff0c;总结如何在Debian上部署ASP.NET Core7.0WebAPI应用程序。 在linux上不依赖于Nignx等服务器部署ASP.NET Core 7.0 WebAPI 1.先决条件2.应用发布3.部…

MetaGPT 1 安装与配置踩坑实录

安装 与 配置直接参考这里就行&#xff1a;Hugging Muti Agent&#xff08;二月学习&#xff09; - 飞书云文档 (feishu.cn) 这里按照教程安装的是metagpt 0.6.6 &#xff0c;经过跟0.7.0对比&#xff0c;个人认为0.7对其他llm接入可能更好&#xff0c;文档也更清晰。 0.6.6的…

【Python笔记-设计模式】中介者模式

一、说明 中介者模式是一种行为设计模式&#xff0c;减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互&#xff0c;迫使它们通过一个中介者对象进行合作。 (一) 解决问题 降低系统中对象之间的直接通信&#xff0c;将复杂的交互转化为通过中介者进行的间接交…

热闹元宵进行中,如何利用VR全景展示民宿品牌形象?

错峰出游闹元宵&#xff0c;元宵节恰逢周末&#xff0c;而且还是春节假期返工之后的首个休息日&#xff0c;不少人都想通过短途度假来缓解“节后综合征”。两位数的特价机票、打折的各种酒店让你实现“旅行自由”&#xff0c;那么如何知道特价酒店服务好不好呢&#xff1f;先别…

VR转接器:破解虚拟与现实边界的革命性设备

VR转接器&#xff0c;这一革命性的设备&#xff0c;为虚拟现实体验带来了前所未有的自由度。它巧妙地连接了虚拟与现实&#xff0c;使得用户在享受VR眼镜带来的奇幻世界的同时&#xff0c;也能自由地在现实世界中活动。这一设计的诞生&#xff0c;不仅解决了VR眼镜续航的瓶颈问…

抖音短视频提取器|视频内容批量提取软件

抖音短视频提取器是一款功能强大的工具&#xff0c;旨在解决用户获取抖音视频时需要逐个复制链接、下载的繁琐问题。我们希望用户能够通过简单的关键词搜索&#xff0c;实现自动批量抓取视频&#xff0c;并根据需要进行选择性批量下载。基于C#开发的这款工具不仅支持通过关键词…

vscode——本地配置(C和C++)(1)

本地配置C和C&#xff08;1&#xff09; 什么是vscodevscode和visual studio的区别vscode的本地配置汉化 vscode配置C和C环境创建全局变量安装插件编写C或C程序生成task.json文件生成.exe文件 今天我们来看看一个开发工具——vscode。 什么是vscode 在正式了解vscode之前&…