Javascript开销(Cost)

原文链接:https://medium.com/dev-channel/the-cost-of-javascript-84009f51e99e

当我们建立的网站越来越多依赖Javascript时,我们有时候会为了一些我们不容易看到的地方付出代价。在这边文章中,我会讲述一些规范,如果你想让你的网站在移动设备上更快加载和响应的话,这些规范可能会对你有帮助。

tl;dr: less code = less parse/compile + less transfer + less to decompress

tl;dr是too long; don't read的缩写

网络

当大多数开发者想到Javascript的开销时,可能会联想到下载和执行开销。传送的数据量越大,那用户连接的速度就越缓慢。

Javascript开销(Cost)_第1张图片
1_U00XcnhqoczTuJ8NH8UhOw.png

即使在一些大国家,如果用户的有效网络不是3G, 4G 或者WiFi的话(用户可能在咖啡厅用着wifi,但是实际网速是2G),那么这就会成为一个问题。

你可以通过以下几种方式来减少网络传输的开销:

  • 只传输用户需要的. 代码分离(Code-spliting)能起到一定作用.
  • 混淆(丑化) (ES5有Uglify, ES2015有babel-minify 或者 uglify-es)
  • 强力压缩 (使用 Brotli ~q11, Zopfli 或者 gzip). 相较于gzip,Brotli 在压缩比上更胜一筹. 它使CertSimple在JS的体积上节省了17%,使LinkedIn在加载时间上节省了4%.
  • 删除无用代码. 与DevTools code coverage类似. 对于剥离的代码, 见 tree-shaking, Closure Compiler的高级优化和其它类似的插件,像 lodash-babel-plugin 或者 Webpack的 ContextReplacementPlugin. 使用babel-preset-env 和 browserlist 以避免转换已经存在于现代浏览器的一些特性. 资深开发者可能已经发现 analysis of their Webpack bundles 可以帮助去除那些不必要的依赖.
  • 缓存 优化脚本有效时间以及ETag来避免传输没有变化的数据. Service Worker缓存能帮助实现弹性网络,并且使你更早使用一些特性,像V8’s code cache. 同时也可以通过filename hashing学习持久化缓存.
Javascript开销(Cost)_第2张图片
1_8Spf9To8dzTG3Xy9s57oVA.png

解析/编译

一旦脚本下载完成了之后,JS最大的开销之一就是JS引擎的解析/编译代码。在Chrome开发者工具里的“性能”模块,解析和编译代码用黄色标识.

Javascript开销(Cost)_第3张图片
1__4gNDmBlXxOF2-KmsOrKkw.png

通过Bottom-Up/Call Tree(调用树),可以看实际的解析/编译用时:

Javascript开销(Cost)_第4张图片
1_GdrVt_BTTzzBOIoyZZsQZQ.png

但是,为什么这个很重要呢?

Javascript开销(Cost)_第5张图片
1_Dirw7RdQj9Dktc-Ny6-xbA.png

花费大量时间在解析/编译代码上会延迟用户与网站的交互,破坏用户体验。在网站呈现之前,JS量越大,花在解析/编译上的时间就越长。

Javascript开销(Cost)_第6张图片
1_6Y665hpxfWNMu2EXu3VGlw.png

对于大小相同的JS和图片或者网页字体,JS需要浏览器花费的时间最多— Tom Dale

与Javascript相比,处理相同大小的图片所需要的开销明显小很多。

Javascript开销(Cost)_第7张图片
1_PRVzNizF9jQ_QADF5lQHpA.png

当我们讨论解析和编译的速度之慢时,上下文是很重要的。我们的讨论是基于平均水平的移动设备。平均水平的用户使用的移动设备可能是CPU/GPU很慢的、没有L2/L3缓存的或者甚至内存很有限的。

网络和设备不总是匹配的。一个用户可能有很好的网络条件,但是只有一部烂手机。相反,一个用户可能有一部神机,却碰上了龟速网络 — Kristofer Baxter, LinkedIn

在JavaScript Start-up Performance文中, 我提到了分别在低端和高端机型中1MB原始JS代码的解析时间. 它们之间的差距达到了2-5倍.

Javascript开销(Cost)_第8张图片
1_8BQ3bCYu1AVvJWPR1x8Yig.png

那实际的网站如何呢,比如CNN.com?

在高端机iPhone 8上,解析/编译JS代码只需大约4s,而在平均水平的手机Moto G4上却要花上将近13s。这很明显得影响了用户能够多快看到界面。

Javascript开销(Cost)_第9张图片
1_7ysArXJ4nN0rQEMT9yZ_Sg.png

这就要求我们要更加注重一些平均水平的设备的测试,而不仅仅是自己口袋里的高端机。上下文是很重要的:一定要针对你的用户的设备和网络进行优化。

Javascript开销(Cost)_第10张图片
1_6oEpMEi_pjRNjmtN9i2TCA.png

可以通过 mobile device classes 来看看真实用户的分析情况。

我们真的是传输了太多的JS代码吗? 呃。。有可能 :)

使用HTTP Archive (top ~500K sites)来分析JS在移动设备上的使用情况JavaScript on mobile, 我们就能发现50%左右的网站需要14s以上才能真正让用户用上。这些网站光花在解析/编译JS上的时间就多达4s.

Javascript开销(Cost)_第11张图片
1_sVgunAoet0i5FWEI9NSyMg.png

介于以上这些情况,怪不得用户在还没有看到页面之前就离开了。我们当然可以在这点上做得更好。

删除一些非必要JS代码能有效减少转换时间、CPU的解析/编译时间以及内存占用。同样也能是用户更快得与网站交互。

执行时间

当然,编译和解析只是JS开销的一部分。执行JS代码也是主线程上必须要做的,如果执行时间冗长,也会直接影响用户体验。

Javascript开销(Cost)_第12张图片
1_ec0wEKKVl7iQidBks3oDKg.png

一旦脚本执行时间超过50ms,后果不堪设想 — Alex Russell

为了减少执行时间,你可以将JS代码分离成一块块的,以免阻塞主线程。

设计模式

有时候一些设计模式能够帮助你,比如基于路由的代码分块 (route-based chunking) 或者PRPL.

如下图所示,PRPL就是一个利用代码分离和缓存方式来优化交互体验的模式:

Javascript开销(Cost)_第13张图片
1_VgdNbnl08gcetpqE1t9P9w.png

让我们来看看这个影响.

我们使用V8的 Runtime Call Stats 分析了一些主流网站以及PWA的加载时间。可以看到,解析时间在整个加载时间中占了可观的部分:

Javascript开销(Cost)_第14张图片
1_9BMRW5i_bS4By_JSESXX8A.png

Wego,是使用了PRPL的一个站点,让每个路由都保持很少的解析时间,使得用户能更快得与网站交互。上图中的很多网站也是采用了代码分离和性能预算 (performance budget) 来尝试降低JS开销。

其它开销

JS也会在其它方面影响页面性能:

  • 内存。页面可能会因为垃圾回收而导致频繁的闪烁或暂停。当浏览器回收内存的时候,JS执行就会暂停。这就导致了当浏览器频繁回收垃圾时,JS执行的暂停频率就会比我们想象中的更多。避免内存泄漏和频繁的垃圾回收能够是页面更稳定。

  • 在运行时,如果JS运行时间过长就会阻塞主进程,导致页面无法交互。把这些任务分成一小块一小块 (可以采用 requestAnimationFrame() 或者 requestIdleCallback() 或者 scheduling) 能够最小化其带来的影响。

渐进式 Bootstrapping

为了让网站更快呈现在用户面前,许多网站会使用服务器端渲染来实现,然后在页面返回后通过绑定事件来“升级”它。

当心 -- 这种方式也有它的开销。一方面传输回来的HTML比较大,另一方面用户必须等到JS处理完毕才能真正与页面进行交互。

渐进式 Bootstrapping 可能是一种更好的方法。先发送一部分功能性页面(只是当前路由需要的HTML/JS/CSS)回来。当更多的资源传输回来时,页面就会进行懒加载并且解锁更多的功能。

Javascript开销(Cost)_第15张图片
1_zY03Y5nVEY21FXA63Qe8PA.png

加载当前页面的代码实在是非常好的方法。PRPL和渐进式 Bootstrapping 就是能帮助实现这点的模式。

结论

在网络不佳的情况下,传输数据的大小是至关重要的。对于CPU不给力的设备,解析时间是很重要的。

参考 Alex Russell 的 “Can You Afford It?: Real-world Web Performance Budgets”。

Javascript开销(Cost)_第16张图片
1_U8PJVNrA_tYADQ6_S4HUYw.png

如果你正在搭建一个基于移动设备的站点,尽量在典型的设备上开发。减少JS解析/编译时间,采用Performance Budget让团队成员都能检测JS开销。

硬广

这是本人的前端技术小程序,基本上所有的文章都会同步更新在小程序中。欢迎大家来凑热闹。

Javascript开销(Cost)_第17张图片
image

Javascript开销(Cost)_第18张图片
image

你可能感兴趣的:(Javascript开销(Cost))