译文出处,响应缓慢的网站会导致客户流失

作者: 前端  发布:2019-11-22

JavaScript 的代价

2018/08/31 · JavaScript · Javascript

原文出处: Addy Osmani   译文出处:开源中国   

9159.com 1

建立交互式网站包括向用户发送 JavaScript 。通常,太多了。你是否经历过在一个手机页面上,它看起来已经加载好了,但是点击一个链接或者试图滚动页面的时候,什么也没发生?

一字节又一字节,JavaScript 仍然是我们发送给手机的代价最大的资源,因为它会很大程度上延迟交互。

9159.com 2

由 WebPageTest(src) 评测的 CNN.com 的 JavaScript 处理时间。高端手机(iPhone8)在约4s的时间处理脚本。相比较而言,普通手机(Moto G4)是约13s的时间,以及2018年低端手机(Alcatel 1X)是约36s。

现在我们讨论一些策略,可以让你高效地传送 JavaScript ,同时给用户提供一个有价值的体验。

9159.com 3

9159.com 4

概括:

  • 要保持快速,则只加载当前页面必要的 JavaScript 。优先考虑用户需要的内容,然后使用代码拆分延迟加载剩下来的内容。这是快速加载和交互的最好的机会。默认情况下,基于路由的代码拆分堆栈是一个转折。
  • 接受性能预算,学会在预算中生活。对于手机来说,JS的预算目标为简化/压缩后小于170KB。未压缩时代码约为0.7MB。预算对成功至关重要,然而,他们单独不能神奇地修正 perf 数值。团队文化,结构和强制措施。没有预算的项目建立会导致性能退化并导致失败。
  • 学习如何审计并裁剪 JavaScript 捆绑库。当你只需要一小部分却搭载了整个库,浏览器不需要的填充字符,或者重复代码,这些很容易发生。
  • 每个交互都是一个新的“交互时间”的开始;考虑在这种情况下进行优化。传送数据的大小对低端手机网络至关重要,而且 JavaScript 解析时间受设备 CPU 限制。
  • 如果客户端 JavaScript 对用户体验没有好处,问问自己是否真的有必要。也许服务端渲染 HTML 会更快一些。考虑将客户端框架限制到绝对需要它们页面上的使用。如果做的不好,服务器渲染和客户端渲染都会是灾难。

(本文基于我最近的“JavaScript 的代价”的演讲:)

作者丨exAspArk

作者丨Ben Schwarz

在建立那些严重依赖于JavaScript网站的时候,有时我们会为自己发送的内容付出一些隐形的成本。在本篇文章中,我会介绍一些可以帮助你提升网站在移动设备上加载和运行速度的实用规则。

网站因用户“体验”而膨胀

当用户访问网站,你可能正在下载大量文件,其中很多都是脚本。从给一个web浏览器的角度来看有点像这个:

9159.com 5

扔给你一大堆文件

虽然我很喜欢JavaScript,但它总是网站中消耗最大的东西。我想解释一下为什么这是一个主要问题。

9159.com 6

现在中等的web页面搭载了大概350KB的简化或压缩后的JavaScript脚本。浏览器需要处理的未压缩的脚本膨胀到了超过1MB。

注:不确定你的JavaScript包是否延迟了用户与网站交互速度?查看Lighthouse

9159.com 7

2018年7月的HTTP压缩状态的JavaScript报告中的统计突出显示了中等web页面搭载了约350KB的简化或压缩后的脚本。这些页面要花15s才能交互。

在移动设备上,搭载这么多的JavaScript脚本从经验来看要花费超过14+秒才能加载并交互。

其中的一个很大的因素是在移动网络中下载代码,然后再移动设备CPU上处理它,这个过程所花费的时间。

我们来看移动网络。

9159.com 8

在某一指标上表现较好的国家,颜色较深。不包括在内的国家是灰色的。还有值得注意的是,即使在美国,农村的宽带速度要比城市慢20%。

这个来自OpenSignal的图表展示了全球4G网络的稳定性,以及每个国家的用户体验到的平均连接速度。正如我们看到的,很多国家的连接速度仍比我们想象中要慢。

不仅中型网站的350KB的脚本要花上一段时间才能下载,事实上,如果我们浏览热门网站,实际上会加载比这更多的脚本:

9159.com 9

Facebook.com和其他网址相关数据”中的未压缩的JS包大小数据。像谷歌表格这样的网站被突出显示为最多加载5.8MB的脚本(在解压缩后)。

我们在桌面和移动web上都遇到了这个瓶颈,这些网站有时会加载几兆字节的代码,然后浏览器需要处理这些代码。问题是,你能负担得起这么多JavaScript脚本吗

译者丨姚佳灵

译者丨核子可乐

tl;dr:更少的代码 = 更少的解析/编译 (parse/compile)+ 更少的传送 + 更少的解压缩

JavaScript 有代价

9159.com 10

“含有这么多脚本的网站根本不能送达全球的诸多用户;统计表示,用户不会(以后也不会)等待它们加载” — Alex Russell

注:如果你使用了大量的脚本,应该考虑使用 code-splitting 对其进行分解,或者使用 tree-shaking 技术减少 JavaScript 的加载量。**

现代网站通常会通过 JS 包发送下面这些东西:

  • 客户端框架或 UI 库
  • 状态管理(比如 Redux)
  • Polyfills(一般现代浏览器不需要他们)
  • 完整的库或仅用到的部分(比如 lodash 完整库、Moment + 其本地库)
  • 一套 UI 组件(按钮、顶部、侧边栏等)

累积起来的代码越来越多,页面加载的时候也就越来越长。

加载网页就像电影胶片一样,有三个关键时刻。

即:是否发生?是否有用?是否可用?

9159.com 11

加载是一个过程。我们正逐渐开始关心用户的良好体验。我们不再盯着 onload 和 domContentLoaded,而是会问“用户什么时候才能正常使用页面?”如果用户点击用户界面中L的o某个地方,是否有所反馈?

是否正在发生是指屏幕上开始显示某些内容。(导航开始了吗?服务器在响应吗?)

是否有用 指文本或内容显现之后,用户是否通过体验或参与感受到价值。

还有是否可用是指用户可以根据经验开始交互并发生一些事情。

我之前也提到过这个术语:“交互”,它到底是什么意思呢?

9159.com 12

交互时间的可视化强调,不好的体验会让用户认为他们能达到某个目标,但实际上页面还没有加载完要达到这个目标所需要的代码。感谢 Kevin Schaaf 的关于交互的动画

对于要交互的页面,它必须能够快速响应用户输入。较小的 JavaScript 可以保证快速响应。

无论用户点击链接还是滚动页面,他们都需要看到有反馈他们动画的事情发生。如果做不到这样的体验,用户就会感沮丧。

9159.com 13

灯塔有一系列以用户为中心的性能指标,比如在实验设置中的交互时间。

通常发生的地方是当服务端在渲染的过程中,下载一堆“溶入”界面的 JavaScript(添加事件处理函数和其它行为)。

浏览器可能会在处理用户输入的线程上运行许多可能需要处理的事件。这个线程称为主线程。

在主线程上加载太多 JavaScript(通过 <script> 等)会是个问题。把脚本加载到  Web Worker 或者由 [Service Worker]() 处理脚本会减轻这些与交互时间相关的负责影响。

(这里有一个用户点击 UI 的例子。通常,用户勾选复选框,或者点击链接,一切都很美好。但如果我,们模拟阻塞主线程,就什么事都不会发生了。用户无法勾选复选框,也不能点击连接,因为主H线程被阻塞了:)

应该尽可能地避免阻塞主线程。了解更多内容,请看 “为什么 Web 开发者需要关心交互性

我们看到我们合作的团队遭受JavaScript影响了许多类型网站的交互性。

9159.com 14

JavaScript可以延迟可见元素的交互性。可视化是Google搜索中的一些UI元素

太多(主线程)JavaScript可以延迟可见元素的交互性。这对许多公司来说都是一个挑战。

以上是Google搜索中的一些示例,您可以在这些示例中开始使用UI,但如果某个网站运行过多的JavaScript,则可能会在实际发生某些事情之前出现延迟。这会让用户感到有点沮丧。理想情况下,我们希望所有体验尽快互动。

9159.com 15

通过WebPageTest和Lighthouse衡量news.google.com的互动时间(来源)

通过衡量Google新闻在移动设备上的交互时间,我们观察到大约7s的高端交互与低端设备在55秒内实现交互的巨大差异。那么,什么是交互性的良好目标?

9159.com 16

谈到Time to Interactive,我们认为您的基准应该是在中等移动设备上的慢速3G连接上以五秒钟的速度进行交互。“但是,我的用户都在使用快速网络和高端手机!”……是吗?你可能会使用“快速”的咖啡店WiFi,但实际上只能获得2G或3G的速度。多变性问题

谁传输更少的 JavaScript 以减少响应时间?

9159.com 17

  • Pinterest 将 JavaScript 包从 2.5MB 减小到 < 200KB,响应时间从 23 秒减少到 5.6 秒。收益提升了 44%,注册人数上升了 753%,移动网站的是周活跃人数提升了 103%。
  • AutoTrader 将他们的 JavaScript 包大小减小了 56%,响应时间减少了约 50%。
  • Nikkei 将 JavaScript 包大小减小了 43%,响应时间提升了 14 秒。

我们来设计一个更具弹性的 Web,它不依赖于加载巨大的 JavaScript。

交互性会影响很多东西。它可能会影响网站的移动数据规划,或者咖啡厅的 WiFi,或者他们I只是伴随着时断时续的连接。

9159.com 18

这些事情发生的时候,你有一大堆的 JavaScript 需要运行,用户可以放弃等待网站的渲染。另外,如果有东西在渲染,也需要等待大量的时间才可以交互。理想情况下,较少的 JavaScript 可以减轻这些问题。

最近,我们把 Universe.com 的主页性能提高了 10 几倍。让我们一起来探索一下我们是如何实现这个结果的,涉及到了哪些技术。

众所周知,速度已经成为各类网站增加收入并提升访客驻留率的核心因素。如今,谷歌也开始将页面速度纳入排名因素考量,这直接促使更多组织将注意力集中在性能身上。本文,我们将探讨 PageSpeed 如何计算最重要的这项指标:速度得分。

网络

为什么 JavaScript 如此昂贵?

为了解释 JavaScript 会有如此之大的代价,我想告诉你在内容发送到浏览器之后发生了些什么。当用户在浏览器的地址栏输入 URL:

9159.com 19

请求会被发送到服务器,然后服务器会返回一些标记。接着,浏览器解析这些标记,并找到必不的可少的 CSS、JavaScript 和图片。然后,浏览器还得获取并处理所有这些资源。

上面的动画准确描述了 Chrome 在处理你发送的所有内容时要干的事情(确实,它是一个巨大的表情工厂)。

这里存在一个挑战:JavaScript 最终会成为瓶颈。设想,我们希望能快速画出每一个像素,然后让页面可以交互。但是,如果 JavaScript 成为瓶颈,你最终只能看到东西却不能交互。

我们希望防止 JavaScript 为成现代体验的瓶颈。

要记住一点,作为一个流程,如果我们想要 JavaScript 变得更快,我们就必须快速地下载、解析、编译并执行它。

9159.com 20

也就是说,我们必须保证快速的网络传输,以及处理关于脚本其它方面的事情。

如果你花很长的时间在 JavaScript 引擎中解析和编译脚本,就是延迟用户与你的体验交互的时间。

为了提供这方面的数据,这里有一个 V8(Chrome 的 JavaScript 引擎)在处理页面中脚本的时间分解数据:

9159.com 21

JavaScript 解析/编译 = 在页面加载时 10–30% 时间消耗在 V8 (Chrome 的 JS 引擎) 上

橙色代表了当下流行的网站消耗在解析 JavaScript 上的全部时间。黄色部分则是编译所消耗的时间。它们总计占用了处理页面上 JavaScript 30% 的时间 —— 这是真实的成本。

对于 Chrome66 来说,V8 在后台线程中编译代码,将编译时间减少了 20%。但是解析和编译的代价仍然很高,而且想要看到一个大型脚本执行时间少于 50ms,实属罕见,哪怕不在主线程编译。

另一个要知道的是 JavaScript 的所有字节都不等价。200KB 的脚本和 200KB 的图片所需要的代价差异很大。

9159.com 22

并非所有字节都是等价的。除开原始的网络传输成本,200KB 的脚本和 200KB 的 JPG 所需要的代价大相径庭。

他们的下载时间可能一样,但处理起来却需要不同的成本。

JPEG 图像需要解码、光栅化,然后绘制在屏幕上。JavaScript 需要下载,然后解析、编译、执行—— 还有大量需要引擎完成的其它步骤。请注意,他们的成本并不相同。

成本变得重要的原因之一是因为移动端。

一开始,我们先来看看,为什么网站性能如此重要(在本文末尾附有本案例研究的链接):

去年,谷歌公司对其搜索索引及排名算法做出了两项重大变更:

大多数开发人员考虑JavaScript成本的时候,考虑的都是下载和执行成本。通过线路发送的JavaScript字节越多,所需时间就越长,用户连接就越慢。

移动端是一个谱系。

9159.com 23

移动端是一个包含了便宜/低端、中端和高端设备的谱系

如果运气好,我们可能会遇到一个中高端的手机。但实事上并非所有用户都有这么好的设备。

用户使用的可能是中低端的手机,而且各类设备之间也存在极大的差异;热节流、缓存大小差异、CPU、GPU —— 最终,处理资源的时间会像处理 JavaScript 一样存在较大的差异,这些都取决于所使用的设备。使用低端手机的用户甚至可能就在美国。

9159.com 24

newzoo 发表了“对 23 亿 Android 智能手机的观察分析”。Android 在全球占有 75.9% 的市场A份额。预计 2018 年至少还有 3 亿的智能手机进入市场。其中有大量的 Android 设备。

下面是 2018 年各种硬件设备下对 JavaScript 解析时间的分析:

9159.com 25

处理 (解析/编译) 1MB 未压缩的 JavaScript (压缩和 gZip 处理后 < 200KB)所需要的时间,这些时间是在真实设备上手工测得。(来源)

最上面是像 iPhone8 这样的高端设备,处理 JavaScript 相对较快。下面是一些普通手机,比如 Moto G4 和低于 100 美元的 Alcatel 1X。注意到处理时间的差异了吗?

随着时间的推移,Android 手机越来越便宜,而不是更快。这些设置的 CPU 频率更高,L2/L3 缓存也小得可怜。如果你期待用户都使用高端设备,那么你的目标用户就会减少。

我们从一个真实的网站来实际看看这个问题。下面是 CNN.com 的 JS 处理时间:

9159.com 26

CNN.com 上的 JavaScript 处理时间,来自 WebPageTest (来源)

iPhone 8 (使用 A11 芯片) 在处理 CNN 的 JavaScript 比说通手机快 9 秒。这 9 秒可以让增强用户体验。慢得甚至需要特别说明:

9159.com 27

对于 CNN.com,这个大量使用 JavaScript 的网站,比较中低端硬件通过 3G 访问加载的情况(来源)。Alcatel 1X 花了 65秒 才完成加载。

这暗示我们应该停止使用快速的网络和快速的设备。

9159.com 28

有一些用户不会使用快速的网络,或者没有最新最好的手机,所以我们有必要从实际的手机和实际的网络环境开始测试。易变性确实是个问题。

“可变性是杀死用户体验的原因” –  Ilya Grigorik。快速设备实际上有时可能很慢。快速网络可能很慢,可变性最终会使一切变得缓慢。

当差异可能会破坏用户体验时,使用缓慢的基线进行开发可确保每个人(快速和慢速设置)都能获益。如果您的团队可以查看他们的分析并准确了解您的用户实际访问您网站的设备,那么您将会提示您应该在办公室中使用哪些设备来测试您的网站。

9159.com 29

    测试真实的手机&网络。

webpagetest.org/easy在“移动”配置文件下预先配置了许多Moto G4。如果您无法购买自己的中等级硬件进行测试,这将非常有用。

这里设置了许多配置文件,您可以使用这些配置文件已预先配置了常用设备。例如,我们有一些中等移动设备,如Moto G4准备测试。

在代表性网络上进行测试也很重要。虽然我已经谈到了低端手机和中位手机的重要性,但布莱恩霍尔特提出了这个观点:了解你的观众非常重要。

9159.com 30

“了解您的受众,然后适当地关注应用程序的性能至关重要” –  Brian Holt(src)

并非每个站点都需要在低端手机上的2G上表现良好。也就是说,在整个频谱范围内实现高水平的性能并不是一件坏事。

9159.com 31

谷歌分析>观众>移动设备>设备 可视化设备&操作系统访问您网站的位置。

您可能在频谱的较高端或频谱的不同部分拥有广泛的用户。请注意您网站背后的数据,以便您可以合理地调用所有这些内容。

如果您希望JavaScript快速,请注意低端网络的下载时间。您可以进行的改进包括:减少代码,缩小源代码,利用压缩(即gzip,Brotli和Zopfli)。

9159.com 32

利用缓存重复访问。对于CPU速度慢的手机,解析时间至关重要。

9159.com 33

如果您是后端或全堆栈开发人员,您知道您可以获得有关CPU,磁盘和网络的费用。

当我们构建越来越依赖JavaScript的网站时,我们有时会以我们并不总是容易看到的方式支付我们发送的内容。

用户体验:糟糕的性能导致无响应,从 UI 和 UX 角度来看,这可能会让用户感到沮丧。

去年 3 月,索引编目开始基于页面的移动版本,而非桌面版。

9159.com 34

怎样才可以发送更小的 JavaScript

无论如何,只要我们能让发送出去的 JavaScript 最小,同还还能让用户有较好的体验,那么成功就在眼前。Code-splitting 就是一个能实现这一愿望的选择。

9159.com 35

基于页面、路由或组件拆分巨大而完整的 JavaScript 包。如果“splitting”一开始就用在你的工具链中,你离成功就更近一步。

Code-splitting 的思想是这样的:不是向用户发送一个巨大的单个 JavaScript 文件 —— 就像一个巨大的披萨一样 —— 如果每次只给他们一块怎么样?只要有足够多片(但不是全部 —— 译者注)就能让当前页面跑起来。

Code-splitting 可以工作在页面、路由或组件级别。这对于很多现代的库和框架来说是件好事,这些库或框架可能通过 webpack 和 Parcel 生成脚本包。按照指南,可以将其用于 React、 Vue.js 和 Angular。

9159.com 36

在 React 应用中通过 React Loadable  添加 code-splitting。React Loadable 是一个高阶组件,它可以将动态导入封装在对 React  友好的 API 中,将 code-splitting 应用于给定的组件。

最近,很多大型团队看到了胜利背后的 code-splitting。

9159.com 37

基于希望用户能快速进入交互的需求,Twitter 和 Tinder 积极采用代码分割方法来重写他们的移动 Web 体验,其交互性能提高了 50%。

像 Gatsby.js (React)、Preact CLI 和 PWA Starter Kit,通过尝试,在普通的移动硬件上达到了快速加载以及快速入进交互的效果。

这些网站还做了一件事情,就是采用审查,使其成为工作流程的一部分。

9159.com 38

定期审查 JavaScript 包。像 webpack-bundle-analyzer 这样的工具非常适合用来分析构建出来的 JavaScript 包,以及可视代码的导入成本,这对于将本地化迭代工作流程错综复杂的依赖关系可视化非常有效。(比如,使用 npm install 导入包时)

值庆幸的是,JavaScript 生态系统中有大量优秀的工具可用于包分析。

像工具 Webpack Bundle AnalyzerSource Map Explorer 和 Bundle Buddy 允许你审核你的包用以减少包的数量。

这些工具可视化了JavaScript包的内容:它们突出显示您可能不需要的大型库,重复代码和依赖项等。

9159.com 39

来自Benedikt Rötsch的 “放下你的Webpack包,节制使用”  

打包审计通常会突出显示重要的依赖项(如Moment.js及其语言环境)的机会,以获得更轻的替代方案(例如 date-fns)。

如果你使用 webpack, 您可能会发现我们打的包中通用库问题在打包中很有用。

转化率和收入:通常,响应缓慢的网站会导致客户流失,并对转化率和收入产生不好的影响。

去年 7 月,SEO 排名算法迎来更新,将页面速度作为移动页面及广告的排名因素。

即使是在发达国家,这也可能是一个问题,因为用户实际上用的有效网络连接类型可能并不是3G、4G或者Wifi。表面上你可能连的是咖啡店的Wifi,但实际上连到的是只有2G速度的蜂窝热点。

衡量,调优,监控,然后重复。

如果你不确定你对JavaScript性能方面是否有什么问题,请查看Lighthouse:

9159.com 40

你可能没有注意到,Lighthouse最近添加了大量有用的新的性能审计功能。

Lighthouse是一个嵌入到Chrome开发工具的工具。它也可以作为Chrome扩展使用。它给你一个深入的性能分析来凸显提高性能的机会。、

9159.com 41

我们最近已经在Lighthouse添加了对高“JavaScript启动时间”标记的支持。这个审计工具突出显示那些可能花大量时间来解析/编译的脚本,这些脚本延迟了交互。你可以将这个审计工具看做一个机会,不是拆分脚本,就是在这里做更少的工作。

你还可以做的另外一件事就是确定你不会为你的用户加载无用代码:

9159.com 42

使用Chrome开发工具中的覆盖率标记来找到无用的CSS和JS代码。

代码覆盖率是开发工具中的一个特性,它可以让你发现页面中的无用JavaScript(以及CSS)。在开发工具中加载页面后,覆盖标记会显示执行了多少代码vs.加载了多少代码。你可以通过只加载一个用户需要的代码来提高页面性能。

9159.com 43

注:通过覆盖率记录,你可以与应用交互,然后开发工具会刷新用到了哪些包。

对于找机会来拆分脚本以及在需要的时候延迟加载非必要代码,这是有价值的。

如果你正在寻找有效地为用户提供JavaScript的模式,请查看PRPL模式。

9159.com 44

PRPL是高效加载的性能模式。它代表着:将关键资源推送(Push)到初始路由上,渲染(Render)初始路由,预缓存(Pre-cache)其他路由,延迟加载(Lazy-load)并按需创建剩余路由

PRPL (Push, Render, Precache and Lazy-Load) 是一种模式,用于为每条路径快速划分代码,然后利用服务[工作者](

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

如果你关心性能,或者你曾经为你的网站做过性能补丁,那么你知道有时你可能最终会遇到这样一类问题修正,几周之后回来才发现团队中的某个人正在处理的功能和无意中破坏了这类体验。它有点像这样:

9159.com 45

值得庆幸的是,我们可以尝试解决这个问题,一种方法是制定性能预算

9159.com 46

性能预算至关重要,因为他让每个人都在页面上。他们创造了分享热情的文化,不断改善用户体验和团队责任感。

预算通过定义可计算的约束来允许团队达到他们的性能目标。正因为你不得不生活在预算的约束之中,所以每一个步骤都需要考虑到性能,而不能事后再考虑。

基于Tim Kadlec的工作,性能预算的指标可包括:

  • 里程碑时间 — 基于加载页面的用户体验的计时(例如 Time-to-Interactive)。在页面加载时,您通常需要匹配多个里程碑时间来准确的呈现完整的故事。
  • 基于质量的测量— 基于初始值(例如 JavaScript的weight,HTTP请求的number)。这些都专注于浏览器体验。
  • 基于规则的测量— 由例如Lighthouse或者WebPageRest这样的工具生成分数。通常情况下,单个数字或者一些列的数据来评价您的网站。

9159.com 47

Alex Russell发表了一片关于预算性能的tweet-storm文章,其中有几点值得关注:

  • “领导的支持很重要. 为了保持整体用户体验的良好性,领导愿意将特征工作保持在对技术产品的深思熟虑的管理中。”
  • “性能是有关工具支持的文化。浏览器尽可能的优化了HTML+CSS。将您的工作更多的投入到JS中会为您的团队和所用的工具带来负担。”
  • “预算不会让你伤心。他们的存在使得组织能够自我纠正。团队需要预算来约束决策空间并帮助打击他们。”

影响网站用户体验每个人都与网站的性能有关。

9159.com 48

将性能作为讨论的一部分。

SEO:从 2019 年 7 月 1 日开始,谷歌将对所有新网站默认启用移动优先索引。如果网站在移动设备上的响应缓慢,并且没有适合移动设备的内容,那么它们的排名会降低。

由此,我们可以总结出两点可靠结论:

你可以通过以下的几种方式来降低JavaScript的网络传输成本:

性能更多是一种文化挑战,而不是技术挑战。

在规划会议和其它集会时讨论性能。询问商业项目关系人他们期望的性能是什么。他们是否明白性能是如何影响他们所关心的业务指标的?问开发团队他们如何规划定位性能瓶颈。当得不到满意答复的时候,他们开始讨论。

9159.com 49

“通过展示性能是如何影响项目关系人所关心的性能指标,来使性能与他们的目标相关联。若没有性能文化,性能将无法保证。”——Allison McKnight
性能行动规划如下:
创建性能视图。这是商业项目关系人和开发者达成共识的“良好性能”的协议。
设置性能预算。从视图中挑出关键性能指标(KPIs),并从中设置合理的,可计量的目标。例如:“5秒内加载并获取交互”。大小预算可能会从而消失。例如“将JS限制在简化/压缩后170KB的大小”。
建立关于KPIs的定期报告。这可以是一份定期发送给业务部门的报告,强调进展和成就。

Andy Still的《网络性能勇士》(Web Performance Warrior)和Lara Hogan的《为性能而设计》(design for Performance)都是很好的书,讨论了如何思考建立性能文化。

性能预算的工具怎样?你可以用Lighthouse CI项目持续集成设置Lighthouse评分预算:

9159.com 50

如果您的性能分数低于Lighthouse CI的规定值,可以拒绝那些合并的拉取请求。Lighthouse阈值是另一种基于配置的方法来设置性能预算。

许多性能监控服务支持设置性能预算和预算报警,包括Calibre、Treo、Webdash和SpeedCurve:

9159.com 51

我网站teejungle.net的JavaScript性能预算使用SpeedCurve,它支持一些列的预算指标。

接受性能预算可以鼓励团队来认真思考他们从设计阶段的早期到里程碑结束的任何决策的后果。

寻找进一步参考?美国数字服务部门通过为时间到交互等指标设定目标和预算,用Lighthouse记录他们跟踪性能的方法。

下一步..

每个站点都应该可以访问实验室和现场性能数据。

要跟踪JavaScript可能对RUM(真实用户监控)设置中的用户体验产生的影响,网络上有两件事情我建议您检查一下。

9159.com 52

现场数据(或RUM  – 真实用户监控)是从用户在野外经历的实际页面加载中收集的性能数据。拥有大量JavaScript负载的站点将受益于通过长任务和第一输入延迟测量此工作的主线程。

第一个是长任务 – 一个API,使您能够收集任务(及其属性脚本)的真实世界遥测,持续时间超过50毫秒,可能会阻止主线程。您可以记录这些任务并将其记录回分析。

第一输入延迟(FID)是衡量用户首次与您的站点交互时(即,当他们点击按钮时)到浏览器实际能够响应该交互的时间的度量。FID仍然是一个早期指标,但我们今天有一个可用的折叠,您可以查看。

在这两者之间,您应该能够从真实用户那里获得足够的遥测,以查看他们遇到的JavaScript性能问题。

9159.com 53

马塞尔·弗赖比希勒(Marcel Freinbichler)发布了一则面向欧盟用户的关于“今日美国”(USA Today)的病毒推文,用它比正常页面加载快42秒。

众所周知,第三方JavaScript可能会对页面加载性能产生严重影响。虽然这仍然是正确的,但重要的是要承认,今天的许多经验也带来了很多自己的第一方JavaScript。如果我们要快速加载,我们需要消除这个问题的双方可能对用户体验产生的影响。

我们在这里看到了几个常见的漏洞,包括团队在文档头部使用阻止JavaScript来决定向用户显示的A / B测试。或者,将所有A / B测试变体的JS运送下来,即使实际上只使用了一个。

9159.com 54

9159.com,如果这是您目前遇到的主要瓶颈,我们会另外提供有关加载第三方JavaScript的指南。

没有最快,只有更快

性能就像一段旅程,经过了许多小的变更的积累,最终获得大的性能提升。

让你的用户可以尽可能平滑的与你的网站进行交互,运行最少的JavaScript脚本来传递数据。你可以通过逐步递进的方法来慢慢实现这些。最终,你会得到用户的认可。

9159.com 55

参考:

  • Can You Afford It?: Real-world Web Performance Budgets
  • Progressive Performance
  • Reducing JavaScript payloads with Tree-shaking
  • Ouch, your JavaScript hurts!
  • Fast & Resilient — Why carving out the “fast” path isn’t enough
  • Web performance optimization with Webpack
  • JavaScript Start-up Optimization
  • The Impact Of Page Weight On Load Time
  • Beyond The Bubble — Real-world Performance
  • How To Think About Speed Tools
  • Thinking PRPL

非常感谢Thomas Steiner, Alex Russell, Jeremy Wagner, Patrick Hulce, Tom Ankers 和 Houssein Djirdeh 对文章的贡献,也非常感谢 Pat Meenan 在WebPageTest上的努力, 这里有一篇文章是关于WebPageTest的,有兴趣的话可以看一看,你也可以在Twitter上关注我。

 

1 赞 收藏 评论

9159.com 56

在本文中,我们将简要介绍帮助我们提高页面性能的以下几个主要方面:

站点在移动设备上的速度将影响整体扫描引擎优化排名。

  1. 只传送用户需要的代码。可用代码拆分(Code-splitting)。
  2. 优化压缩代码(ES5的Uglify,ES2015的babel-minify或者uglify-es)
  3. 高度压缩(用Brotli~q11,Zopfli或gzip)。Brotli的压缩比优于gzip。它可以帮CertSimple节省17%的压缩JS的字节大小,以及帮LinkedIn减少4%的加载时间。
  4. 移除无用的代码。用 Chrome DevTools代码覆盖率功能来查找未使用的JS代码。对于精简代码,可参阅tree-shaking, Closure Compiler的高端模式(advanced optimizations)和类似于 lodash-babel-plugin的微调库插件,或者像Moment.js这类库的Webpack的ContextReplacementPlugin。用babel-preset-env & browserlist来避免现代浏览器中已有的转译(transpiling)功能。高级开发人员可能会发现仔细分析Webpack打包(bundle)有助于他们识别和调整不必要的依赖关系。
  5. 缓存HTTP代码来减少网络传输量。确定脚本最佳的缓存时间(例如:max-age)和提供验证令牌(Etag)来避免传送无变化的字节。用Service Worker缓存一方面可以让应用程序网络更加灵活,另一方面也可以让你能够快速访问像V8代码缓存这样的功能。长期缓存可以去了解下Webpack带哈希值文件名(filename hashing)。

性能测量:实验室和现场测试工具。

如果页面加载速度较慢,则会降低广告质量得分,意味着广告成本将因此上升。

9159.com 57
(减少向用户发送JavaScript量的最佳做法。)

渲染:客户端和服务器端渲染,预渲染和混合渲染方法。

谷歌方面写道:

解析/编译

网络:CDN、缓存、GraphQL 缓存、编码、HTTP/2 和服务器推送(Server Push)。

速度更快的网站不仅能够改善用户体验。最近的调查数据显示,提高网站速度还有助于降低运营成本。像我们一样,每位用户都能够通过速度获得巨大的价值收益,因此我们决定将网站速度纳入搜索排名的考量当中。

下载成功后,JavaScript**绝大部分的时间都消耗在JS引擎对下载代码的解析/编译**上。在Chrome DevTools中,解析和编译是下面性能面板(Performance panel)中黄色“脚本”时间的一部分。

浏览器中的 JavaScript:包大小预算、代码拆分、异步和延迟脚本、图像优化(WebP、延迟加载、渐进)和资源提示(预加载、预取、预连接)。

为了从性能角度理解这些调整会对我们造成怎样的影响,首先需要理解其中的基础性技术。PageSpeed 5.0 可谓对以往版本做出了彻底颠覆,其现在由Lighthouse 以及CrUX(Chrome 用户体验报告)提供支持。

9159.com 58

针对某些情况,我们的主页是用 React(TypeScript)、Phoenix、Puppeteer(无头 Chrome)和 GraphQL API(Ruby on Rails )构建的。在移动设备上的界面如下所示:

此次升级还带来了一种新的评分算法,使得网站更难获得较高的 PageSpeed 得分。

Bottom-Up/Call Tree允许我们去确切地查看解析/编译所用时间:

9159.com 59

1

9159.com 60

Universe homepage 和 explore

PageSpeed 5.0 发生了哪些变化?

(Chrome DevTools性能面板下级菜单>Bottom-U。启动V8的Runtime Call Stats,就能看到不同阶段的时间消耗,比如解析/编译所用时间。)

1

在 5.0 版本之前,PageSpeed 会针对特定页面运行一系列启发式算法。如果页面中包含大量未经压缩的图像,则 PageSpeed 会建议进行图像压缩。如果缺少 Cache-Header,PageSpeed 也会建议立即添加。

但是,这为什么会是个问题?

性能测量

这些启发式方法还与系列指导方针相结合。当然,我们不能简单地认为只要遵循这些指导方针,就足以带来更理想的性能。因为其中并不涉及对真实访客所面对的负载与渲染体验进行实际分析。

9159.com 61

提供线索的编辑没有数据,只不过是空谈。—— W. Edwards Deming

在 PageSpeed 5.0 当中,页面会被加载至由 Lighthouse 控制的真实 Chrome 浏览器当中。Lighthouse 会记录来自浏览器的指标,利用评分模型进行指标分析,并给出整体性能得分。根据具体的指标评分方式,PageSpeed 即可给出改进建议。

耗费很长的时间在解析/编译代码上,会严重延迟用户与你网站的交互时间。你发送的JavaScript越多,在网站实现交互前所用的解析/编译的时间就会越长。

实验室测试工具(Lab instruments)

与 PageSpeed 类似,Lighthouse 同样提供性能评分。在 PageSpeed 5.0 当中,性能得分会直接被发送给 Lighthouse。这意味着 PageSpeed 的速度得分现在与 Lighthouse 性能得分保持一致。

9159.com 62

实验室测试工具允许在受控环境中,用预定义设备和网络设置采集数据。借助这些工具,调试任何性能问题和具有良好重现性的测试就变得更加简单。

现在,我们了解了 PageSpeed 的得分来源,下面开始深入了解具体得分的计算方法,以及如何对其进行有意义的改进。

即使是同样多的字节,浏览器处理JavaScript也会比处理等大小的图片和网页字体消耗更高的成本——Tom Dale

Lighthouse 是在本地计算机上审核 Chrome 页面的出色工具。它还提供一些关于如何提高性能、可访问性、SEO 等有用技巧。下面是一些模拟 Fast 3G 和 4 倍 CPU 减速的 Lighthouse 性能审核报告:

2

相比于JavaScript,处理等字节的图片所需要的时间成本很高(因为图片仍需要解码!)但是在一般的移动设备上,反而是JS更有可能对页面的交互产生负面的影响。

用 First Contentful Paint 提高 10 倍性能的前后对照

谷歌 Lighthouse 是什么?

9159.com 63

然而,只使用实验室测试工具的缺点是:它们不一定能发现真实世界的瓶颈问题,这些问题可能取决于终端用户的设备、网络、位置和很多其他因素。这就是为什么使用现场测试工具也很重要的原因。

Lighthouse 是一个由谷歌 Chrome 旗下专项团队负责运营的开源项目。过去几年以来,其已经逐步发展成一款免费的性能分析工具。

(JavaScript字节和图像字节耗费的时间成本不同。图像通常不会阻塞主线程,也不会在解码和光栅化的时候阻止接口进行交互。然而JS会因为解析、编译和执行的时间消耗阻滞交互性。)

现场测试工具(Field instrument)

Lighthouse 采用 Chrome 的远程调试协议读取网络请求信息、测量 JavaScript 性能、观察可访问性标准,并测量以用户为中心的各项时序指标,包括首次内容填充、交互时间或者索引速度等。

当我们说解析和编译的速度变慢的时候,要注意具体的网络端和设备端的情况,在这里我们针对的是普通手机。普通用户所使用手机的CPU和GPU速度比较慢,没有L2/L3缓存,甚至可能会有内存限制。

现场测试工具使我们可以模拟和测量真实的用户页面负载。有很多有助于从实际设备中获取真实性能数据的服务:

Lighthouse 如何计算性能得分

网络功能和设备功能并不总是相匹配的。有速度惊人的光纤连接的用户不一定会有最好的CPU来解析和评估发送到他们的设备的JavaScript。反过来也是如此…你可能有糟糕的网络连接,但却有快速的CPU。 – Kristofer Baxter,LinkedIn

WebPageTest——允许在不同位置的实际设备上执行来自不同浏览器的测试。

在性能测试过程中,Lighthouse 会记录下一系列与用户观感及体验相关的指标。

在JavaScript Start-up Performance一文中,我曾提到过在低端和高端硬件上解析~1MB解压缩过(简单)的JavaScript所需要消耗的时间。市面上的普通手机和运行速度最快的手机相比,解析/编译代码的所用的时间会有2-5倍的差距。

Test My Site——使用基于 Chrome 使用情况统计的 Chrome 用户体验报告(Chrome User Experience Report,简称 CrUX);它对公众开放,并每月更新一次。

总体性能得分的建立基于六项具体指标,分别为:

9159.com 64

PageSpeed Insights——结合了实验室(Lighthouse)和现场数据。

交互时间

(在不同级别的台式和移动设备上解析1MB的JavaScript包(经gzip压缩,大小约为250KB )。当分析解析成本时,我们需要考虑的是解压后的数据量,例如〜250KBgzip压缩过的JS解压缩后约为〜1MB的代码。)

9159.com 65

索引速度

那解析/编译真实网站的时间差异又会是怎样呢,比如CNN.com网站?

WebPageTest 报告

首次内容填充

在高端的iPhone 8上解析/编译CNN网站的JS大约花费了4秒,相比于普通手机(Moto G4)的13秒左右。这可以显著地影响用户与CNN网站实现完全交互的速度。

2

首次 CPU 空闲

9159.com 66

渲染

首次有意义填充

(苹果公司的A11仿生芯片和更普通的Android硬件中的Snapdragon 617的解析时间上的性能比较

渲染内容的方法有很多,每种方法都有其优缺点:

估算输入延迟

这就突出了在普通硬件(比如Moto G4)上测试的重要性,而不仅仅是在自己恰好有的手机上测试。基于自己客户原有的设备和网络条件来进行优化是很重要的。

服务器端渲染(Server-side rendering,简称 SSR)是在服务器端为浏览器获取最终 HTML 文档的过程。优点:搜索引擎可以爬取网站而不执行 JavaScript、快速初始页面加载、代码只存在于服务器端。缺点:没有丰富的网站交互、重新下载整个页面,对浏览器功能的访问有限制。

Lighthouse 会为以上各项指标打出 0 到 100 之间的得分。整个过程会从 HTTP Archive 当中提取第 75 与第 95 百分位的移动测量指标,而后应用一项 log normal 函数。

9159.com 67

客户端渲染是使用 JavaScript 在浏览器中渲染内容的过程。优点:丰富的网站交互、在初始下载后根据路由变化快速渲染、可以访问现代浏览器功能(如,Service Workers 的离线支持)。缺点:不支持 SEQ、初始页面加载速度慢、通常需要在服务器端执行单页面应用程序(Single Page Application、简称 SPA)和 API。

根据用于计算交互时间的算法与参考数据,我们可以看到,如果页面在 2.1 秒之内实现“交互”,则交互时间指标的标准得分为 92/100。

分析可以使你更加深入了解自己真实客户访问网站所用的移动设备的级别和这些设备CPU/GPU的局限性。

预渲染和服务器端渲染类似,但是它是在构建时期提前发生的,而不是在运行时发生的。优点:服务构建静态文件通常比运行服务器简单、支持 SEO、初始页面加载快。缺点:如果代码有任何变化,需要预先预渲染所有可能的页面、重新加载整个页面、站点交互不够丰富、访问浏览器功能受限。

9159.com 68

我们真的发送了太多的JavaScript了吗?呃…真有可能:)

客户端渲染

每项指标接受评分之后,Lighthouse 会为其分配一个权重,该权重作为总体性能得分计算中的调节器。具体权重如下所示:

用HTTP Archive(qian500K站点)来分析移动设备上JavaScript的状态时,我们可以看到,50%的站点需要14秒才能取得交互。这些网站光是用来解析和编译JS的时间就长达4秒。

之前,我们把我们的主页和 Ember.js 框架一起实现为具有客户端渲染的 SPA。我们遇到的一个问题是,Ember.js 应用程序包太大。这意味着,在浏览器下载、解析、编译和执行 JavaScript 文件时,用户只能看到一个空白的屏幕。

9159.com 69

9159.com 70

白屏

这些权重代表着每一项指标对移动用户体验造成的具体影响。

考虑到获取和处理JS和其他资源所耗费的时间,也就不奇怪用户可能需要在页面可用之前等待一段时间了。我们绝对可以在这个方面做的更好。

我们决定用 React 重建该应用程序的某些部分。

未来,大家还可以引入来自 Chrome 用户体验报告数据集的用户观察数据来进一步增强估算结果。

从网页中删除不必要的JavaScript可以减少传输时间、CPU密集型解析和编译以及潜在的内存消耗,同时也有助于加快网页的交互速度。

我们的开发人员已经熟悉构建 React 应用程序。

您可能希望了解每项指标的权重如何影响整体性能得分。Lighthouse 团队开发出一款实用的谷歌 Spreadsheet 计算器,用以解释整个过程:

执行时间

我们已经有了一些 React 组件库,可以在多个项目之间共享它们。

使用上述示例,如果我们将交互时间从 5 秒改为 17 秒(全局平均移动 TTI),那么,我们的得分将降至 56%(即 100 分制中的 56 分)。

不光是解析和编译会有时间成本。执行JavaScript(在解析/编译之后运行代码)也是需要在主线程上进行的操作之一。长的执行时间也会延迟用户与你网站的交互时间。

新页面有一些交互式 UI 元素。

如果我们将首次内容填充指标修改为 17 秒,那么得分将为 62%。

9159.com 71

有一个拥有大量工具的庞大的 React 生态系统。

交互时间 是性能得分当中最重要的一项指标。

如果脚本执行的时间超过了50ms,那么延迟交互的时间将会是下载、编译和执行JS所需时间的总和——Alex Russell

借助浏览器中的 JavaScript,可以构建具有大量良好功能的渐进式 web 应用程序(Progressive Web App)。

因此,要获得较高的 PageSpeed 得分,需要优先关注 TTI 速度水平。

为了解决这个问题,可以将JavaScript脚本分为几个小块来执行,以避免锁定主线程。探索一下是否可以减少脚本执行过程中进行中工作量的可能性。

预渲染和服务器端渲染

如何改善 TTI?

减少JavaScript交付成本的模式

例如,用 React Router DOM 构建的客户端渲染应用程序的问题, 仍然和 Ember.js 的相同。JavaScript 开销大,并且需要一些时间才能看到浏览器中的首次内容绘制(First Contentful Paint)。

从高级视角来看,有两个重要因素会对 TTI 产生直接影响:

当你试图降低JavaScript解析/编译和网络传输所用时间时,类似于基于路由分块和PRPL这些模式也会有用。

当我们决定使用 React 后,我们马上就用其它潜在的渲染选项进行试验,以让浏览器更快地渲染内容。

传递给页面的 JavaScript 代码量

PRPL是一种通过激进的(aggresive)代码分割和缓存来优化交互性的模式:

9159.com 72

主线程上 JavaScript 任务的运行时间

9159.com 73

使用 React 的常规渲染选项

我们在交互时间指南当中解释了 TTI 的具体工作原理,如果大家希望快速了解情况,我们将优化思路总结如下:

为了能将PRPL的影响以视觉化方式表现出来。

Gatsby.js 使我们可以用 React 和 GraphQL 预渲染页面。Gatsby.js 是个很棒的工具,它支持很多开箱即用的性能优化。然而,对我们来说,预渲染没有用,因为我们可能有无数个页面,它们包含用户生成的内容。

减少 JavaScript 代码量在可能的情况下,删除未使用的 JavaScript 代码或者确保仅交付当前页面需要运行的脚本。这可能意味着您需要删除旧的 polyfill,或者使用更小、更现代的第三方库作为替代。需要注意的是,JavaScript 代码带来的成本并不仅仅体现在下载时间上。浏览器需要对相关代码进行解压缩、解析、编译以及最终执行,这一切都会带来可观的处理时间,特别是在移动设备当中。

我们用V8引擎中的Runtime Call Stats分析了流行移动网站和progressive Web Apps(PWA)的加载时间。正如我们所看到的,解析部分(用橙色表示)是很多网站页面加载时产生显著时间消耗的部分。

Next.js 是流行的 Node.js 框架,它允许服务器端用 React 渲染。然而,Next.js 很自我,需要使用其路由、CSS 解决方案等等。我们现有的组件库是为浏览器而构建的,与 Node.js 不兼容。

下面来看减少页面中脚本数量的有效方法:

9159.com 74

这就是我们为什么决定尝试一些混合方法的原因,尝试从每个渲染选项中获得最佳效果。

审查并删除访客并不需要的 polyfills。

Wego网站就使用了PRPL来保持较低的路由解析时间,让页面交互得以快速的进行。以上的很多站点都试图采用代码分割和性能预算来降低JS的消耗。

运行时预渲染

了解各类第三方 JavaScript 的性能成本。使用 webpack-bundle-analyser 或者 source-map-explorer 对各个库的大小进行可视化。

JavaScript的其他消耗

Puppeteer 是个 Node.js 库,它允许使用无头 Chrome。我们希望让 Puppeteer 试试在运行时进行预渲染。这支持使用一种有趣的混合方法:服务器端用 Puppeteer 渲染,客户端用激活渲染。这里有一些谷歌提供的有用窍门,关于如何使用无头浏览器来进行服务器端渲染。

现代 JavaScript 工具(例如 Webpack)能够将大型 JavaScript 应用拆分成一系列小型捆绑包,这些捆绑包会在用户导航时自动加载。这种方法被称为代码拆分,且具有显着的 TTI 改进效果。

JavaScript还可以通过其他方式来影响页面性能:

9159.com 75

服务工作程序会缓存经过解析且编译的脚本的字节码结果。如果能够使用此项功能,那么访问者只需要付出一次性解析与编译成本,接下来相关结果都可以从缓存中直接调用。

  • 存储。页面可能会因为垃圾回收(GC,garbage colleciton),页面可能会出现画面中断卡顿(junk)和暂停。因为当一个浏览器回收内存的时候,JS的执行也会被中止,所以经常回收垃圾的浏览器会比我们想象中的更频繁地中止JS的执行。在这种情况下,可以通过避免内存溢出和频繁内存回收来保持页面的流畅。
  • 在运行时,长时间的运行JavaScript会阻塞主线程,导致页面没有响应。这种情况下,可以将脚本的工作量分成多个小的板块(具体可用requestAnimationFrame()或者requestIdleCallback()进行任务调度)来执行,以此减少页面响应的问题。

用于运行时预渲染 React 应用程序的 Puppeteer

监控交互时间

Progressive Bootstrapping

使用这种方法有如下优点:

为了成功发现用户体验中的重大差异,我们建议使用性能监控系统(例如 Calibre),其允许通过至少两台设备进行测试,包括一台调整台式设备与一台中低端移动手机。

很多网站将优化内容可视性作为保证交互性所需代价的一部分。为了在JavaScript有大体积包体时改善首屏性能,开发人员有时会先用服务器端渲染帮助客户提前看到页面内容,然后再在JavaScript最终执行完成后“升级”附加上事件处理程序。

可以使用 SSR,对 SEO 来说,这很棒。爬虫程序不需要执行 JavaScript 就能看到内容。

通过这种方式,开发者就能够得到最佳与最差情况下的客户体验数据。下面,我们以客户使用低性能硬件为例,聊聊如何进行体验优化。

但是值得注意的是,这样做也是有代价的。你1)通常会发送一个更大的HTML响应来增加交互性,2)在一段时间内,用户会处在一半的页面交互体验缺失的奇怪状况下,直到JavaScript处理完成。

允许构建简单浏览器 React 应用程序一次,然后把它用在服务器端和浏览器中。让浏览器应用程序更快地自动让 SSR 更快,这是双赢。

深入手动分析

Progressive Bootstrapping或许会是一个更好的处理方式。浏览器请求一个最少化的功能页面(仅由当前路由所需要的HTML/JS/CSS组成)。当有更多的资源请求的时候,应用程序则可以懒加载(lazy-load)和解锁更多的功能。

在服务器上用 Puppeteer 渲染页面通常比在终端用户的移动设备上更快(连接更好, 硬件更好)。

为了在分析 JavaScript 性能方面获得最佳结果,大家应该主动使用低性能设备进行页面测试。如果您的办公桌里恰好有一部旧手机,是时候让它再发挥一波余热了。

9159.com 76
Progressive Bootstrapping visual by Paul Lewis

激活允许用对 JavaScript 浏览器功能的访问来构建丰富的 SPA。

另有一种理想的真实设备替代方案,即使用 Chrome DevTools 硬件仿真模式。我们编写了一份广泛的性能分析指南,可帮助大家快速理解运行时性能。

仅加载可视区域内的代码是其中的关键。PRPL和Progressive Bootstrapping模式均可以用来实现这一点。

我们无需事先知道所有可能的页面来预渲染它们。

3

结论

然而,我们在使用这个方法时遇到了一些挑战:

其它指标处理

传输脚本的大小对低端网络至关重要,而解析时间对于CPU有局限性的设备很重要。降低传输脚本的大小和减少解析消耗时间是有必要的。

吞吐量是主要问题。在单独的无头浏览器进程中执行每个请求消耗了大量资源。你可以使用单个无标题浏览器进程,并在单独的选项卡中运行多个请求。然而,使用多个选项卡将会使整个进程的性能下降了。

接下来是索引速度、首次内容填充以及首次有意义填充,这些都是浏览器填充方面的指标。它们也受到类似因素的影响,而且一般能够同时接受优化。

有团队发现采用严格的性能预算可以成功降低他们JavaScript的传输和解析/编译的时间消耗。

9159.com 77

通过页面呈现的速度计算这些指标,能够在客观上降低指标的改进难度。另外,您可以根据 Lighthouse 性能审查规则对这些指标做出优化。如果尚未预加载字体或者针对关键请求进行优化,那么这两项工作无疑应该首先完成。

9159.com 78

使用 Puppeteer 进行服务器端渲染的体系结构

4

(考虑一下在我们所做的架构决策下,JS有多大的空间可以让我们的应用程序具有逻辑)

稳定性。扩展或缩小很多无头浏览器,让流程保持“热度”及平衡工作负载是个挑战。我们尝试了不同的托管方法:从 Kubernetes 集群自托管到用 AWS Lambda 和 Google Cloud Functions 的无服务器。我们注意到,后者在用到 Puppeteer 时有一些性能问题:

跟踪进度并进行有意义的改进

如果你正在建一个用于移动设备上的站点,请尽可能的在代表性硬件上开发,保持较低的JavaScript解析/编译的时间成本,并采用性能预算来确保团队对自身JavaScript的成本关注。

9159.com 79

谷歌最近更新了搜索控制台,Lighthouse 与 PageSpeed Insights 都是快速获得页面性能直观结果的好办法。但对于需要持续跟踪并改善网页性能的用户而言,其功能可能仍然不够理想。持续性能监控对于确保最终速度改进至关重要,并且能够在性能退化时立即向团队发送通知。手动测试则可能在结果当中引入意想不到的变化,而且如果没有专门的实验室环境,我们几乎不可能在不同区域及各类设备之上进行测试。

【编辑推荐】

在 AWS Lambdas 和 GCP 函数上的 Puppeteer 响应时间

速度已经成为搜索引擎优化排名中的关键因素,特别是考虑到目前近 50% 的网络流量都来自移动设备。为了避免排名下降,请确保使用最新性能套件跟踪关键页面(pssst,我们将 Calibre 打造成性能伴侣,其中内置有 Lighthouse。目前全球范围内数百支团队都在日常使用这款工具)。

随着我们越来越熟悉 Puppeteer,我们已经迭代了我们的初始方法。我们还进行着一些有趣实验,通过一个无头浏览器来渲染 PDF。还可以使用 Puppeteer 来进行自动端到端测试,甚至都不用写任何代码。现在,除了 Chrome,它还支持 Firefox。

混合渲染方法

点个在看少个 bug

在运行时使用 Puppeteer 很具挑战性。这是我们为什么决定在构建时使用它,并借助一个在运行时可以从服务器端返回实际用户生成内容的工具。与 Puppeteer 相比,它更稳定,并且吞吐量更大。

我们决定尝试一下 Elixir 编程语言。Elixir 看起来像 Ruby,但是运行于 BEAM(Erlang VM)之上,旨在构建容错且稳定的系统。

Elixir 使用 Actor 并发模型。每个“Actor”(Elixir process)只占用很少的内存,约为 1-2KB。这样允许同时运行数千个独立进程。Phoenix 是一个 Elixir web 框架,支持高吞吐量,并在独立的 Elixir 过程中处理每个 HTTP 请求。

我们结合了这些方法,充分利用了它们各自的优点,满足了我们的需要:

9159.com 80

Puppeteer 用于预渲染,而 Phoenix 用于服务器端渲染

Puppeteer 在构建时用我们希望的方式预渲染 React 页面,并以 HTML 文件形式保存它们(App Shell 来自 PRPL 模式)。

我们可以继续构建一个简单的浏览器 React 应用程序,不需要在终端用户设备上等待 JavaScript 就可以快速加载初始页面。

我们的 Phoenix 应用程序服务于这些预渲染页面,并动态地把实际内容注入到 HTML 中。

这让内容 SEO 变得很友好,允许根据需要处理大量不同的页面,并且更容易扩展。

客户端接收并立即显示 HTML,然后激发 Recat DOM 状态以继续作为常规 SPA。

这样,我们可以构建高度交互的应用程序,和访问 JavaScript 浏览器功能。

9159.com 81

使用 Puppeteer 进行预渲染、使用 Phoenix 进行服务器端渲染和激发使用 React 的客户端的体系结构

3

网络

内容分发网络

使用 CDN 可以实现内容缓存,并可以加速其在世界范围内的分发。我们使用 Fastly.com,它为超过 10% 的互联网请求提供服务,并为各种公司使用,如 GitHub、Stripe、Airbnb、Twitter 等等。

Fastly 允许我们通过使用名为 VCL 的配置语言编写自定义缓存和路由逻辑。下图显示了一个基本请求流的工作原理,根据路由、请求标头等等来自定制每个步骤:

9159.com 82

VCL 请求流

另一个提高性能的选择是在边缘使用 WebAssembly和 Fastly。把它想象成使用无服务器,但是在边缘使用这些编程语言,如 C、Rust、Go、TypeScript 等等。Cloudflare 有个类似的项目支持 Workers 上的 WASM.

4

缓存

尽可能多地缓存请求对提高性能很重要。CDN 级别上的缓存可以更快地为新用户提供响应。通过发送 Cache-Control 头来缓存可以加快浏览器中重复请求的响应时间。

大多数构建工具(如 Webpack)允许给文件名添加哈希值。可以安全地缓存这些文件,因为更改文件将创建新的输出文件名。

通过 HTTP/2 缓存和编码的文件

GraphQL 缓存

发送 GraphQL 请求最常见的方法之一是使用 POST HTTP 方法。我们使用的一种方法是在 Fastly 级缓存一些 GraphQL 请求:

我们的 React 应用程序注释了可以缓存的 GraphQL 请求。

发送 HTTP 请求前,我们通过从请求正文构建哈希值来附加 URL 参数,该请求正文包括 GraphQL 请求和变量(我们使用 Apollo Client 自定义 fetch)。

默认情况下,Varnish使用整个 URL 作为缓存键的一部分。

这允许我们继续在请求正文中使用 GraphQL 查询发送 POST 请求,并在边缘缓存,而不会访问我们的服务器。

发送带有 SHA256 URL 参数的 POST GraphQL 请求

以下是一些其它潜在的 GraphQL 缓存策略:

在服务器端缓存:整个 GraphQL 请求,在解析器级别上或通过注释模式声明性地进行缓存。

使用持久的 GraphQL 查询和发送 GET/graphql/:queryId 以便能够依赖 HTTP 缓存。

通过使用自动化工具(如 Apollo Server 2.0)或使用 GraphQL 特定的 CDN与 CDN 集成。

5

编码

所有主流浏览器都支持带有 Content-Encoding 头的 gzip 来压缩数据。这可以让我们给浏览器发送的字节更少,这通常意味着内容传递会更快。如果浏览器支持的话,你还可以使用更有效的 brotli 压缩算法。

HTTP/2 协议

HTTP/2 是 HTTP 网络协议(在 DevConsole 中是 h2)的新版本。切换到 HTTP/2 可以提升性能,这归结于它和 HTTP/1.x 的这些不同之处:

HTTP/2 是二进制的,不是文本。解析更高效,更紧凑。

HTTP/2 是多路复用的,这意味着 HTTP/2 可以通过单个 TCP 连接并行发送多个请求。它让我们不用担心每个主机限制和域分片的浏览器连接。

它使用头压缩来减少请求 / 响应大小开销。

允许服务器主动推送响应。该功能相当有趣。

HTTP/2 服务器推送

有很多编程语言和库并不完全支持所有 HTTP/2 功能,原因是它们为现有工具和生态系统引入了破坏性更改。但是,即使在这种情况下,仍然可以使用 HTTP/2,至少可以部分使用。如:

在常规 HTTP/1.x 服务器前使用 HTTP/2 设置代理服务器,如 h2o 或 nginx。例如 Puma 和 Rails 上的 Ruby 可以发送 Early Hints,这可以启用 HTTP/2 服务器推送,但受到一些限制。

使用支持 HTTP/2 的 CDN 提供静态资产。例如,我们用这种方法给客户端推送字体和一些 JavaScript 文件。

HTTP/2 推送字体

推送关键的 JavaScript 和 CSS 也可以很有用。只是不要过度推送,并提防某些陷阱。

6

浏览器中的 JavaScript

包大小的预算

提供线索的编辑第一条 JavaScript 性能规则是不要使用 JavaScript。我这么认为。

如果我们已经有现成的 JavaScript 应用程序,那么设置预算可以改进包大小的可见性,并让所有人都停留在同一个页面上。超预算迫使开发人员三思而后行,并把规模的增加控制在最小程度。关于如何设置预算,在此举几个例子:

根据我们的需要或一些推荐值使用数字。例如,小于 170KB 的缩小和压缩的 JavaScrip。

把当前的包大小作为基准,或尝试把它减少,例如 10%。

试试我们的竞争对手中最快的网站,并相应地设置预算。

我们可以使用 bundlesize 包或 Webpack 性能提示和限制来追踪预算:

Webpack 性能提示和限制

删除依赖项

这是由 Sidekiq 的作者所写的一篇热门博文的标题

提供线索的编辑没有代码能比没代码运行得更快。没有代码能比没代码有更少的错误。没有代码能比没代码使用更少提供线索的编辑的内存。没有代码能比没代码更容易让人理解。

不幸的是,JavaScript 依赖项的现实是,我们的项目很有可能使用数百个依赖项。试试 Is node_modules | wc -l。

在某些情况下,添加依赖项是必须的。在这种情况下,依赖项包的大小应该是在多个包之间进行选择时的标准之一。我强烈推荐使用 BundlePhobia:

9159.com 83

BundlePhobia 发现向包中添加 npm 包的成本

代码拆分

使用代码拆分可能是显着提高 JavaScript 性能的最佳方法。它允许拆分代码,并只传递用户当前需要的那部分。以下是一些代码拆分的例子:

在单独的 JavaScript 块中分别加载路由。

页面上可以不立即显示的组件,例如,在页面下方的模态、页脚。

在所有主流浏览器中,polyfills 和 ponyfills 都支持最新的浏览器功能。

通过使用 Webpack 的 SplitChunksPlungin,避免代码重复。

根据需要定位文件,以避免一次性发送所有我们支持的语言。

借助 Webpack 动态导入和具有 Suspense 的 React.lazy,我们可以使用代码拆分。

9159.com 84

借助动态引入和具有 Suspense 的 React.lazy 的代码拆分

我们构建了一个取代 React.lazy 的函数来支持命名导出,而不是默认导出。

7

异步和延迟脚本

所有主流浏览器支持脚本标签上的异步和延迟属性

9159.com 85

加载 JavaScript 的不同方法

内联脚本对于加载小型关键 JavaScript 代码非常有用。

当用户或任何其他脚本不需要该脚本,要获取 JavaScript 而不妨碍 HTML 解析时,使用带 async 的脚本非常有用。

从性能的角度看,要获取和执行非关键 JavaScript,并且不阻碍 HTML 解析,那么,使用带 defer 的脚本可能是最佳方法。

此外,它确保调用脚本时的执行顺序,如果一个脚本依赖另一个脚本,那么这个方法会很有用。

以下显示了在头标签中这些脚本之间的差异:

9159.com 86

脚本获取和执行的不同方法

8

图像优化

尽管 JavaScript 的 100KB 与图像的 100KB 相比,性能成本有很大的不同,但是,通常来说,尽量让图像保持比较小的文件大小很重要。

一种减小图像大小的方法是,在受支持的浏览器中使用更轻量级的 WebP 图像格式。对于那些不支持 WebP 的浏览器来说,可以使用以下策略:

退回到常规 JPEG 或 PNG 格式(一些 CDN 根据浏览器的 Accept 请求头自动执行)。

在检测到浏览器支持后,加载并使用 WebP polyfill。

使用 Service Workers 来侦听以获取请求,如果 WebP 受到支持,那么就更改实际的 URL 以使用 WebP。

WebP 图像

仅当图像在位于或接近视图端口时才延迟加载图像,对于具有大量图像的初始页面加载来说,这是最显着的性能改进之一。我们可以在支持的浏览器中使用 IntersectionObserver 功能,或使用一些可替换的工具来实现同样的结果,例如,react-lazyload。

9159.com 87在滚动期间延迟加载图像" style="width:60%;margin:1rem auto">

{"type":1,"value":"其他一些图像优化可能包括:

降低图像的质量以减少图像的尺寸。

调整大小并尽可能加载最小的图像。

使用 srcset 图像属性为高分辨率视网膜显示器自动加载高质量图像。

使用渐进式图像,先立即显示出模糊的图像。

9159.com 88

加载常规图像和渐进图像的对比

我们可以考虑使用一些通用 CDN 或专用图像 CDN,它们通常实现了这些图像优化的大部分工作。

9

资源提示

资源提示让我们可以优化资源的交付,减少往返次数,以及资源的获取,以便在用户浏览页面时更快地传递内容。

带有 link 标记的资源提示

预加载在当前页面加载的后台下载资源,并会实际用于当前页面。

预取的工作原理和预加载类似,都是获取资源并缓存它们,但用于未来用户的导航。

预连接(preconnect)允许在 HTTP 请求在实际发送到服务器之前,设置早期连接。

9159.com 89

提前预连接以避免 DNS、TCP 和 TLS 往返延迟

还有其他一些资源提示,如预渲染或 DNS 预取。其中有一些可以在响应头上指定。在使用资源提示时,请小心行事。很容易一开始就造成太多不必要的请求和下载太多数据,特别是如果用户在使用蜂窝连接。

10

结论

提供线索的编辑在不断增长的应用中,性能是永无止境的过程,该过程通常需要在整个栈中不断更改。

这个视频提醒我,大家希望减少应用程序包的大小——我的同事

9159.com 90把一切你现在不需要的东西都扔出飞机!——电影《珍珠港》" style="width:60%;margin:1rem auto">

{"type":1,"value":"以下是一个列表,表中是我们在使用或计划尝试的其他未提及的潜在性能改进:

使用 Service Workers 进行缓存、脱机支持及卸载主线程。

内联关键 CSS 或使用功能性 CSS,以便长期减小尺寸大小。

使用如 WOFF2 而不是 WOFF 的字体格式(最高可压缩一半大小)。

浏览器列表保持更新。

使用 webpack-bundle-analyzer 进行构建块的可视化分析。

优选较小的包(例如,date-fns)和允许减小尺寸大小的插件(如,lodash-webpack-plugin)。

试试 preact、lit-html 或 svelte。

在 CI 中运行 Lighthouse。

渐进激发和用 React 进行流处理。

提供线索的编辑令人兴奋的想法无穷无尽,我们都可以拿来尝试。我希望这些信息和这些案例研究可以启发大家去思考应用程序中的性能。

据亚马逊计算,页面下载速度每下降 1 秒就可能造成年销售额减少 13 亿美元。

沃尔玛发现,加载时间每减少 1 秒,将使转换量增加 2%。每 100ms 的改进还会带来高达 1% 的收入增加。

据谷歌计算,搜索结果每放慢 0.4 秒,那么每天的搜索次数有可能减少 8 百万次。

重构 Pinterest 页面的性能使等待时间减少了 40%,而 SEO 流量增加了 15%,注册转化率增加了 15%。

BBC 发现,其网站加载时间每增加一秒,就会多流失 10% 的用户。

对新的更快的 FT.com 的测试表明,用户参与度提高了 30%,这意味着更多的访问次数和更多的内容消费。

Instagram 通过减少显示评论所需 JSON 的响应大小,将展示次数和用户个人资料滚动互动量增加了 33%。

点个在看少个 bug

本文由9159.com发布于前端,转载请注明出处:   译文出处,响应缓慢的网站会导致客户流失

关键词:

上一篇:没有了
下一篇:没有了