JavaScript 的性能分析与提升
对于 JavaScript/前端来说,性能的提升主要有两大方面:
-
页面初始化的优化
这一方面主要涉及到非代码结构上,但是能够提升用户体验感的优化,如,提升用户看到页面的速度、减少用户等待与页面交互的事件。
如 lazy loading 就是一个比较知名的初始化上的优化,购物网站也好,视频网站也好,这种 2C 的页面会载入大量的数据,如果让用户等到所有的数据都加载成功,再和页面交互,那肯定是会因为使用体验感太差而造成用户流失。
是否在页面初始化的时候存在太多的 HTTP 请求,以及 HTTP 请求的文件是否太大,这些都会导致加载速度变慢,从而影响用户体验感。
-
页面运行时的优化
这块大部分涉及到结构和实现上的优化,影响到代码实际上的运行速度和性能。
如页面上如果要使用 3D 功能,那 3D 的渲染是否流畅,是否会阻塞页面的渲染。如果用户点击页面上的可点击部分,是否会造成延迟等。代码的实现是否会造成内存泄露,导致 reference 无法被 GC 正常清理,导致使用的 cache 越来越大,最终页面加载、运行越来越缓慢等。
有一个比较少提及,但是又的确是问题的方面就是,是不是做了太多的为优化(micro-optimizations)。以 React 为例,
useMemo
和useCallback
是两个内置的 memoization 的 hooks,用这两个 hooks,React 会在每次渲染的时候去比较状态是否有变化,如果有的话,才会重新计算。当计算被 memoized 值的成本高于重新渲染时,那么 memoized 的过程就是没有必要的,这种 micro-optimizations 反而会降低整体页面渲染的效率。
顺便,如果 CSS 很花哨,或者无意义的 HTML 太多,那么有的时候降低页面渲染速度的可能不是 JS,而是 CSS 和 HTML。在不少用了比较炫的 CSS & CSS Animation 的页面,其实 CSS 吃渲染的比重还挺大的。
可选的 performance 工具
测试 performance 的话,除了 devtools 之外其实还有一些其他的工具,不过因为这里不会一一列出,只会大概提一下百度/Google 的范围:
-
JavaScript 自身提供的一些计算表现的函数
具体可以查看一下 Performance API 和 PerformanceEntry Object,这个适用于开发环境下使用,因为有一些函数会产生 log,在开发环境下反而会影响 performance。
-
benchmar.js
这个可能需要自己实现,不过在 Stack Overflow 上找到一个代替品:https://jsbench.me/
本质上来说,这是一个网站可以接受两个 snippets,然后分别运行并且比较 performance。
Stack Overflow 上面的回答是:
jsperf is based on benchmarkjs so using an online code editor (like jsfiddle, jsbin, plunker etc…) and including benchmarkjs as a library will do.
如果需要对大段的代码进行分析的话,可能说找一个相关的 node 包配置一下比较好。
-
一些可以测试网站综合性能的 API 或是网页
如 https://www.webpagetest.org/,看需求,这可能会是一个付费服务,pricing 如下:
这种网站唯一麻烦的地方就在于,要测试的服务必须要已经有 domain 才可以测试,如果是私网的 beta 版本,可能会有一定的局限性。
另外,Google 也有对应的 API 可以进行测试,所以我猜国内的一些 cloud service 应该也会有吧
-
devtools
下面会稍微详细介绍的部分,这里主要说的是 chrome devtools,目前来说最好用
其他的浏览器也有提供相似的服务(我指的是 Firefox,常用的就这两种)
devtools
network snapshots
它看起来大概是这个样子的:
具体打开的方式是点击 capture screenshots
,随后使用 ⌘ R
去录制,就会有上面的效果。
对于我来说,加载 csdn 的 html 文件就要一秒多:
1.31s 的时候一些 JS 的文件和海量的图片都下载好了:
诸如次来的,有 screenshots 看起来会方便一点,因为会有当前的 snapshots:
不然的话直接看 waterfall 也不是不可以。
network 还有另外一个功能就是对网页进行限速(throttling),这样可以模拟网速慢的情况,网站的表现如何。
performance
performance insight 好像是新推出的:
所以这里就优先使用 performance 了,其结果如下:
对于 performance 来说,除了 network throttle 之外还可以使用 cpu throttle,这样可以模拟并分析当前页面在比较老的设备上的性能。
当 record 一部分片段的时候,能够通过 performance 这块比较明显的看出当前页面的性能:
大致分析一下几个比较重要的时间点:
- 在大概 700ms 的时候,浏览器开始 FP(First Paint),即浏览器开始渲染初始页面。
- 在大概 800ms 的时候,浏览器开始 FCP(First Contentful Paint),即开始渲染页面上的第一个内容(有可能是文字、图片等)。
- 在大概 1700ms 的时候,浏览器完成了 DCL(DomContentLoaded)。这个时候,浏览器已经完成了所有 DOM 节点的渲染,接下来就是依靠客户端去执行操作。
- 在大概 2800ms 的时候,浏览器完成了 LCP(Largest Contentful Paint)的渲染。
- 在大概 3000ms 的时候,浏览器完成了加载(load)
滑动窗口也能够看到在某个区间最耗时的部分是什么。
之前就有碰到一个情况,说是前端的页面渲染太慢,最后通过 profiling 看了一下,主要是 idle(XHR)的时间太长了,所以愉快的把锅甩给了后端(❓)
memory
这里可以查看并对比内存之间的 snapshots,如:
这是对比了两次 YouTube 首页刷新的状态,可以看到删除合新建的结点是一样的(YouTube 的首页布局肯定是不变的),因此相对变化为 0(# Delta)。在进行 DOM 节点操作的时候,就可以使用 memory 进行对比。
举个例子,如果删除了 list 一个结点,但是# Delta 变化过大,就有可能是整个 list 被删除了并重新渲染了,这个时候就可以考虑在这里做一些优化。
又或者是添加/删除的时候 # delta 没有变化,那就就代表了某个结点在未被创建的时候就存在了(添加的情况),或是应该被删除了 GC 却没有能够顺利地清理(删除的情况),二者都代表可能会出现内存泄露问题。
lighthouse
lighthouse 是一个可以跑 audit 的工具,基本上可以分析一下该页面的表现情况,以及在 Google 的 SEO 表现情况,如:
如果是 2C 的项目,可以根据这些问题进行修改。
reference
- Performance API
- PerformanceEntry Object
- How to profile Javascript now that JSPerf is down? [closed]
- Analyze runtime performance
- Profile Site Speed With The DevTools Performance Tab