说到性能优化,你们确定能想到不少内容。好比说如何修改 Webpack 配置来达到构建提速以及优化产物的需求;好比说如何对页面进行性能优化等等。前端
其实性能优化还存在不少能够玩的地方,今天笔者就来聊一些你们不常关注的地方,从开发到 CI 的构建阶段以及最后部署上线这几个链路。node
构建阶段分为两个部分:react
这两部分各自侧重的点并不相同。前者更加关注构建速度,对于产物没有什么要求,毕竟本地开发时候构建是一件很频繁的事情,速度慢就意味着下降开发效率;后者更关注产物,好比说文件体积、数量等等指标,固然了构建速度也不能说彻底不关心,但可能是在保证产物的前提下再去提高构建速度。webpack
接下来笔者会就这两部分来分别聊聊咱们能够作的性能优化。git
以 Webpack 为例,本地开发构建这部分是咱们平常工做中最常接触到的,若是能带来必定的提速,那么感知仍是挺强的。github
这部分主要分为两个阶段:web
yarn start
第一阶段相对来讲频率低点,速度慢点在大部分状况下仍是能够忍受的。面试
可是第二阶段是咱们前端开发天天都会进行数十次甚至上百次的高配操做,而且对于一些中大型项目来讲,在这部分花的时间一次可能就须要 10 秒多。假设老王天天修改 100 次代码,那么在这部分天天都得等待上 15 分钟甚至更多。shell
那么咱们有办法去提效这部份内容么?答案是有的,应该不少读者都早已了解到这类产品了:Vite。npm
Vite 属于 NoBundle 方案,也能够称之为 UnBundle、Bundleless,反正都是同一个玩意。同类竞品还有 snowpack、wmr 等,固然名气都不如前者大。
若是你已经使用过 Vite,那么应该对它秒级启动以及热更新有了映像,相比单纯的 Webpack 提高巨大。
上图出自该文章,项目有 15 万行代码,很大型的一个项目了,你们能够发如今启动以及热更新上真的是有数十倍的提高,这对于开发体验来讲真的流畅不少。另外笔者以前也参与过公司内部 NoBundle 方案的研发,得出的数据也是很可观的。
那么为何 Vite 能带来这样的体验的呢,究竟是如何实现这样的效果的?笔者这里就粗略的来聊聊。
首先来聊聊 Webpack 构建,基于 Webpack4 以及中大型项目来讲。当咱们执行 yarn start
之后,Webpack 会开始全量打包,构建出依赖图后把全部内容都打成几个文件,这个构建依赖图以及打包的过程会花掉咱们数分钟。
当触发热更新的时候,Webpack 也须要找出这一条依赖链路并再次对链路进行打包,所以这个过程耗时也很多。固然若是你用上了 Webpack5 的话,基于持久化缓存应该能提速很多。
可是对于 Vite 来讲就不须要这样干了。
首先 Vite 使用到了 ESBuild 来预构建依赖,这是一个用 Go 写的特别牛逼的构建器,效率相比 JS 来讲快了数十倍甚至百倍,Vite 须要利用这个来处理模块以及构建 ESM 环境。
另外就是得益于 ESM 按需加载的特性,咱们无需启动项目的时候构建依赖图以及打包文件,而是浏览器请求什么文件才编译什么文件(好比说编译 TS、插入热更新代码等),依托于这个特性咱们能很快的跑起来项目。
最后当用户触发热更新的时候,Vite 也无需像 Webpack 那样作,而是找出最小的依赖路径(通常来讲就是修改的那个文件),而后修改下文件的 hash 下发给浏览器失效以前的缓存便可。
基于上面的一些特性以及 ESBuild,Vite 基本不会由于代码量变大而形成速度有明显拖慢,可是对于 Webpack 来讲,项目体积很明显会拖慢构建速度。
读者看到这里,可能以为这玩意确实牛逼,摩拳擦掌准备干上一番。可是,这里要给各位泼个冷水。根据咱们内部的 Nobundle 使用结果以及笔者与多位在大厂作过这个方案的朋友交流的结果来看,接入业务成本巨大,虽然提效不错,可是以接入的成原本看可能投入产出比就不是那么可观了。目前也没有一套很好的接入方案,极可能在不一样的项目里会踩到不一样的坑,最大的缘由仍是来自于 ESM 环境。
可是笔者认为 Nobundle 之后必定会成为本地开发构建的主流,由于开发体验实在太顺滑了。
另外极可能读者会问 Vite 是否会取缔 Webpack 这类问题。笔者认为这两个东西算不上是竞品,Webpack 能适用于复杂、须要定制化的场景,这点 Vite 是作不到的(将来可能也不会去作),Vite 主要是为了改善开发阶段的体验,顶多在开发阶段 Vite 能顶替掉 Webpack 的工做。
如下笔者列了点资料,你们有兴趣能够了解下:
最后你们若是有兴趣在业务里作迁移的话,必定要多看看市面上迁移相关的文章,能帮助你们在踩坑的时候快速解决问题。
在 CI 中构建大体分为三个环节:
前两个环节涉及到的内容很少,笔者这里就快速带过。
依赖安装仍是挺耗时的,咱们能够经过如下几点来加速这个环节:
这里稍微聊一下 yarn2 这个事情。升级到 yarn2 之后会有两种可选方案,一种仍是 node_modules 的方案,另外一种是抛弃 node_modules,转而使用 PnP。
这两种方案前者在迁移的过程当中基本不用动代码,可是已经能改善依赖安装的速度;后者须要变动的地方还挺多,在内部所有推广开来仍是存在一部分阻力的,可是这种方案可以大幅度减小依赖体积以及改善安装速度,你们能够自行评估投入产出比。
代码质量保障通常分为两块:
固然还有别的质量保障方案,这里就不表了。
ESLint 其实没啥优化的方案,却是本地提交代码的时候大部分项目应该都作了优化,只是可能不少人都忽略了。想必你们项目中应该都会存在 husky + Lint-stage,这两个工具其实能帮助咱们在提交代码的时候只对须要提交的文件进行 lint。这是一种增量的思路,在不少状况下咱们都须要这种思路来帮助咱们作性能优化。
对于单测来讲,可能不少读者压根就没写过这玩意。可是若是你作过一些 npm 包或者 Node 服务的话,会发现单测仍是挺有必要的。
对于大型的项目来讲,Test case 是至关多的。以咱们内部的组件库为例,总共有 1000+ 的 Test case,光在本地完整执行一次 yarn test
就足足须要花费两三分钟,在云端跑的速度就更不用说了。可是实际上咱们每次提交的代码影响到的 Test case 远没有那么多,每次全量跑单测花费的时间真的太多了。
说到这儿,读者们应该能记起笔者上文提到过的增量。没错,在这里咱们彻底可使用增量来提升跑单测的速度。若是你使用 Jest 框架的话,能够了解下和 —onlyChanged
相关联的参数来实现增量单测。
说到构建优化,想必不少读者都会说这题我会。毕竟优化 Webpack 配置已经算是面试考烂的题目了,而且市面上关于这类的文章也是层出不穷。
所以笔者这里就再也不来聊咱们该这样那样配置 Webpack,若是读者有须要的话能够自行网上翻阅资料。
其实除了修改 Webpack 来达成性能优化的目的,升级版本也会有很大的惊喜。
好比说从 4 升级到 5 之后,咱们能够经过这些新增特性来实现提效:
笔者以上列举了一部分升级 Webpack 能带来的收益,你们若是对某个特性有兴趣的话能够自行搜索文章。
另外其实咱们常说的构建速度优化,其中有一个点关注的人并很少,可是对构建速度也有不小的影响,那就是压缩代码。
若是你用过 Speed Measure Plugin 这个插件的话,就能发现笔者所言不虚。对于大型应用来讲,就算你使用多线程进行压缩,最终可能仍是会花费二三十秒的时间。
固然了,咱们是可以对这个阶段作优化的,用到的工具上文也说过,也就是 ESBuild。ESBuild 主打的就是构建快,从官方的性能对比图里能够看出是降维打击其它构建器。
恐怖的数百倍提高(固然笔者实测拿不到这样的数据),但即便它构建速度确实很快,目前仍是存在了一些问题(最大的问题是 CSS 上的处理)致使上生产仍是不大现实。可是实际上 ESBuild 还支持用于压缩代码,风险基本能够忽略,笔者实测业务项目中能带来 30% ~ 40% 的速度提高,仍是至关可观的。
另外除了以上说的这些以外,其实在构建这个环节中咱们也能够经过增量的思路来提高效率。
对于多页应用来讲,大部分状况下咱们每次发布所修改的代码不会影响到全部的入口。所以没有被影响到的入口实际是不须要再次被构建的,直接使用以前的缓存就好了。那么根据这个思路,咱们须要每次在构建前找出上次发布到当前为止全部变更过的文件以及这些文件所影响的入口,最后动态修改 Webpack 的入口配置便可实现增量构建。
说干就干,如下是增量构建的大体思路。
首先是找到距离上次发布后有变动的文件,这个很简单,一行命令就搞定了:
git diff --name-only {git tag / commit sha}
复制代码
部署完别忘了打个 tag 或者记录一下 commit id,下次执行命令的时候传入。
当咱们拿到变动后的文件名后,接下来须要找出这些文件所影响的入口,所以须要开始构建依赖树。虽然 Webpack 也会帮助咱们构建这个,可是咱们不必用到那么重的东西,找个专一依赖树的库就行,你们能够选择 madge 或者别的相似产品。
接下来咱们只须要匹配文件找到影响的几个入口就行,而后动态修改 Webpack 配置里的 entry
属性进行构建便可。
最后咱们将构建的内容替换掉以前的旧入口产物就好了,没有变更的不须要管。
其实这个多页应用的增量构建作法和 monorepo 里的部署很类似。若是咱们在 monorepo 里只须要对改过代码的 package 进行部署的话,那么部署代码的逻辑是很类似的,一样也是找到被影响的 package(多页应用里就是入口了),而后进行构建发布。
若是你们业务中也存在多页应用的项目,那么能够尝试下该方案,带来的收益应该会很可观。
说了那么多,笔者来总结一下上文中聊过的优化手段:
上线后的通用性能优化也被说烂了,无非从网络协议、CSS、Webpack 配置入手,笔者仍是来说点别的。
既然要聊性能优化,那么咱们确定得知道到底哪里存在性能问题,不然就是虚空优化了。如何检测性能优化、到底有哪些性能指标也是笔者常问的面试问题(固然得面试者简历里写了作过这方面),可是大部分时候获得的答案在笔者看来是不正确的,并不能肯定到底对方是否是真的作过这方面的优化。
好比说谈到性能指标,问十人九人必会说白屏时间,可是其实白屏时间在当下并非一个合格的指标。大部分应用开屏都会存在 Loading 或者骨架屏,在这些内容过渡到页面出现用户关心的内容还须要一段时间。可是若是咱们仅仅靠收集白屏时间来判断用户看到 DOM 出现是错误的作法,单靠这个指标去作开屏的优化是远远不够的,咱们必须得收集到用户看到真实 DOM 的时间。
此时咱们能够收集 LCP(Largest Contentful Paint)指标,这个指标会帮助咱们记录页面中最大内容绘制的时间戳。
经过这个指标外加白屏时间,咱们才可以正确的去作开屏时间的优化。另外在这里不使用 LCP 指标也是能够的,咱们能够本身给关键 DOM 打点,实现个性化的收集。
除了 LCP 指标以外,还存在很多新的指标,你们有兴趣的能够了解下笔者以前写的文章,文中作几个新的指标作了阐述并说明了该如何优化这些指标。
以上就是本文的所有内容了。性能优化是一个很大的话题,除了那些耳熟能详的手段以外,其实还存在着很多方案能作。
你们若是有什么疑问欢迎在评论区交流。