Front-End Performance Checklist 2021[1]
https://www.smashingmagazine....前端性能优化(一):准备工作[2]
一、传输优化
Google在2015年推出了Brotli,这是一种全新的开源无损数据格式,并被所有现代浏览器支持。Brotli有11个预设的编码质量级别,更高的质量级别要求更多的CPU以换取更好的压缩比。较慢的压缩速度最终会导致更高的压缩率,但Brotli解压速度仍然很快。4级压缩的Brotli比Gzip更小,压缩速度也更快。浏览器只有在用户通过HTTPS访问网站时才会接受Brotli。它被广泛支持,许多cdn也支持它。你甚至可以在尚不支持BCD的CDN上启用Brotli(与Service Worker一起使用)。
问题在于,由于以高压缩级别使用Brotli压缩所有资产的成本很高,因此许多托管服务提供商不能仅仅因为其产生的巨额成本开销而在短时间内使用它。实际上,在最高压缩级别下,Brotli是如此之慢,以至于服务器等待动态压缩资产时,服务器开始发送响应所花费的时间会抵消文件大小中的任何潜在收益。(但是,如果在构建期间有时间进行静态压缩,则最好使用更高的压缩设置。)
Brotli文件格式包括一个内置的静态字典,除了包含多种语言的各种字符串外,它还支持对这些单词进行多种转换的选项,从而增加了其多功能性。在Felix Hanau研究中,他发现了一种方法可提高5-9级的压缩[3]---使用“比默认值更专业的字典子集”,加上Content-Type头告诉压缩器它是否应该对HTML、JavaScript或CSS使用子集。结果是“当使用有限的字典使用方法在高压缩级别压缩web内容时,对性能的影响可以忽略不计(与通常的增加12%相比,CPU只增加了1%到3%)。”
通过Elena Kirilenko的研究[4],我们可以使用以前的压缩产物来实现快速高效的Brotli再压缩。根据Elena的说法,“一旦我们拥有通过Brotli压缩的资产,并且我们尝试动态压缩动态内容(其中的内容类似于我们可以提前使用的内容),我们就可以大大缩短压缩时间”。例如,提供JavaScript包子集(如部分代码已在客户端上缓存或通过WebBundles提供动态包)或者使用基于事前已知模板的动态HTML或动态子集的WOFF2字体。根据Elena的说法,删除10%的内容时,压缩率提高了5.3%,压缩速度提高了39%;删除50%的内容时,压缩率提高了3.2%,压缩率提高了26%。
策略:使用最高压缩比配置的Brotli+Gzip 预压缩静态资源,并使用 Brotli 配置 4~6 级压缩比来快速压缩(动态)HTML。确保服务器正确处理 Brotli 或 gzip 的内容协商头。
二、图片优化
(一)CSS像素与物理像素
设备像素越多,屏幕上显示内容的细节就越细。
高分辨率屏幕:图像资产需要更多的细节。对于位图来说,它的图像编码数据是基于每一个像素的,因此,图像像素越多,文件越大。当我们将物理屏幕的分辨率增加一倍时,像素的总数增加了四倍:水平像素的两倍,垂直像素的两倍。一个“2倍”的屏幕不仅仅是双倍,而是四倍所需的像素数!
因此,尽可能选择矢量图像,因为它们与分辨率无关,总是提供清晰的结果。如果需要位图,提供响应图像。
(二)图片格式
针对图片的无损压缩,从高到低:JPEG XL > AVIF >> WebP > JPEG
1、JPEG XL
另一种由谷歌和Cloudinary开发的自由开放格式。目前未标准化,暂没有浏览器支持。
2、AVIF
一种开放的,免版权法的格式,支持有损和无损压缩,动画,有损alpha通道,可以处理尖锐的线条和纯色(这是JPEG的一个问题),同时提供更好的结果。在相同的DSSIM(使用近似人类视觉的算法在两个或多个图像之间的相似性(差异))下,可以节省高达50%的文件大小。与WebP不同,AVIF在很大程度上一直优于JPEG。
AVIF甚至比大型svg表现得更好,尽管它当然不应该被视为svg的替代品。它也是第一个支持HDR颜色支持的图像格式之一;提供更高的亮度,色位深度和色域。唯一的缺点是,目前AVIF不支持渐进图像解码,类似于Brotli,高压缩率编码目前相当慢,尽管解码是快速的。
3、WebP
苹果公司在Safari 14中添加了对WebP的支持,到今天为止,所有现代浏览器都支持WebP。WebP也不是没有缺点的,它不支持像JPEG那样的渐进式渲染,这就是为什么用户使用好的 JPEG 可能会更快地看到实际图像,尽管 WebP 图像的网络加载速度可能会更快。使用 JPEG,我们可以用一半甚至四分之一的时间就提供给“像样的”用户体验,并在稍后加载其余的数据,而不是像 WebP 那样只有半空的图像。是否使用WebP取决于你想要的是什么:使用 WebP,你将减少图像大小,而使用 JPEG,你将提高图像的可感知性。另外,WebP并不总是生成比JPEG更小的图像。
如何选择?我们可以使用渐进式增强的方式:
当然,如果是背景图片,我们可以使用image-set做相同的处理
(三)自适应媒体加载与Client Hints
除了图片格式的问题,针对不同的网络情况,我们也应该做相应的优化:为慢速网络和低内存设备提供轻体验,为快速网络和高内存设备提供全体验。
为此,我们可以使用Client Hints与服务器协商选择适当的资源填充在页面上。Client Hints是HTTP请求头字段,例如DPR, Viewport-Width, Width, Save-Data, Accept(指定图像格式首选项)等。
我们还可以将其与Service Worker结合,Service Worker可以在请求中添加新的Client Hints Hearders values,重写URL并将图像请求指向CDN,根据连通性和用户偏好调整响应,等等。它不仅适用于图像资产,而且适用于几乎所有其他请求。
(四)其他优化
1、使用压缩工具适当压缩图片
● 针对JPEG
mozJPEG:可通过控制扫描级别来缩短开始渲染时间
Guetzli:谷歌的开源编码器,专注于感知性能,并利用Zopfli和WebP的学习成果,唯一的缺点是:处理时间慢(每百万像素一分钟的CPU)
● 针对PNG
可以使用Pingo
● 针对SVG
可以使用SVGO、SVGOMG,如果你需要从网站快速预览和复制或下载所有SVG资产,可以使用svg-grabber
始终值得一提的是保持矢量资产整洁。确保清理未使用的资产,删除不必要的元数据,并减少图稿中路径点的数量(从而减少SVG代码)。
● 其他工具
Squoosh:以最佳压缩级别(有损或无损)压缩,调整大小和处理图像
使用响应式图像断点生成器或Cloudinary或Imgix等服务来自动执行图像优化。同样,在许多情况下,仅使用srcset和size将会获得显着的好处。
要检查响应式标记的效率,可以使用Imaging-heap,这是一种命令行工具,可以测量视口大小和设备像素比率之间的效率。
可以将自动图像压缩添加到你的GitHub工作流程中,因此任何图像都不会影响未压缩的生产。可对PNG和JPG一起使用mozjpeg和libvips。
Lepton是一种工具和文件格式,可平均无损压缩JPEG 22%。
如果你想尽早显示占位符图像,可使用BlurHash。BlurHash将图像转换代表该图像占位符的短字符串(仅20-30个字符!)。该字符串足够短,可以轻松地将其添加为JSON对象中的字段。当然,简单点,你也可以使用纯色、渐变色或小图;如果想要占位图更接近于原图,除了BlurHash,你也可以使用SVG。后面我们会介绍,想先了解的可以看一下:
LQIP(Low Quality Image Placeholders)[7]
SQIP(a pluggable image converter with vector support)[8]
svg-placeholders[9]
Gradient Image Placeholders[10]
2、懒加载非关键图片,并在关键图像渲染完成后再加载任何异步脚本
最可靠的方法是混合惰性加载,使用Native Lazy-loading和支持懒加载第三方库(检测任何通过用户交互触发的可见性变化(使用IntersectionObserver))
3、考虑preload关键图片
4、考虑根据媒体查询加载不同大小的图片
5、确保图片总是设置width与height或在css中设置aspect-ratio属性,避免页面加载期间产生布局跳跃
6、使用CDN,通过网络更快地发送图像
更多更详细的图像优化指南,可参考:
● 图像优化指南[5]
● Maximally optimizing image loading for the web in 2021[6]
三、视频优化
是时候该抛弃GIF了,具有昂贵动画效果的GIF加载会影响渲染性能和带宽,不如改用动画 WebP(将 GIF 用作兜底),或将其全部替换为循环的 HTML5 视频;与图像不同,浏览器不会预加载 <video> 内容,但 HTML5 视频往往比 GIF 更轻,更小。
如果没有选择的情况下,也可以使用有损 GIF,gifsicle或giflossy对 gif 进行有损压缩以减小图像大小。
Colin Bendell的测试显示,在Safari技术预览中,img标签内的内联视频比GIF标签至少快20倍,解码速度快7倍,而且文件大小也只有一小部分。但是,其他浏览器不支持它。
有很多方式可以将GIF转化为MP4,比如:使用FFmpeg[11],你可以在控制执行下面命令即可:
ffmpeg -i my-animation.gif -b:v 0 -crf 25 -f mp4 -vcodec libx264 -pix_fmt yuv420p my-animation.mp4
WebM是一种相对较新的文件格式,最初发布于2010年。它比MP4格式的视频要小很多,但是浏览器支持不是很好,并不是所有的浏览器都支持。我们也可以使用FFmpeg将GIF转为WebM格式:
ffmpeg -i my-animation.gif -c vp9 -b:v 0 -crf 41 my-animation.webm
我们在使用过程中,可以同时提供WebM和MP4,这样如果浏览器不支持WebM,它可以退回到MP4。
<!-- By Houssein Djirdeh. https://web.dev/replace-gifs-with-videos/ -->
<!-- A common scenartio: MP4 with a WEBM fallback. -->
<video autoplay loop muted playsinline>
<source src="my-animation.webm" type="video/webm">
<source src="my-animation.mp4" type="video/mp4">
</video>
2018 年,开源媒体联盟发布了一种新的有前途的视频格式,称为 AV1。AV1 的压缩与 H.265 编解码器(H.264 的演进)相似,但与后者不同,AV1 是免费的。H.265 许可证的价格迫使浏览器供应商改为使用性能相同的 AV1:(就像 H.265 一样)AV1 压缩的效果是 WebM 的两倍。
事实上,苹果目前使用HEIF格式和HEVC (H.265),最新iOS上的所有照片和视频都以这些格式保存,而不是JPEG格式。虽然HEIF和HEVC (H.265)还没有适当地暴露在web,AV1是-它正在获得浏览器支持。因此,可以在<video>的source中添加AV1格式的视频。
如果视频文件太大,但又想要快速渲染图片,比如在你的启动页面又一个比较大的背景视频,一种常用的技术是首先以静止图像的形式显示第一帧,或者显示一个经过大量优化的、可以被解释为视频一部分的短循环片段,然后,当视频缓冲足够时,就开始播放实际的视频。
如果你想提供响应式的海报图片,你也可以借助第三方库responsive-video-poster去实现它。
研究表明视频流的质量会影响观看者的行为。事实上,如果启动延迟超过2秒,观众就会开始放弃视频。超过这一点,1秒的延迟将导致约5.8%的放弃率增加。
通常小屏幕设备无法处理我们提供的在电脑上播放的720p和1080p。根据Doug Sillars的说法,我们可以创建更小的视频版本,并使用Javascript为更小的屏幕检测源代码,以确保在这些设备上快速流畅地播放。或者,我们可以使用流媒体视频。HLS视频流将向设备发送适当大小的视频-抽象出为不同屏幕创建不同视频的需求。它还将协商网络速度,并适应视频比特率的速度,您正在使用的网络。
为了避免带宽上的浪费,我们只能为真正能够播放视频的设备添加视频源。或者,我们可以从视频标签中删除autoplay属性,并使用JavaScript为更大的屏幕插入自动播放。此外,我们需要在视频中添加preload="none"来告诉浏览器不要下载任何视频文件,直到它真正需要该文件:
<!-- Based on Doug Sillars's post. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ -->
<video id="hero-video"
preload="none"
playsinline
muted
loop
width="1920"
height="1080"
poster="poster.jpg">
<source src="video.webm" type="video/webm">
<source src="video.mp4" type="video/mp4">
</video>
然后我们可以针对实际支持AV1的浏览器:
<source src="video.av1.mp4" type="video/mp4; codecs=av01.0.05M.08">
<source src="video.hevc.mp4" type="video/mp4; codecs=hevc">
<source src="video.webm" type="video/webm">
<source src="video.mp4" type="video/mp4">
然后我们可以在特定的阈值(例如1000px)上重新添加自动播放:
/* By Doug Sillars. https://dougsillars.com/2020/01/06/hiding-videos-on-the-mbile-web/ */
<script>
window.onload = addAutoplay();
var videoLocation = document.getElementById("hero-video");
function addAutoplay() {
if(window.innerWidth > 1000){
videoLocation.setAttribute("autoplay","");
};
}
</script>
四、字体优化
(一)字体格式
WOFF2 的浏览器支持非常好,可以将 WOFF 作为不支持 WOFF2 的浏览器的兜底字体 - 也可以用系统字体兜底。
(二)基本概念
在了解如果优化字体之前,有几个基本的概念你需要掌握:
【font-display】
在font-display未出来之前,大多数浏览器都实现了一个超时时间,如果字体下载太慢,超过这段时间后将使用备用字体。这是一种有用的技术,但不幸的是,浏览器的实际实现是不同的。
Browser | Timeout | Fallback | Swap |
---|---|---|---|
Chrome 35+ | 3 seconds | Yes | Yes |
Opera | 3 seconds | Yes | Yes |
Firefox | 3 seconds | Yes | Yes |
Internet Explorer | 0 seconds | Yes | Yes |
Safari | No timeout | N/A | N/A |
font-display,它是一个css属性,它决定了一个@font-face 在不同的下载时间和可用时间下是如何展示的。
字体显示时间轴:字体显示时间线基于一个计时器,该计时器在用户代理尝试使用给定下载字体的那一刻开始。时间线分为三个时间段,在这三个时间段中指定使用字体的元素的渲染行为。
● 字体阻塞周期:如果未加载字体,任何试图使用它的元素都必须渲染不可见的后备字体。如果在此期间字体已成功加载,则正常使用它。
● 字体交换周期:在阻塞周期后立即发生,如果未加载字体,任何尝试使用它的元素都必须渲染后备字体。如果在此期间字体已成功加载,则正常使用它。
● 字体失败周期:在交换周期后立即发生,如果在此周期开始时字体还未加载,则标记为加载失败,使用正常的后备字体。否则,字体就会正常使用。
font-display有几个取值:
● auto:默认,字体显示策略由用户代理定义(绝大多数浏览器默认使用类似block的方式)
● block:为字体提供一个短暂的阻塞周期(绝大多少情况推荐为3s)和无限的交换周期。换句话说就是,如果字体没有加载,浏览器首先会绘制“不可见”的文本,但一旦加载,就会替换字体面。
● swap:为字体提供一个非常小的阻塞周期(通常为0s)和无限的交换周期。(后备文本立即显示直到自定义字体加载完成后再使用自定义字体渲染文本)。通常用于比较重要的文案,比如Logo 文案。
● fallback:为字体提供一个非常小的阻塞周期和短暂的交换周期(这个可以说是auto和swap的一种折中方式。需要使用自定义字体渲染的文本会在较短的时间(100ms或更少 according to Google )不可见,如果自定义字体还没有加载结束,那么就先加载无样式的文本。一旦自定义字体加载结束,那么文本就会被正确赋予样式)。通常用于正文。
● optional:为字体提供一个非常小的阻塞周期(通常100ms或更少),并且没有交换周期(效果和fallback几乎一样,都是先在极短的时间内文本不可见,然后再加载无样式的文本。不过optional选项可以让浏览器自由决定是否使用自定义字体,而这个决定很大程度上取决于浏览器的连接速度。如果速度很慢,那你的自定义字体可能就不会被使用)。
【Flash of Invisible Text(FOIT)】
在加载web字体时,浏览器默认渲染文字不可见,在现代浏览器中,FOIT最多持续3s,当人们说web字体阻塞了资源时,他们很可能是指FOIT。
【Flash of Unstyled Text(FOUT)】
在加载web字体后,默认使用系统字体作为渲染文字的备用方案,通常在FOIT超时(3s)后使用。IE与Edge浏览器并不会等待,会直接使用备用方案渲染文字。FOUT 比 FOIT更可取,但需注意尽量减少其回流影响。
【Flash of Faux Text(FOFT)】
它是一种字体加载策略,首先渲染常规 web字体,当粗体与斜体正在加载时,使用字体合成来渲染粗体与斜体变体。
(三)字体子集化
尤其是对于中文来说,一个完整的中文字体包至少几M,但我们的项目仅使用了其中一部分而已,没必要全加载。我们可以用字体代工厂将 Web 字体转换成较小的子集,或者如果您使用的是开源字体,则可以使用Glyphhanger或Fontsquirrel对它们进行子集化。您甚至可以使用 PeterMüller 的subfont来自动完成整个字体子集化的工作流程,subfont 是一个命令行工具,可以静态分析您的页面以生成最佳的 Web 字体子集,然后将其注入到您的页面中。
(四)字体加载方案
目前可以使用的更好的选择是:预加载关键 FOFT[11]and “compromise”技术[12]。他们两个都分两阶段渲染来逐步交付 Web 字体-首先需要一个小的超级子集,以便使用 Web 字体快速准确地渲染页面,然后加载异步家族的其余部分。所不同的是,只有在不支持字体加载事件的场景中,“compromise”技术才会异步加载 polyfill,因此默认情况下您无需加载 polyfill。
除了这两种方式,还有其他的,我们统一对比一下:
1、preload
使用preload尽可能早的获取字体资源,但predload 字体需要放在关键 CSS 和 JavaScript 的链接之后。
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
注意:它的优缺点很大程序上取决与你如何结合@font-face和font-display使用。
优点:
● 非常容易实现,一个<link>就可以了。
● 比@font-face块有更好的渲染性能。在加载流中,Web字体被要求更高的优先级。
● 使用type属性指定字体格式,对未来友好。在这一点上,仍然有可能(虽然看起来不太可能)web浏览器将在WOFF2之前实现预加载,如果没有这个属性,您可能会看到一个浪费的请求。所以,确保包含type。
● 不要包含修改过的字体(使用子集或其他方式)。
缺点:
● 可伸缩性:预加载的越多,就越阻塞初始渲染。尽量只使用最重要的一到两种字体。
● 灵活性:没有办法分组重绘/回流。
● 在第三方服务器上可能没法使用。在你请求web字体之前就需要知道标记渲染的URL。例如,Google Fonts,在你向他们的CDN发出的CSS请求中生成这些。
结论:单凭这一点是不够的。
2、不使用web字体
虽然这不是web字体加载策略,但不得不说它比错误的使用web字体好。
优点:
● 不确定是否可以更简单:只使用font-family没有@font-face。
● 接近即时渲染性能:不担心FOUT或FOIT。
缺点:
● 可用性有限。很少有系统字体可以跨平台使用。检查fontfamily.io查看系统字体是否为你的需求提供了可接受的浏览器支持。
结论:好,但是也没什么值得兴奋的。
3、内联Data URI
有2种典型的方式:内联进<link rel="stylesheet">中或放进<style>中。
优点:
● 看起来很好的渲染性能:没有FOUT或FOIT。这是一个非常好的点。
● 灵活性:没有FOUT或FOIT,就不需要担心重绘/回流。
● 健壮性:内联将所有鸡蛋放入初始服务器请求篮子中。
缺点:
● 渲染性能问题:虽然它没有FOUT,但它显然延迟了初始渲染时间。另外,单一的一个WOFF2文件可能只有10-15KB左右,内联它可能会让你超过HTTP/1的建议14KB或更少。
● 浏览器支持:不利用@font-face块中逗号分隔的格式列表,这种方法只嵌入了一种格式类型。通常情况下,这意味着WOFF,因此使用这种方法迫使您在普遍性(WOFF)和更窄的用户代理支持、更小的文件大小(WOFF2)之间进行选择。
● 糟糕的可伸缩性:请求不会并行发生,连续加载。
● 自托管:当然是必需的。
结论:只有当你真的不喜欢FOUT时才使用这种方法——不推荐。
4、异步DATA URI样式
使用像loadCSS的工具将所有的字体转化为Data URI。
优点:
● 渲染性能:基本消除了FOIT。
● 灵活性:易于将请求分组到单个重绘(将多个Data uri放在一个样式表中)。
● 轻松:不需要任何额外的CSS更改使用。这是一个很大的好处,但不是万能的。
● 健壮:如果异步请求失败,将继续显示回退文本。
缺点:
● 渲染性能:在解析样式表和Data uri时,具有非常明显但很短的FOIT。
● 灵活性和可伸缩性:分组请求和重绘是耦合在一起的。如果将多个Data uri组合在一起(这将导致串行加载而不是并行加载),它们将一起重新绘制。使用此方法,不能并行加载并重绘。
● 维护不友好:要求你有自己的方法来确定字体格式支持,在获取Data URI样式表之前,你的JavaScript加载器需要确定支持哪种字体格式(WOFF2或WOFF)。这意味着如果出现了一种新的字体格式,你需要为它开发一个功能测试。
● 浏览器支持:你可以绕过对加载器步骤的维护和对WOFF2或WOFF的硬代码,但这将导致不必要的请求或潜在的丢弃请求(这与我们讨论内联数据uri的缺点相同)。
● 自我托管:必需的。
结论:还好,但我们可以做得更好。
5、FOUT+一个类名
使用带有polyfill的css字体加载API来检测特定的字体何时已经加载,并且只有在它成功加载后才应用该web字体在你的CSS中。通常这意味着在元素上切换一个类。使用SASS或LESS mixins更容易维护。
// demo:https://www.zachleat.com/web-fonts/demos/fout-with-class-polyfill.html
<style>
@font-face {
font-family: Lato;
src: url('font-lato/lato-regular-webfont.woff2') format('woff2'),
url('font-lato/lato-regular-webfont.woff') format('woff');
}
@font-face {
font-family: LatoBold;
src: url('font-lato/lato-bold-webfont.woff2') format('woff2'),
url('font-lato/lato-bold-webfont.woff') format('woff');
font-weight: 700;
}
body {
font-family: sans-serif;
}
.fonts-loaded body {
font-family: Lato, sans-serif;
}
.fonts-loaded h1,
.fonts-loaded strong {
font-family: LatoBold, sans-serif;
font-weight: 700;
}
</style>
<script>
(function() {
// Optimization for Repeat Views
if( sessionStorage.fontsLoadedFoutWithClassPolyfill ) {
document.documentElement.className += " fonts-loaded";
return;
}
/* Font Face Observer v2.0.13 - © Bram Stein. License: BSD-3-Clause */
// 此处省略
var fontA = new FontFaceObserver('Lato');
var fontB = new FontFaceObserver('LatoBold', {
weight: 700
});
Promise.all([
fontA.load(null, 10000),
fontB.load(null, 10000),
]).then(function () {
document.documentElement.className += " fonts-loaded";
// Optimization for Repeat Views
sessionStorage.fontsLoadedFoutWithClassPolyfill = true;
});
})();
</script>
优点:
● 渲染性能:消除了FOIT。这种方法经过了试验和测试,是TypeKit推荐的方法之一。
● 灵活性:很容易将请求分组到一个重新绘制(使用一个类用于多个web字体加载)。
● 可伸缩性:请求是并行发生的。
● 健壮:如果请求失败,回退文本仍然显示。
● 托管:独立于字体加载器的工作(很容易通过第三方主机或现有的@font-face块实现)。
● 强大的浏览器支持,polyfill通常可以在任何支持web字体的地方工作。
● 未来友好:polyfill不耦合到字体格式,应该与现有的@font-face块工作。这意味着当新格式出现时,你可以像平常一样改变你的@font-face。
● 不需要修改字体(通过子设置或其他方式)。
缺点:
● 需要严格维护/控制CSS。在CSS中单一使用web字体font-family而不使用Loaded类可能会触发FOIT。
● 通常需要你硬编码哪些web字体你想在页面上加载。这可能意味着你最终会加载比页面需要的更多的web字体内容。记住,如果使用@font-face,更新的浏览器只下载在你的页面上实际使用的web字体。这是免费给你的。这就是为什么《纽约时报》可以在其主页上避开100种不同的@font-face字体阻塞——浏览器只下载其中的一小部分。使用这种方法,您必须告诉浏览器下载哪些字体,而不依赖于使用。
结论:这是基线标准。适用于大多数用例。
6、FOFT或FOUT+两段渲染
该方法是基于上一种方法做了些许改变,当你加载同一个字体的多维度或多样式的时候这非常有用,比如:常规、加粗、倾斜、加粗+倾斜等等。该方法主要是将这些web字体分为两个阶段加载,先加载常规体,然后渲染假粗体和假斜体内容(使用字体合成),而真正的web字体的权重和替代样式正在加载。
// demo: https://www.zachleat.com/web-fonts/demos/foft.html
// 基于上一个方式,修改了部分内容,下面仅修改的部分
<style>
body {
font-family: sans-serif;
}
.fonts-loaded-1 body {
font-family: LatoInitial;
}
.fonts-loaded-2 body {
font-family: Lato;
}
</style>
<script>
(function() {
if( "fonts" in document ) {
// Optimization for Repeat Views
if( sessionStorage.fontsLoadedFoft ) {
document.documentElement.className += " fonts-loaded-2";
return;
}
document.fonts.load("1em LatoInitial").then(function () {
document.documentElement.className += " fonts-loaded-1";
Promise.all([
document.fonts.load("400 1em Lato"),
document.fonts.load("700 1em Lato"),
document.fonts.load("italic 1em Lato"),
document.fonts.load("italic 700 1em Lato")
]).then(function () {
document.documentElement.className += " fonts-loaded-2";
// Optimization for Repeat Views
sessionStorage.fontsLoadedFoft = true;
});
});
}
})();
</script>
优点:
● 拥有上一种方式的所有优点。
● 渲染性能:极大地减少了网页字体加载时发生的内容跳跃量。考虑到我们将网页字体加载分为两个阶段,这使得第一个阶段(常规字体,这将导致最多的回流)比我们将所有字体组合在一起进行一次重绘要快得多。
缺点:
● 拥有上一种方式的所有缺点。
● 有些设计师对字体合成非常敏感。客观地说,合成的变体不如真正的变体有用,但这不是一个公平的比较。请记住,合成版本只是一个临时占位符,我们需要问的问题是:它们比后退字体更有用还是更没用?更有用。
结论:适合那些对额外性能感兴趣的,但不能与关键FOFT相比。
7、关键FOFT
这与标准的FOFT的区别就是它不像标准FOFT那样在第一阶段加载全部的常规字体,它仅加载其子集(通常只包含A-Z和可选的0-9和/或标点)。完整的常规web字体会在第二阶段与其他权重和样式一起加载。
// demo: https://www.zachleat.com/web-fonts/demos/critical-foft-polyfill.html
优点:
● 有FOFT的所有优点。
● 渲染性能:第一阶段的加载速度更快(在较慢的连接上更明显),进一步减少了第一阶段网页字体重绘的时间,使你最常用的网页字体回流发生得更快而不是更晚。
缺点:
● 有FOFT的所有缺点。
● 第一阶段加载的常规字体子集与第二阶段加载整个常规字体重复加载了,引入了少量的开销,这是我们为减少回流而付出的代价。
● 许可限制:需要子集。
结论:使用下面的一个改进的关键FOFT变体。
8、关键FOFT+DATA URI
第一阶段加载子集的方式改为转成data url 的形式进行加载。虽然这会阻塞初始渲染时间,但是我们仅植入了很小的子集,与消除了大部分的FOUT相比这个代价很小。
// demo:https://www.zachleat.com/web-fonts/demos/critical-foft-preload-polyfill.html
优点:
● 有关键FOFT的所有优点。
● 为常规字体消除FOIT和大大减少FOUT。当加载其他权重和样式时,第二阶段加载的其他字符会出现一个小回流,但影响要小得多。
缺点:
● 有关键FOFT的所有缺点。
● 小的内联Data URI将略微阻塞初始渲染时间,我们用这个来换取高度减少的FOUT。
● 自我托管:必需的。
结论:在浏览器还不完全支持preload时,这是黄金标准。
9、关键FOFT+Preload
第一阶段的常规字体子集使用Preload的方式进行加载。
优点:
● 有关键FOFT的所有优点。
● 渲染性能:与之前的方式相比下载的优先级更高,它比上一种方式以Data URI的方式内嵌更好,因为它可以利用请求缓存,而不是每次请求都要去获取相同的web字体数据。
缺点:
● 有关键FOFT的所有缺点。
● 只使用单一的web字体格式。
● preload会略微延迟初始渲染时间。
● 自托管:可能需要。
结论:目前的黄金标准。
(五)其他优化
1、不要将local与web字体混合使用,如下:
@font-face {
font-family: Open Sans;
src: local('Open Sans Regular'),
local('OpenSans-Regular'),
url('opensans.woff2') format ('woff2'),
url('opensans.woff') format('woff');
}
就算本地字体名称可以匹配web字体名字,但不能保证它两是同一个字体,事实上,大多数情况不是。因为手机上的字体可能只是字体的一个子集,用户可以修改字体如line-height等,表现会有所不同,有些字体可能还是以其他字体代替,还有可能字体因为版本的问题而不同,所以不推荐使用local字体。除了Android请求Roboto,Google字体对所有用户禁止使用local()。
2、尽可能的自托管你的静态资源
自从Chrome v86(发布于2020年10月),跨站点的资源,如字体不能共享在同一个CDN -由于分区浏览器缓存,这是Safari多年来的默认行为。没有了跨站点资源共享缓存的优势。
当所有的请求都来自同一个域,并且连接在同一个HTTP/2上时,它们可以相互调度。关键资源(如CSS和字体)可以在队列中向前推,并在低优先级资源(如图像)之前交付。
由于谷歌字体(以及大多数第三方资源)是从与主页资源不同的领域提供的,因此不能对它们进行优先排序,并最终相互竞争下载带宽。这可能导致实际获取时间比最佳情况下的8次往返要长得多。
如果做不到自托管,也可以使用代理的方式将其代理到你的域名下:将字体请求url的域名改为html的域名,利用Service worker拦截,请求真正的链接。
(六)指标
要测量 Web 字体加载性能,请考虑所有文本可见时间(所有字体均已加载且所有内容均以 Web 字体显示的时刻)、变为真实斜体的时间以及首次渲染后的Web字体回流数。
总的来说,(安全)的网络字体加载策略就是:将字体子集化并在第二阶段渲染做好准备,使用 font-display 描述符声明它们,使用字体加载 API 对重绘进行分组,并将字体存储在持久的 service worker 缓存中。第一次访问时,在阻塞的外部脚本之前插入脚本预加载字体。
欢迎关注我的个人公众号:
参考资料
Front-End Performance Checklist 2021[1]:https://www.smashingmagazine....
前端性能优化(一):准备工作[2]:https://mp.weixin.qq.com/s/QD...
Brotli compression using a reduced dictionary[3]:https://blog.cloudflare.com/b...
Fast and efficient recompression using previous compression artifacts[4]:https://dev.to/riknelix/fast-...
图像优化指南[5]:https://images.guide/
Maximally optimizing image loading for the web in 2021[6]:https://www.industrialempathy...
LQIP(Low Quality Image Placeholders)[7]:https://www.guypo.com/introdu...
SQIP(a pluggable image converter with vector support)[8]:https://github.com/axe312ger/...
svg-placeholders[9]:https://jmperezperez.com/svg-...
Gradient Image Placeholders[10]:https://calendar.perfplanet.c...
exact instructions for FFmpeg[11]:https://medium.com/@borisscha...
Critical FOFT with preload[12]:https://www.zachleat.com/web/...
"The Compromise" method:https://www.zachleat.com/web/...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。