31

前端性能之JavaScript成本2018版

 5 years ago
source link: http://www.infoq.com/cn/articles/the-cost-of-javascript-in-2018?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

构建交互式网站通常涉及向用户发送大量的JavaScript代码。JavaScript是我们向手机端发送的最昂贵的资源,因为在很大程度上,它们会增加交互延迟。在这篇文章中,我们将介绍一些策略,用于在保持良好用户体验的前提下更高效地发送JavaScript。

JavaScript是有成本的

今天的网站通常会在他们的JS中发送以下内容:

  • 客户端框架或UI库
  • 状态管理解决方案(例如Redux)
  • Polyfill(现代浏览器通常并不需要它们)
  • 完整库或按需加载的内容(例如lodash、Moment和本地化资源)
  • 一组UI组件(按钮、标题、侧边栏等)

要加载的代码越多,页面加载时间就越长。

加载页面通常分为三个阶段,即加载是否在发生、加载的内容是否是有用的,以及加载的内容是否可用。其中最后一点涉及“交互性”问题。

交互式页面必须能够快速响应用户输入。当用户点击链接或滚动页面时,他们需要看到实际发生了什么。如果无法实现这一体验目标,你的用户就会感到沮丧。我们看到JavaScript影响到了很多类型网站的交互性。加载太多JavaScript会延迟可见元素的交互性,这对很多公司来说都是一个挑战。

可交互性会影响到很多东西。比如用户通过咖啡店WiFi或旅途中的间歇性网络连接来加载网页,而你的网页需要处理大量的JavaScript,这个时候用户需要等待页面呈现内容。或者即使页面元素已经呈现出来,但他们仍然需要等待很长时间才能与这些元素进行交互。理想情况下,减少发送JavaScript可以缓解这些问题。

以下是一些知名网站通过减少发送JavaScript来提升可交互性。

  • Pinterest将他们的JavaScript从2.5MB减少到200KB以下,Time-to-Interactive(TTI)从23秒减少到5.6秒。收入增长了44%,注册用户增长了753%,移动网络上的每周活跃用户增长了103%。
  • AutoTrader将他们的JavaScript大小减少了56%,并将页面的TTI缩短了约50%。
  • Nikkei将他们的JavaScript大小减少了43%,TTI缩短了14秒。

为什么JavaScript的成本如此之高

如果我们想要使用JavaScript,必须下载它,解析它,编译它,并执行它。如果你花费很长时间在JavaScript引擎中解析和编译脚本,就会增加用户的交互时间。下图是V8(Chrome的JavaScript引擎)在处理包含脚本的页面时花费时间的细分:

 8621-1534614333507.png 

橙色表示用于解析JavaScript所花费的时间,黄色是指编译的时间。把它们加起来,最多需要高达30%的时间用于处理页面的JavaScript——这些成本是真实存在的。

从Chrome 66开始,V8使用后台线程编译代码,将编译时间缩短了20%。但解析和编译仍然非常昂贵,并且很少看到大型脚本能够在50毫秒内执行完毕,即使是在线程外编译也是如此。

另外要注意,同样是200KB的脚本和200KB的图像所消耗的成本是不一样的。它们可能需要相同的时间来下载,但却具有不同的处理成本。

JPEG图像需要进行解码、栅格化并绘制到屏幕上。JavaScript在下载后进行解析、编译和执行,JavaScript引擎还需要完成其他的很多步骤。这些成本是不一样的。

移动设备频谱

廉价/低端、中端和高端设备组成了移动设备频谱。

如果我们足够幸运,可能会拥有一部高端或中端手机,但现实情况是并非所有用户都拥有这样的设备。

他们可能使用低端或中端的手机,这些设备之间也可能存在明显的差异——散热、高速缓存大小、CPU、GPU——根据设备的不同,最终处理JavaScript等资源的时间也不同。

以下是2018年可用硬件解析JavaScript所需时间的细分:

 6822-1534614332449.png 

随着时间的推移,Android手机会越来越便宜,而不是更快。这些设备通常配备较差的CPU,使用更小的L2/L3高速缓存。如果你希望他们都拥有高端硬件,那么普通用户该怎么办?

另外,如果你希望JavaScript变得更快,请注意在低端网络下的下载时间。你可以进行的改进包括:减少代码、缩小源代码、使用压缩(即gzip、Brotli和Zopfli)。

使用缓存来应对重复访问的内容。对于CPU速度慢的手机来说,解析时间至关重要。

如何发送更少的JavaScript

我们必须向用户发送最少量的脚本,同时仍然为他们提供良好的体验,而代码拆分是实现这种可能性的手段之一。

代码拆分就是不要向用户发送整个JavaScript包——有点像一块大型的披萨——如果一次只发送一小个片段会怎样?只要让当前页面的功能可用就可以了。

代码拆分可以是页面级别、路由级别或组件级别的。很多现代库和框架都通过Webpack和Parcel来实现代码拆分。React( https://reactjs.org/docs/code-splitting.html )、Vue.js( https://router.vuejs.org/guide/advanced/lazy-loading.html )和Angular( https://angular.io/guide/lazy-loading-ngmodules )提供了这方面的指南。

很多大型团队最近在代码拆分方面取得了巨大成功。

 623-1534614333811.jpeg 

为了确保用户能够尽早与网页进行交互,Twitter和Tinder采用了激进的代码拆分,把TTI减少了50%左右。

这些网站把JavaScript的审计分析作为工作流程的一部分。所幸的是,JavaScript生态系统提供了很多很棒的工具可用于分析JavaScript包。

我们可以使用Webpack Bundle Analyzer、Source Map Explorer和Bundle Buddy等工具来分析JavaScript包,寻找进行代码拆分的可能性。

这些工具可以对JavaScript包的内容进行可视化:它们突出显示大型的库、重复的代码和你可能不需要的依赖项。

 4624-1534614332073.png 

审计分析通常会突出显示重量级依赖项(如Moment.js及其语言环境),以便使用更轻量级的替代方案(例如date-fns)。

度量、优化、监控和重复

如果你不确定你的JavaScript性能是否存在问题,可以借助Lighthouse:

 525-1534614331222.jpeg 

Lighthouse是Chrome开发者工具中的一款工具,也可以作为Chrome扩展程序使用。它为你提供深入的性能分析,帮你找出性能问题。

你可以做的另一件事是确保不要将没有用的代码发送给你的用户:

 326-1534614331733.jpeg 

Code coverage是DevTools提供的一项功能,可用它发现页面中没有用到的JavaScript(和CSS)代码。在DevTools中加载页面,coverage选项卡将显示被执行的代码数量与加载的代码数量。你只需为用户提供必要的代码,这样就可以提高页面性能。

这对于找出可以进行拆分的脚本以及延迟加载非关键脚本来说非常有用。

PRPL(Push、Render、Precache和Lazy-Load)是一种模式,用于对每个路由进行代码拆分,然后利用Service Worker预先缓存未来路由需要用到的JavaScript和逻辑,并按需延迟加载。

这意味着当用户导航到其他视图时,它很可能已经存在于浏览器缓存中,因此启动脚本和获取交互方面的成本降低了很多。

如果你很关心性能,或者曾经为网站开发过性能补丁,那么你就会知道,有时候你修复一个问题,但几周之后,却发现其他人添加的功能无意中破坏了先前的经验。

所幸的是,我们可以尝试解决这个问题,比如制定性能预算。

性能预算定义了可度量的约束,让团队去实现性能目标。因为你不能突破预算的限制,所以每走一步都要考虑好性能,而不是事后再来解决。

性能预算的指标包括:

  • 里程碑时间——基于加载页面的用户体验的时间(例如TTI)。
  • 基于质量的指标——基于原始值(例如JavaScript的权重、HTTP请求的数量)。
  • 基于规则的指标——由Lighthouse或WebPageTest等工具生成的分数。

在性能预算工具方面,可以在Lighthouse中设置评分预算:

 2827-1534614332760.png 

有很多性能监控服务支持设置性能预算和预算警报,如Calibre、Treo、Webdash和SpeedCurve。

拥抱性能预算鼓励团队认真思考他们在设计阶段到里程碑结束所做出的任何决策将产生怎样的后果。

接下来……

每个网站都应该能够访问实验和生产环境的性能数据。

要想知道JavaScript可能对RUM(真实用户监控)的用户体验产生怎样的影响,我建议你检查一下两件事情。

 2228-1534614333164.png 

第一个是Long Tasks( https://w3c.github.io/longtasks )——一组API,帮助你收集真实世界中持续时间超过50毫秒并可能会阻塞主线程的任务。你可以将这些任务记录下来。

第二个是First Input Delay( https://developers.google.com/web/updates/2018/05/first-input-delay ),它用于度量从用户首次向网站发起交互(如当他们点击按钮时)到浏览器能够响应该交互的时间。FID是一个早期指标,不过现在已经有一个可用的polyfill,你可以尝试下。

众所周知,第三方JavaScript可能会对页面加载性能产生严重影响,但我们也不要忽略了自家JavaScript对性能的影响。如果我们要加快加载速度,需要同时消除双方对用户体验产生的影响。

我们看到了几个常见的漏洞,包括在文档头部使用JavaScript来决定向用户显示那个版本的A/B测试。或者将A/B测试所有的JS都发送给用户,但实际上只用了其中一个。

如果这是你目前遇到的主要瓶颈,我们还提供了一个有关加载第三方JavaScript的指南( https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript )。

总结

提升性能是一段长途旅行。很多小的变化可以带来巨大的收益。

让用户能够以最少的阻力与你的网站发生交互。运行最少量的JavaScript以提供真正的价值。这可能需要采取渐进的步骤来实现。

最后,你的用户会感谢你。

重要链接:

PRPL模式: https://developers.google.com/web/fundamentals/performance/prpl-pattern

Lighthouse: https://developers.google.com/web/tools/lighthouse/

Code coverage: https://developers.google.com/web/updates/2017/04/devtools-release-notes#coverage

Webpack Bundle Analyzer: https://www.npmjs.com/package/webpack-bundle-analyzer

Source Map Explorer: https://www.npmjs.com/package/source-map-explorer

Bundle Buddy: https://github.com/samccone/bundle-buddy

Calibre: https://calibreapp.com/

Treo: https://treo.sh/a/addyosmani/3

Webdash: https://webdash.xyz/

SpeedCurve: https://speedcurve.com/about/

First Input Delay: https://github.com/GoogleChromeLabs/first-input-delay

英文原文: https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4

感谢覃云对本文的审校。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK