第三方脚本会影响性能,因此,请务必定期审核这些脚本并使用高效的加载方法。此 Codelab 介绍了如何优化第三方资源的加载。该课程涵盖以下技巧:
延迟脚本加载
延迟加载非关键资源
预先连接到所需的源
随附的示例应用具有一个简单的网页,其中包含来自第三方来源的三项功能:
视频嵌入
用于渲染折线图的数据可视化库
社交媒体分享 widget
您首先要衡量应用的性能,然后应用每种分析法来提升应用性能的不同方面。
衡量性能
首先在全屏视图中打开示例应用:
- 点击 Remix to Edit 使项目可修改。
- 如需预览网站,请按查看应用,然后按全屏 。
对该页面运行 Lighthouse 性能审核,以确定基准性能:
- 按 `Control+Shift+J`(在 Mac 上,则按 `Command+Option+J`)打开开发者工具。
- 点击 Lighthouse 标签页。
- 点击移动设备。
- 选中效果复选框。(您可以在“审核”部分中取消选中其余复选框。)
- 点击 Simulated Fast 3G, 4x CPU Slowdown。
- 选中清除存储空间复选框。
- 点击运行审核。
当您在计算机上运行审核时,确切结果可能会有所不同,但您应该会注意到首次内容绘制 (FCP) 时间非常长,而 Lighthouse 建议调查两个机会:消除阻塞渲染的资源和预连接所需的源站。(即使指标均显示为绿色,优化仍会提升成效。)
推迟第三方 JavaScript
“消除阻塞渲染的资源”审核确定,您可以通过推迟来自 d3js.org 的脚本来节省一些时间:
D3.js 是一个用于创建数据可视化图表的 JavaScript 库。示例应用中的 script.js
文件使用 D3 实用函数来创建 SVG 折线图,并将其附加到页面。此处的操作顺序很重要:script.js
必须在文档解析且 D3 库加载之后运行,因此,它包含在 index.html
中结束的 </body>
标记之前。
不过,D3 脚本包含在页面的 <head>
中,这会阻止对其余文档的解析:
将两个魔法属性添加到脚本标记后,可以取消屏蔽解析器:
由于此图表对整个网页而言并不是非常重要,而且很可能在非首屏位置展示,因此请使用 defer
确保解析器不会阻塞。
第 1 步:使用 defer
属性异步加载脚本
在 index.html
的第 17 行,将 defer
属性添加到 <script>
元素中:
<script src="https://d3js.org/d3.v3.min.js" defer></script>
第 2 步:确保正确的操作顺序
由于 D3 被延迟,因此 script.js
将在 D3 准备就绪之前运行,从而导致错误。
具有 defer
属性的脚本会按照指定顺序执行。为了确保在 D3 就绪后执行 script.js
,请为其添加 defer
,并将其上移至文档的 <head>
,紧跟 D3 <script>
元素。现在,它不再阻塞解析器,并且会更快开始下载。
<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>
延迟加载第三方资源
非首屏的所有资源都非常适合进行延迟加载。
该示例应用在 iframe 中嵌入了 YouTube 视频。要查看网页发出了多少请求以及哪些请求来自嵌入式 YouTube iframe,请执行以下操作:
- 如需预览网站,请按查看应用,然后按全屏 。
- 按 `Control+Shift+J`(在 Mac 上,则按 `Command+Option+J`)打开开发者工具。
- 点击网络标签页。
- 选中停用缓存复选框。
- 在 Throttling(节流)下拉菜单中,选择 Fast 3G(快速 3G)。
- 重新加载页面。
网络面板显示,该网页一共发出了 28 个请求,并传输了近 1 MB 的压缩资源。
如需识别 YouTube iframe
发出的请求,请在 Initiator(发起方)列中查找视频 ID 6lfaiXM6waw
。如需按网域将所有请求分组在一起,请执行以下操作:
在网络面板中,右键点击某个列标题。
在下拉菜单中选择网域列。
若要按网域对请求进行排序,请点击网域列标题。
新的排序显示还有向 Google 网域发出的其他请求。YouTube iframe 总共发出 14 次脚本、样式表、图片和字体请求。但是,除非用户真正向下滚动来播放视频,否则他们并不需要所有这些素材资源。
等到用户向下滚动到网页的该部分时,可以延迟加载视频,从而减少网页最初发出的请求数量。这种方法可以节省用户数据,并加快初始加载速度。
实现延迟加载的一种方式是使用 Intersection Observer,后者是一种浏览器 API,可在元素进入或退出浏览器视口时通知您。
第 1 步:一开始阻止视频加载
要延迟加载视频 iframe,您必须先阻止以常规方式加载。为此,请将 src
属性替换为 data-src
属性,以指定视频网址:
<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,并为其命名。
- 点击添加此文件。
将脚本标记添加到文档标头:
<script src="/lazy-load.js" defer></script>
在 lazy-load.js
中,创建一个新的 IntersectionObserver
并向其传递要运行的回调函数:
// create a new Intersection Observer
let observer = new IntersectionObserver(callback);
现在,在 observe
方法中以实参的形式传递 observer
要观看的目标元素(在本例中为视频 iframe):
// 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
。
在此示例中,target
是 iframe
。当 target
进入视口时,isIntersecting
等于 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);
});
});
- 如需预览网站,请按查看应用,然后按全屏 。
- 按 `Control+Shift+J`(在 Mac 上,则按 `Command+Option+J`)打开开发者工具。
- 点击控制台标签页。
请尝试上下滚动。您应该会看到 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 步:重新评估效果
要查看资源大小和资源数量的变化情况,请打开开发者工具 Network 面板,然后再次重新加载页面。Network 面板显示,该网页发出了 14 个请求,只有 260 KB。这是一项非常有意义的改进!
现在,向下滚动页面,关注 Network 面板。到达该视频后,您应该会看到该网页触发了其他请求。
预先连接到所需的源
您推迟了非关键 JavaScript 并延迟加载了 YouTube 请求,现在是时候优化其余的第三方内容了。
为链接添加 rel=preconnect
属性会告知浏览器在请求某个资源之前与网域建立连接。此属性最适合用于提供您确定网页需要的资源的来源。
您在预先连接到必需的源中建议的第一步中进行了 Lighthouse 审核,您可以通过尽早连接到 staticxx.facebook.com 和 youtube.com,节省大约 400 毫秒的时间:
由于 YouTube 视频现在属于延迟加载状态,因此只会留下社交媒体分享微件的来源 staticxx.facebook.com。只需在文档的 <head>
中添加 <link>
标记,即可尽早与此网域建立连接:
<link rel="preconnect" href="https://staticxx.facebook.com">