Hi guys,你们的webpack项目是怎么做性能优化的?

742 阅读5分钟

前言

事情是这样的开始的,有一次朋友约饭,一哥们冷不丁地问了一句:“Hi guys,你们的vue项目都是怎么做性能优化的呢"?此时大家相互看向了对方,空气也变得格外安静,我抿了一口水胸有成竹的说:“这个鄙人熟”。于是做了如下回答:

pexels-sora-shimazaki-5926382.jpg

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-loaderHardSourceWebpackPlugincacheDirectory( 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。