改进整个 JavaScript 生态系统中的 Largest Contentful Paint。
在 Aurora 项目中,Google 一直在与热门 Web 框架合作,确保它们能根据 Core Web Vitals 指标提供出色的性能。Angular 和 Next.js 已经推出了内嵌字体,本文第一部分对此进行了介绍。我们将介绍的第二项优化是关键 CSS 内嵌,它现在在 Angular CLI 中默认处于启用状态,并且正在 Nuxt.js 中实现。
内嵌字体
在分析了数百个应用后,Aurora 团队发现,开发者通常会在 index.html
的 <head>
元素中引用字体,以便在其应用中添加字体。下面的示例展示了添加 Material Icons 后的效果:
<!doctype html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
...
</html>
尽管此模式完全有效且正常运行,但它会阻止应用呈现并引入额外的请求。为了更好地了解发生了什么情况,请查看上面 HTML 中引用的样式的源代码:
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}
.material-icons {
/*...*/
}
请注意 font-face
定义如何引用 fonts.gstatic.com
上托管的外部文件。加载应用时,浏览器首先必须下载 head 中引用的原始样式表。
接下来,浏览器下载 woff2
文件,最后才能够继续渲染应用。
一个优化机会是在构建时下载初始样式表并将其内嵌到 index.html
中。这样一来,系统便会在运行时跳过对 CDN 的整个往返,从而缩短屏蔽时间。
构建应用时,系统会向 CDN 发送请求,这会提取样式表并将其内嵌到 HTML 文件中,并向网域添加 <link rel=preconnect>
。应用此技术后,我们会得到以下结果:
<!doctype html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
<style type="text/css">
@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
...
</html>
现在,Next.js 和 Angular 支持内嵌字体
当框架开发者在底层工具中实现优化时,现有应用和新应用便能更轻松地启用优化,从而为整个生态系统带来改进。
从 Next.js v10.2 和 Angular v11 开始,此改进默认处于启用状态。这两种格式都支持内嵌 Google 和 Adobe 字体。Angular 预计会在 v12.2 中引入后者。
您可以在 GitHub 上找到 Next.js 中的内嵌字体实现,并观看介绍在 Angular 环境中进行此优化的视频。
内嵌关键 CSS
另一项增强功能涉及通过内嵌关键 CSS 来改进 First Contentful Paint (FCP) 和 Largest Contentful Paint (LCP) 指标。网页的关键 CSS 包括其在首次渲染时使用的所有样式。如需详细了解此主题,请参阅推迟非关键 CSS。
我们发现,许多应用会同步加载样式,这会阻止应用呈现。快速解决方法是异步加载样式。请勿使用 media="all"
加载脚本,而是将 media
属性的值设置为 print
,并在加载完成后将属性值替换为 all
:
<link rel="stylesheet" href="..." media="print" onload="this.media='all'">
不过,这种做法会导致没有样式的内容闪烁。
上方视频展示了网页的呈现过程,该网页会异步加载其样式。之所以发生闪烁,是因为浏览器先开始下载样式,然后呈现随后的 HTML。浏览器下载样式后,会触发链接元素的 onload
事件,将 media
属性更新为 all
,并将样式应用于 DOM。
在呈现 HTML 和应用样式期间,系统会对网页的一部分取消样式。 当浏览器使用这些样式时,我们会看到闪烁,这会导致用户体验不佳,并导致累计布局偏移 (CLS) 出现回归问题。
关键 CSS 内嵌以及异步样式加载可以改进加载行为。critters 工具会查看样式表中的选择器,并将其与 HTML 进行匹配,以便找出网页上使用的样式。当它找到匹配项时,会将相应的样式视为关键 CSS 的一部分,并将其内嵌。
让我们看看以下示例:
<head> <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'"> </head> <body> <section> <button class="primary"></button> </section> </body>
/* styles.css */ section button.primary { /* ... */ } .list { /* ... */ }
在上面的示例中,critter 会读取并解析 styles.css
的内容,然后将两个选择器与 HTML 进行匹配,并发现我们使用了 section button.primary
。最后,Critters 会在网页的 <head>
中内嵌相应的样式,从而产生以下结果:
<head> <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'"> <style> section button.primary { /* ... */ } </style> </head> <body> <section> <button class="primary"></button> </section> </body>
在 HTML 中内嵌关键 CSS 后,您会发现页面不再闪烁:
关键 CSS 内嵌功能现已在 Angular 中推出,并在 v12 中默认处于启用状态。如果您使用的是 v11,可通过在 angular.json
中将 inlineCritical
属性设置为 true
来启用 v11。如需在 Next.js 中选择启用这项功能,请将 experimental: { optimizeCss: true }
添加到您的 next.config.js
中。
总结
在本博文中,我们介绍了 Chrome 与 Web 框架之间的一些协作。如果您是框架作者,并且发现我们在您的技术中解决了一些问题,我们希望我们的研究成果能激励您应用类似的性能优化。
详细了解改进内容。您可以在Aurora 简介一文中找到我们为 Core Web Vitals 开展的优化工作的完整列表。