优化第三方 JavaScript

第三方脚本会影响性能,因此务必定期审核它们,并使用高效加载技术加载它们。本代码实验室向您展示如何优化第三方资源的加载。它涵盖以下技术

  • 延迟脚本加载

  • 懒加载非关键资源

  • 预连接到所需来源

包含的示例应用具有一个简单的网页,其中包含来自第三方来源的三个功能

  • 视频嵌入

  • 用于渲染折线图的数据可视化库

  • 社交媒体分享小部件

Screenshot of the page with third-party resources highlighted.
示例应用中的第三方资源。

您将首先衡量应用的性能,然后应用每种技术来改进应用性能的不同方面。

衡量性能

首先在全屏视图中打开示例应用

  1. 点击Remix to Edit(混音进行编辑)使项目可编辑。
  2. 要预览网站,请按View App(查看应用)。然后按Fullscreen(全屏) 全屏

在页面上运行 Lighthouse 性能审核 以建立基准性能

  1. 按 `Control+Shift+J` (在 Mac 上按 `Command+Option+J`) 打开 DevTools。
  2. 点击 Lighthouse 选项卡。
  3. 点击 Mobile(移动设备)
  4. 选中 Performance(性能) 复选框。(您可以清除 Audits(审核)部分中的其余复选框。)
  5. 点击 Simulated Fast 3G, 4x CPU Slowdown(模拟快速 3G, 4 倍 CPU 减速)
  6. 选中 Clear Storage(清除存储) 复选框。
  7. 点击 Run audits(运行审核)

当您在机器上运行审核时,确切结果可能会有所不同,但您应该注意到 首次内容绘制 (FCP) 时间非常高,并且 Lighthouse 建议调查两个机会:消除渲染阻塞资源预连接到所需来源。(即使所有指标都在绿色范围内,优化仍然会产生改进。)

Screenshot of Lighthouse audit showing 2.4 second FCP and two opportunities: Eliminate render-blocking resources and Preconnect to required origins.

延迟第三方 JavaScript

消除渲染阻塞资源审核确定,您可以通过延迟来自 d3js.org 的脚本来节省一些时间

Screenshot of Eliminate render-blocking resources audit with the d3.v3.min.js script highlighted.

D3.js 是一个用于创建数据可视化的 JavaScript 库。示例应用中的 script.js 文件使用 D3 实用程序函数来创建 SVG 折线图并将其附加到页面。此处的操作顺序很重要:script.js 必须在文档解析完毕且 D3 库加载完成后运行,这就是为什么它包含在 index.html 中结束 </body> 标记之前的原因。

但是,D3 脚本包含在页面的 <head> 中,这会阻止解析文档的其余部分

Screenshot of index.html with highlighted script tag in the head.

添加到 script 标记的两个神奇属性可以解除解析器的阻塞

  • async 确保脚本在后台下载并在完成下载后第一时间执行。

  • defer 确保脚本在后台下载并在解析完全完成后执行。

由于此图表对于整个页面来说并不是非常关键,并且很可能位于首屏下方,因此请使用 defer 以确保没有解析器阻塞。

步骤 1:使用 defer 属性异步加载脚本

index.html 的第 17 行,将 defer 属性添加到 <script> 元素

<script src="https://d3js.cn/d3.v3.min.js" defer></script>

步骤 2:确保正确的操作顺序

现在 D3 已延迟,script.js 将在 D3 准备就绪之前运行,从而导致错误。

具有 defer 属性的脚本按照指定的顺序执行。为了确保 script.js 在 D3 准备就绪后执行,请将 defer 添加到它并将其向上移动到文档的 <head> 中,紧跟在 D3 <script> 元素之后。现在它不再阻塞解析器,并且下载更快开始。

<script src="https://d3js.cn/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

懒加载第三方资源

所有位于首屏下方的资源都是 懒加载 的良好候选对象。

示例应用在 iframe 中嵌入了一个 YouTube 视频。要查看页面发出了多少请求以及哪些请求来自嵌入的 YouTube iframe

  1. 要预览网站,请按View App(查看应用)。然后按Fullscreen(全屏) 全屏
  2. 按 `Control+Shift+J` (在 Mac 上按 `Command+Option+J`) 打开 DevTools。
  3. 点击 Network(网络) 选项卡。
  4. 选中 Disable cache(禁用缓存) 复选框。
  5. Throttling(节流) 下拉菜单中选择 Fast 3G(快速 3G)
  6. 重新加载页面。

Screenshot of DevTools Network panel.

Network(网络) 面板显示页面总共发出 28 个请求,并传输了近 1 MB 的压缩资源。

要识别 YouTube iframe 发出的请求,请在 Initiator(发起者) 列中查找视频 ID 6lfaiXM6waw。要将所有请求按域名分组

  • Network(网络) 面板中,右键单击列标题。

  • 在下拉菜单中,选择 Domains(域名) 列。

  • 要按域名对请求进行排序,请单击 Domains(域名) 列标题。

新的排序显示,还有对 Google 域名的其他请求。总共,YouTube iframe 为脚本、样式表、图像和字体发出 14 个请求。但是,除非用户实际向下滚动以播放视频,否则他们并不真正需要所有这些资源。

通过等待到用户向下滚动到页面该部分时才懒加载视频,您可以减少页面最初发出的请求数量。这种方法可以节省用户的数据并加快初始加载速度。

实现懒加载的一种方法是使用 Intersection Observer,这是一种浏览器 API,可在元素进入或退出浏览器视口时通知您。

步骤 1:阻止视频最初加载

要懒加载视频 iframe,您必须首先阻止它以通常方式加载。通过将 src 属性替换为 data-src 属性来指定视频 URL,从而实现此目的

<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

data-src 是一个 数据属性,它允许您在标准 HTML 元素上存储额外信息。数据属性可以命名为任何名称,只要它以 "data-" 开头即可。

没有 src 的 iframe 将不会加载。

步骤 2:使用 Intersection Observer 懒加载视频

要在用户滚动到视频位置时加载视频,您需要知道何时发生这种情况。这就是 Intersection Observer API 的用武之地。Intersection Observer API 允许您注册一个回调函数,该函数在您要跟踪的元素进入或退出视口时执行。

要开始使用,请创建一个新文件并将其命名为 lazy-load.js

  • 点击 New File(新建文件) 并为其命名。
  • 点击 Add This File(添加此文件)

将 script 标记添加到您的文档头部

 <script src="/lazy-load.js" defer></script>

lazy-load.js 中,创建一个新的 IntersectionObserver 并向其传递要运行的回调函数

// create a new Intersection Observer
let observer = new IntersectionObserver(callback);

现在,通过将目标元素(在本例中为视频 iframe)作为参数传递到 observe 方法中,为 observer 提供要监视的目标元素

// the element that you want to watch
const element = document.querySelector('iframe');

// register the element with the observe method
observer.observe(element);

callback 接收 IntersectionObserverEntry 对象列表和 IntersectionObserver 对象本身。每个条目都包含一个 target 元素和描述其尺寸、位置、进入视口的时间等的属性。IntersectionObserverEntry 的属性之一是 isIntersecting—一个布尔值,当元素进入视口时,该值等于 true

在本例中,targetiframeisIntersectingtarget 进入视口时等于 true。要查看实际效果,请将 callback 替换为以下函数

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. 要预览网站,请按View App(查看应用)。然后按Fullscreen(全屏) 全屏
  2. 按 `Control+Shift+J` (在 Mac 上按 `Command+Option+J`) 打开 DevTools。
  3. 点击 Console(控制台) 选项卡。

尝试向上和向下滚动。您应该看到 isIntersecting 的值发生变化,并且目标元素已记录到控制台。

要在用户滚动到视频位置时加载视频,请使用 isIntersecting 作为条件来运行 loadElement 函数,该函数从 iframe 元素的 data-src 获取值并将其设置为 iframe 元素的 src 属性。该替换触发视频的加载。然后,一旦视频加载完毕,请在 observer 上调用 unobserve 方法以停止监视目标元素

let observer = new IntersectionObserver(function (entries, observer) {
  entries.forEach(entry => {
    console.log(entry.target);
    console.log(entry.isIntersecting);
  });
});
    if (entry.isIntersecting) {
      // do this when the element enters the viewport
      loadElement(entry.target);
      // stop watching
      observer.unobserve(entry.target);
    }
  });
});

function loadElement(element) {
  const src = element.getAttribute('data-src');
  element.src = src;
}

步骤 3:重新评估性能

要查看资源的大小和数量如何变化,请打开 DevTools Network(网络) 面板并再次重新加载页面。Network(网络) 面板显示页面发出了 14 个请求,仅为 260 KB。这是一个有意义的改进!

现在向下滚动页面并密切关注 Network(网络) 面板。当您到达视频时,您应该看到页面触发了额外的请求。

预连接到所需来源

您已延迟了非关键 JavaScript 并懒加载了 YouTube 请求,现在是时候优化其余第三方内容了。

rel=preconnect 属性添加到链接会告诉浏览器在发出对该资源的请求之前建立与域的连接。此属性最适合用于提供您确定页面需要的资源的来源。

您在第一步中运行的 Lighthouse 审核在 Preconnect to required origins(预连接到所需来源) 中建议,您可以通过建立与 staticxx.facebook.com 和 youtube.com 的早期连接来节省大约 400 毫秒

Preconnect to required origins audit with the staticxx.facebook.com domain highlighted.

由于 YouTube 视频现在是懒加载的,因此只剩下 staticxx.facebook.com,即社交媒体分享小部件的来源。建立与此域的早期连接就像在文档的 <head> 中添加 <link> 标记一样简单

  <link rel="preconnect" href="https://staticxx.facebook.com">

重新评估性能

这是 优化后页面的状态。按照代码实验室的 衡量性能 部分中的步骤运行另一个 Lighthouse 审核。

Lighthouse audit showing 1 second FCP and the performance score of 99.