likes
comments
collection
share

性能优化进阶:让你的移动端网页1s呈现

作者站长头像
站长
· 阅读数 31

前言

现在的消费者越来越依赖移动设备来访问内容和服务,这比以往任何时候都要求更高。当他们权衡您网站上的体验时,他们不仅将您与您的竞争对手进行比较,还会在使用完后对您的应用进行评级。

但是很多网站给用户带来的体验并不太好,以致造成潜在客户流失,所以,性能是留住用户的关键

Pinterest 将感知等待时间减少了 40%,这将搜索引擎流量和注册量增加了 15% 。原文(国外):https://medium.com/pinterest-engineering/driving-user-growth-with-performance-improvements-cfc50dafadd7

COOK 将页面平均加载时间减少了 850 毫秒,从而将转化次数提高了 7%,将跳出率降低了 7%,并将每个会话的页面增加了 10%。

BBC 发现他们的网站加载时间每增加一秒,他们就会失去 10% 的用户。原文(国外):https://www.creativebloq.com/features/how-the-bbc-builds-websites-that-scale

当 AutoAnything 将页面加载时间减少一半时,他们的销售额增长了 12% 到 13%。原文:https://www.digitalcommerce360.com/2010/08/19/web-accelerator-revs-conversion-and-sales-autoanything/

零售商 Furniture Village 审核了他们的网站速度,并制定了解决他们发现的问题的计划,导致页面加载时间降低了 20%,转化率提高了 10%。

在用户体验方面,速度至关重要。一项消费者研究表明,对移动速度延迟的压力反应类似于看恐怖电影或解决数学问题,而且比在零售店结账时排队等候的压力更大!

目前,4G 在全球移动网络中处于主导地位,所以我们会抱有以下预期:大部分用户都是在通过 4G 网络访问您的网页。因此,我们必须假设每项网络请求的平均耗时是 100 毫秒。

基于这项假设,我们现在不妨逆向思考一下。如果我们仔细分析浏览器与服务器之间的典型通信过程,就会发现网络本身已经消耗掉了 300 毫秒的时间:一次 DNS 查找(用于将主机名(比如 baidu.com)解析为 IP 地址)、一次网络往返(用于执行 TCP 握手)以及一次可选的 TLS 连接。所以,留给我们的时间就只有 700 毫秒了。

而要在 1 秒内提供并呈现首屏 (ATF) 内容,并让用户尽快开始与网页互动,我们需要明白这几个核心观点:

  • 服务器必须在200毫秒内呈现相应内容服务器响应时间就是在除去网络传输时间之后,服务器返回初始 HTML 所花费的时间。因为我们剩下的时间实在太少了,所以这个时间应该控制在最低限度:理想情况下应该保持在 200 毫秒以内,而且越少越好!
  • 应尽可能减少重定向次数 额外的 HTTP 重定向可能会增加一次或两次额外的网络往返(如果需要再次查找 DNS 的话就是两次),这在 4G 网络上将会导致数百毫秒的额外延迟。因此,我们需要尽可能减少重定向次数,而且最好完全消除重定向。尽可能避免重定向到“m.”网址。
  • 应尽可能减少首次呈现内容所需的网络往返次数鉴于 TCP 评估连接状况的方式(即 TCP 慢启动),新的 TCP 连接无法立即使用客户端和服务器之间的全部有效带宽。因此,在通过新连接进行首次往返的过程中,服务器最多只能发送 10 个 TCP 数据包(约 14KB),然后必须等待客户端确认已收到这些数据,才能增大拥塞窗口并继续发送更多数据。另外还需注意的是,10 个数据包 (IW10) 这一限值源自 TCP 标准的最近一次更新:我们应确保自己的服务器已升级到最新版本,以便能够充分利用这次更新。否则,这一限值可能会降低到 3-4 个数据包!考虑到 TCP 的这种行为,我们需尽可能减少为传输必要数据(以完成网页的首次呈现)而需进行的网络往返的次数。理想情况下,ATF 内容应小于 98KB,这样浏览器才能在 3 次网络往返之后即可显示网页内容,以便为服务器响应延迟和客户端呈现留出充足的时间预算。
  • 避免在首屏内容中包含会阻止内容呈现的外部 JavaScript 和 CSS 浏览器必须先解析网页,然后才能将其呈现给用户。如果浏览器在解析过程中遇到非异步或阻止呈现的外部脚本,则必须停止解析并且下载相应资源 所以用于呈现首屏内容的 JavaScript 和 CSS 应内嵌到网页中,而用于为网页增添附加功能的 JavaScript 或 CSS 应在 ATF 内容呈现完毕后再开始加载。
  • 为浏览器布局和呈现预留时间(200 毫秒)由于移动设备的运行速度和网页的复杂程度。我们至少需要为浏览器的开销预留 200 毫秒的时间。
  • 优化 JavaScript 的执行及呈现用时

如何在项目中进行优化

相信那些常见的优化方式,例如CDN,或优化代码和利用浏览器http缓存等老生常谈的东西,大家已经耳熟能详。在这里给大家介绍一些不同的东西,希望能给大家带来更多的认识,提升网站的性能。

选择正确的图像格式

图像格式一般分为矢量和光栅两种,这两种尤其在放大后有不同的表现。 性能优化进阶:让你的移动端网页1s呈现矢量格式非常适合由简单几何形状(如徽标、文本或图标)组成的图像。它们在每种分辨率和缩放设置下都能提供清晰的结果,是高分辨率屏幕和需要以不同尺寸显示同样效果的理想格式。

但是,当场景复杂时(例如,照片),矢量格式就会出现不足。例如SVG在这种情况下的标记量可能高得令人望而却步,并且输出可能仍然不“逼真”。所以就需要使用光栅图像格式,例如 PNG、JPEG、WebP 或 AVIF。

光栅图像不具有分辨率或缩放相同的特性——当放大光栅图像时,会看到锯齿状和模糊的图形。因此,我们需要以不同的分辨率保存多个版本的光栅图像,以便为用户提供最佳体验。

不同的光栅图像格式,所支持的特性也不同:性能优化进阶:让你的移动端网页1s呈现

用视频替换动画 GIF

不知道你们是否在开发工具中检查过GIF,可能会发现非常庞大。所以通过将大型 GIF 转换为视频,可以节省大量用户带宽。性能优化进阶:让你的移动端网页1s呈现可以看到,gif原为3.7M,转为mp4格式后551K,而转为webm后,仅有341K。

当图像出现在视图时才开始加载

如果你之前写过延迟加载代码,那么可能通过使用scroll 或 resize 之类的事件处理程序来完成。虽然这种方法在各大浏览器之间的兼容性最好,但现代浏览器提供了一种性能更高、效率更高的方法,通过 Intersection Observer API 来完成检查元素可见性的工作。

我们使用 JavaScript 来检查它们是否位于视区。若位于视区,则会向它们的 src (有时是srcset )属性填充那些指向所需图像内容的 URL。

<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">
document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  } else {
    // Possibly fall back to event handlers here
  }
});

以上是 img 标签的延迟加载方式, 如果用CSS background-image 属性(和其他属性)做延迟加载也是和以上同理。

当视频出现在视图时才开始加载

<video class="lazy" autoplay muted loop playsinline width="610" height="254" poster="one-does-not-simply.jpg">
  <source data-src="one-does-not-simply.webm" type="video/webm">
  <source data-src="one-does-not-simply.mp4" type="video/mp4">
</video>

延迟加载 video 元素时,和延迟加载图像一样,不同的是,视频需要迭代所有子 source 元素,并将其 data-src 属性改为 src 属性。完成后,需要通过调用元素的 load 方法来触发视频的加载,此后,媒体将根据 autoplay 属性开始自动播放。

  • 对于不自动播放的视频 需要在 <video> 元素上指定 preload 属性,preload="none";
  • 对于代替动画 GIF 的视频 有一点需要注意,要在 iOS 中自动播放,playsinline 必不可少;

尽可能减少浏览器重排

很多种操作HTML的行为 都可触发重排。例如:调整浏览器窗口的大小、使用涉及计算出的样式的 JavaScript 方法、在 DOM 中添加或移除元素,以及更改某个元素的类,等等。需要注意的是,这些操作导致的重排用时可能比想象的要长。

下面是一些简单的准则,可帮助我们尽可能缩短在网页中进行重排的用时:

  • 减少不必要的 DOM 深度。在 DOM 树中的一个级别进行更改可能会致使该树的所有级别(上至根节点,下至所修改节点的子级)都随之变化。这会导致花费更多的时间来执行重排。
  • 尽可能减少 CSS 规则的数量,并移除未使用的 CSS 规则。
  • 如果您想进行复杂的渲染更改(例如动画),请在流程外执行此操作。您可以使用 position-absolute 或 position-fixed 来实现此目的。
  • 避免使用不必要且复杂的 CSS 选择器(尤其是后代选择器),因为此类选择器需要耗用更多的 CPU 处理能力来执行选择器匹配。

@babel/preset-env

如果是@babel/preset-env7.10 或babel 8 以下,可以启动 bugfixes: true。babel将多个 JavaScript 语法功能分组到集合中,并根据指定的目标浏览器启用/禁用它们。虽然这很好用,但当目标浏览器只有一个特性的错误时,整个语法特性集合就会被转换。这通常会导致转换后的代码比必要的要多。所以可以通过此选项启用优化。

避免在浏览器中使用CommonJS

CommonJS起初为服务端设计,在一般情况下更难优化,因为它们比 ES 模块的动态程度更高。为确保捆绑程序和压缩器能够成功优化应用程序,请避免依赖 CommonJS 模块,并在整个应用程序中使用 ECMAScript 模块语法。

DNS-prefetch

当浏览器从(第三方)服务器请求资源时,必须先将该跨域域名解析为 IP 地址,然后浏览器才能发出请求。此过程称为 DNS 解析。

而 DNS 解析可以导致请求增加明显的延迟。对于打开了与许多第三方的连接的网站,此延迟可能会大大降低加载性能。

DNS 预获取是尝试在请求资源之前解析域名(这可能是后面要加载的文件,也可能是用户尝试打开的链接目标)。DNS 缓存可以帮助减少此延迟,

 <head>
    <link rel="dns-prefetch" href="https://fonts.gstatic.com/">
    <!-- and all other head elements -->
  </head>

可以看到,百度也采用了这种优化技术

性能优化进阶:让你的移动端网页1s呈现

注意事项:

  • dns-prefetch 仅对跨域域上的 DNS 查找有效,因此请避免使用它来指向本站点或域
  • 考虑将 dns-prefetch 与 preconnect(预连接)提示配对

    dns-prefetch 仅执行 DNS 查找,而preconnect 会建立与服务器的连接。如果站点是通过 HTTPS 服务的,则此过程包括 DNS 解析,建立 TCP 连接以及执行 TLS 握手。将两者结合起来可提供进一步减少跨域请求的感知延迟的机会

      <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
      <link rel="dns-prefetch" href="https://fonts.gstatic.com/">

    如果页面需要建立与许多第三方域的连接,预先连接会适得其反。 preconnect 提示最好仅用于最关键的连接。对于其他的,只需使用dns-prefetch。

    一些资源(如字体)以匿名模式加载。在这种情况下,应使用预连接提示设置 crossorigin 属性

设置Link标签优先级

  • <link rel="preload">通知浏览器需要一个资源作为当前导航的一部分,并且应该尽快开始获取它。
  • <link rel="preconnect">通知浏览器页面打算建立与另一个来源的连接,并且您希望该过程尽快开始。
  • <link rel="prefetch"> 和 <link rel="preload">有点不同,因为它不会让关键的事情发生得更快;相反,如果有机会,它会尝试让一些非关键的事情更早发生。

快速播放音频&视频预加载

播放开始得越快,意味着观看您的视频或收听您的音频的人越多。接下来将探讨一些实用的技术,可以使用这些技术主动预加载资源,来加速音频和视频播放。

性能优化进阶:让你的移动端网页1s呈现

  • 视频预加载属性

使用视频 preload 属性向浏览器提供有关要预加载多少信息或内容的提示。这意味着媒体源扩展 (MSE) 与 preload 不兼容。仅当初始 HTML 文档已完全加载并解析(例如 DOMContentLoaded 事件已触发)后才会开始获取资源,而在实际获取资源时才会触发与之不同的 load 事件。性能优化进阶:让你的移动端网页1s呈现将 preload 属性设置为 metadata 表示用户不需要视频,但需要获取其元数据(尺寸、曲目列表、持续时间等)。请注意,从 Chrome 64 开始,preload 的默认值是 metadata 。 (先前是 auto)。设置为 auto 表示浏览器可以缓存足够的数据,无需停止进一步缓冲即可完成播放。

不过也有一些需要注意的地方。由于这只是一个提示,因此浏览器可能会完全忽略 preload 属性。在撰写本文时,以下是在 Chrome 中应用的一些规则:

  1. 启用流量节省程序后,Chrome 会将 preload 值强制为 none 。
  2. 在 Android 4.3 中,由于 Android 缺陷,Chrome 会将 preload 值强制为 none。
  3. 在蜂窝连接(2G、3G 和 4G)上,Chrome 会将 preload 值强制为 metadata 。

如果网站在同一域中包含许多视频资源,建议将 preload 值设置为 metadata 或定义 poster 属性并将 preload 设置为none 。这样,可避免达到同一域的最大 HTTP 连接数(根据 HTTP 1.1 规范为 6 个),超过6个会挂起资源加载。如果视频不是页面的重点,设置后也会提高页面速度。

  • 链接预加载 链接预加载是一种声明性获取,可强制浏览器请求资源,而不会阻止 load 事件,同时页面也在下载。通过 <link rel="preload"> 加载的资源存储在本地浏览器中,并且在 DOM、JavaScript 或 CSS 中显式引用它们之前,它们实际上是静止的。 性能优化进阶:让你的移动端网页1s呈现

    以下是如何在您的网站上预加载完整视频的方法,这样当您的 JavaScript 要求获取视频内容时,它会从缓存中读取,因为浏览器可能已经缓存了该资源。如果预加载请求尚未完成,则会进行常规网络获取。

    // 如果 as 预加载链接值为 video,预加载资源就会被视频元素消耗。
    // 如果它是一个音频元素,即为 as="audio" 。
    <link rel="preload" as="video" href="https://cdn.com/small-file.mp4">
    
    <video id="video" controls></video>
    
    <script>
    // 随后,在满足某些条件后,将视频源设置为
    // 预加载视频 URL。
    video.src = 'https://cdn.com/small-file.mp4';
    video.play().then(() => {
      // 如果预加载视频 URL 已缓存,即会立即开始播放。
    });
    </script>
    
    // 建议仅将此法用于小型媒体文件(小于 5MB)。
    

优化第三方JavaScript

第三方 JavaScript 通常是指嵌入在您网站中的脚本,第三方脚本可以提供强大的功能,但它们还会影响隐私、安全和页面行为——而且它们对性能的影响尤其大。例如:

  • 触发额外的网络请求
  • 拉入未优化的图像和视频
  • HTTP缓存不足,导致频繁获取网络资源
  • 服务器资源压缩不足
  • 由不同的第三方嵌入引入的框架和库的多个实例

由于使用第三方 JavaScript 通常是不可避免的,但可以采取一些措施来最大程度地减少不利影响:

  • 在选择第三方资源时,请选择那些发送最少代码同时仍能提供所需功能的资源
  • 延迟加载第三方脚本
  • 不要使用来自两个不同供应商的相同功能。比如两个标签管理器或两个选择器。
  • 定期审核并清除多余的第三方脚本。
  • 自托管第三方脚本。

基于网络质量自适应

根据网络条件,加载网站可能是一种非常不同的体验。当使用快速网络时,一切通常都很顺利,但是当在旅途中使用有限的网络和不稳定的连接,这些情况下使用笔记本电脑时,情况就不同了。

可以通过多种方式来改善用户体验:

  • 根据用户的网络在提供高清和低分辨率内容之间切换。
  • 决定是否预加载资源。
  • 当用户连接速度较慢时,推迟上传和下载。
  • 如果网络质量不足以加载应用程序和使用功能,请启用离线模式。

我们可以通过浏览器提供的这个Api来实现:

性能优化进阶:让你的移动端网页1s呈现这是它的使用描述:性能优化进阶:让你的移动端网页1s呈现

然后配合navigator.connection.addEventListener('change', doSomethingOnChange); 实现自适应。

怎么测试网页速度?

  1. Lighthouse 可以通过获取一个 URL 并针对该页面运行一系列审核,生成有关该页面执行情况的报告。有多种运行 Lighthouse 的方法,包括从 Chrome DevTools 中安装插件来审核页面。
  2. PageSpeed Insights 提供有关页面的实验和现实场景的数据。它使用 Lighthouse 收集和分析有关页面的实验数据,而真实的现场数据基于 Chrome 用户体验报告数据集合。
  3. Chrome 开发者工具 是直接内置在 Google Chrome 浏览器中的 Web 开发者工具。允许分析页面运行时、识别和调试性能。

建立公司的性能文化

公司内的所有部门通常都依赖于用来转化用户的网站,无论是为了创收还是其他目的。但残酷的事实是:如果网页加载时间过长,人们是不会留下来查看它们的,无论网页多么漂亮。所以我们需要在公司建立起产品的性能文化。

以下是几点建议,可供参考:

  • 优化网站资源 开发人员可以使用各种媒体优化技术来加快媒体加载速度。UI 可以创建有助于将页面重量保持在约定水平以下的图像。 并致力于研究开发团队如何通过各种资源优化技术(例如资源压缩、响应式图像、图像大小调整、延迟加载、缓存和服务器优化)更快地加载显示资源。

    • 跟踪站点上的哪些页面具有最高的加载时间和最高的页面权重
    • 准备一份清单,列出哪些页面高于了其本身的权重水平,并进行改进。
    • 为设计师准备一份绩效预算,比如:所有页面的图像重量设置为 1 MB之内(如果品牌对转化非常重要,可以设置为 1.5 MB)
  • 在 Lighthouse 分数低于设置的阈值(例如 < 96/100)时不允许合并拉取请求
  • 提炼和精简业务 与网站速度相关的一个常见误解是,这只是开发团队的责任。现实情况是,一个快速的网站需要多个部门一起协作。
  • 定期跟踪提升速度后带来的转化,并及时改进

参考链接:https://developer.mozilla.org/zh-CN/docs/Web/Performance/dns-prefetchhttps://web.dev/fast/#prioritize-resourceshttps://developers.google.com/speed/docs/insights/mobile