前言
事情是这样的开始的,有一次朋友约饭,一哥们冷不丁地问了一句:“Hi guys,你们的vue项目都是怎么做性能优化的呢"?此时大家相互看向了对方,空气也变得格外安静,我抿了一口水胸有成竹的说:“这个鄙人熟”。于是做了如下回答:
1、优化打包速度
通过 speed-measure-webpack-plugin 测量你的 webpack 构建期间各个阶段花费的时间,每个项目都有其构建瓶颈,分析之后对症下药,下面是一些通用的解决方案:
-
thread-loader(webpack4 官方推荐)
我们都知道,日常开发中我们需要使用 loader 对 js ,css ,图片,字体等文件做转换操作,随着后期功能的丰富,转换的文件数据量也随之增加。由于 js 单线程的特性使得这些转换操作不能并发处理文件,而是需要一个个文件进行处理,这个过程过程是耗时,thread-loader 使用起来也非常简单,只要把 thread-loader 放置在其他 loader 之前, 那 thread-loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行,注意:请仅在耗时的 loader 上使用,具体可参考点我。
-
合理配置缓存
使用 webpack 缓存的方法有几种,例如使用 cache-loader、HardSourceWebpackPlugin、cacheDirectory( babel-loader), 这些配置方法在启动时都有一定的开销, 重新运行期间在本地会节省很大的时间,可以根据不同项目特点配置使用。
-
压缩时间的优化
压缩 JavaScript 代码需要先把代码解析成用 Object 抽象表示的 AST 语法树,再去应用各种规则分析和处理 AST,导致这个过程计算量巨大,耗时非常多,使用多进程并行运行来提高压缩速度,具体参考点我。
-
优化搜索时间- 缩小文件搜索范围 减少不必要的编译工作
(1) 优化 module.noParse 配置
添加没有依赖的第三方库(jQuery、lodash等),打包的时候就不会去解析,这样能够提高打包速率。
(2) 优化 loader 配置
通过test、include、exclude 配置项确定 Loader 的解析规则,例如:exclude: /node_modules/, 排除 node_modules 目录下的文件,因为node_modules 目录下的文件都是采用的 ES5 语法,没必要再通过 Babel 去转换。
(3)优化 resolve.module 配置
resolve.modules 告诉 webpack 解析第三方模块时应该搜索的目录。默认值['node_modules'] ,含义是先去当前目录下的 ./node_modules 目录下去找想找的模块,如果没找到就去上一级目录 ../node_modules 中找,再没有就去 ../../node_modules 中找,以此类推,如果你想要添加一个目录到模块搜索目录,此目录优先于 node_modules 搜索,此时就可以通过配置这个属性来实现。
(4)优化 resolve.alias 配置
这个是一个常用配置,通过别名来把原导入路径映射成一个新的导入路径,减少耗时的递归解析操作,详细配置就不再赘述了。
(5)优化 resolve.extensions 配置
在导入语句没带文件后缀时,webpack 会根据 resolve.extension 自动带上后缀后去尝试询问文件是否存在,所以在配置 resolve.extensions 配置时, resolve.extensions 列表要尽可能的小,不要把项目中不可能存在的情况写到后缀尝试列表中。频率出现最高的文件后缀要优先放在最前面,以做到尽快的退出寻找过程。在源码中写导入语句时,要尽可能的带上后缀,从而可以避免寻找过程。
2、优化打包后的文件
大多数情况下,我们会更加侧重于这一优化,这对于线上的产品影响更大,主要优化方案有分包处理、缩小包的体积、配置CDN等,下面依依说一下:
-
配置CND服务器
将打包后的所有静态资源,放到CDN服务器,更快、更可靠地将资源文件发送给用户;来提供高性能,可以直接自己的CDN地址配置在publicPath属性上来实现。
-
代码分离
代码分离是将代码分离到不同的bundle中,之后我们可以按需加载,或者并行加载这些文件;可以分出更小的bundle,以及控制资源加载优先级,提供代码的加载性能;包括动态导入文件(按需加载)、自定义包的拆分。
(1)动态导入模块
当满足条件时通过ECMAScript中的 import() 语法来导入模块,这样可以保证不用到该内容时,浏览器不需要加载和处理该文件的js代码,可以参考路由懒加载的用法。
(2)自定义拆包
通过配置SplitChunks相关的信息来实现,具体可参考webpack中文文档进行配置。 -
Tree Shaking
Tree Shaking表示消除死代码(dead_code),也是export未使用的代码,这里通过配置:
(1)usedExports:通过标记某些函数是否被使用,之后通过Terser来进行优化的; (2)sideEffects:如果所有代码都不包含副作用,我们就可以简单地将该属性标记为false,来告知 webpack 它可以安全地删除未用到的 export。如果你的代码确实有一些副作用,可以以数组的方式配置。
Tree Shaking 详情参考 -
JS代码压缩
JS代码的压缩可以借助插件来帮我们完成并且让我们的bundle变得更小,这里推荐terser-webpack-plugin,使用说明参考terser-webpack-plugin文档,补充说明:真实开发中,在production模式下,默认使用terser-webpack-plugin来处理我们的代码的,如果默认处理未能达到预期,可以创建和覆盖相关的配置。
-
css代码压缩
css的代码压缩可以使用css-minimizer-webpack-plugin,参考css-minimizer-webpack-plugin文档。
-
文件压缩
gzip – GNU zip格式,是目前使用比较广泛的压缩算法,在webpack中我们借助compression-webpack-plugin实现,参考compression-webpack-plugin文档。
结束语
巴拉巴拉说了这么些,刚准备喝第二口水时,一个朋友说:“你们现在还用webpack啊,我们都全用vite了”。震惊的我差点把嘴里的水给喷了出来,那时我就决定下次我们聊聊vite。