《Web性能实战》(笔记)

本文由清尘发表于2020-08-28 14:21属于LIFE分类

Web性能实战
杰里米·瓦格纳
88个笔记

◆ 1.1 理解Web性能

在现代网站自身的需求和尽快提供服务之间取得平衡。你需要了解性能提升技术,这样才能防止复杂Web体验影响用户体验中最有价值的部分:访问内容的能力。

◆ 1.4 优化客户网站

这些优化工作包括:首先要缩小网站资源,包括CSS、JavaScript和HTML本身;然后要在不损害其视觉完整性的前提下,优化网站上的图像;最后要在服

务器上压缩文本资源。

缩小(minification)是从基于文本的资源中去除所有空白和非必要字符的过程,因而不会影响资源的工作方式

经验法则是将任何优化的结果与原始图像进行比较,并确保你对结果满意。

◆ 2.3 检查网络请求

当服务器压缩内容时,它会用Content-Encoding头进行响应。可以使用开发者工具检查响应,以查看其实际效果

◆ 3.1 直入主题,保持DRY

优化CSS的方法之一是进行分割。分割指的是根据特定页面模板拆分CSS样式。将网站的所有CSS合并到一个文件中是合理的,这样用户在第一次访问时就已经缓存了网站的所有CSS。然而,以这种方式提供CSS可能是一场赌博,因为用户可能永远不会跳转到子页面。一部分用户将被迫为他们永远看不到的页面下载CSS。这样做会减慢用户初次访问网站时的速度。更安全的做法是将负担分散在几个页面上,但处理方式要明智

如何实现这一点取决于网站本身以及具体的CSS。如果大多数页面模板是相似的,并且样式是高度通用的,那么坚持使用一个样式表是有意义的。但是如果很多页面模板具有不同的特定样式,请检查用户的行为并据此做出决定

◆ 3.2 移动优先即用户优先

这两种技术都是从一套基本的CSS开始的。这套CSS不包含在任何媒体查询中,它定义网站的默认外观。使用移动优先方法,默认外观就是站点的移动端版本;而在桌面优先网站中,默认外观是网站的桌面版本。

应当以用户为中心选择使用哪种技术,而桌面优先的响应式设计不是用户优先的。使用移动优先方法,首先要构建最简单的网站外观,然后随着规模的扩大而增加复杂性,因为会有越来越多的用户使用移动设备访问Web

移动优先CSS的优势在于,你为最有可能使用网站的设备提供CSS服务。由于移动设备的处理能力和内存通常低于桌面设备,因此不必应用桌面样式和解释媒体查询,然后才应用移动样式

从长远来看,这对开发人员来说也有好处:你可以随时扩大规模(而不是缩小规模)。相比于在缩小规模的时候移除某些内容,从最简单的起点出发,更容易进行优化。

如果你的项目需要支持传统浏览器,请谨慎使用rem。

◆ 3.3 对CSS进行性能调整

避免使用 @import声明

面向性能的网站的目标之一是尽可能多地并行化HTTP请求。并行请求是在同一时间(或接近同一时间)发出的请求。串行请求则相反,一个接一个地发生。在外部CSS文件中使用时,@import串行请求

HTML link标签是加载CSS的最佳原生方法。它不像@import那样串行加载请求,而是并行加载请求。扫描HTML文档后,将加载在文档中找到的所有link标签

从技术上讲,你可以在HTML style标签中使用@import而不会影响性能,但是将此方法与link标签混合使用,或者在CSS文件中使用@import,将导致串行请求。在实践中,最好坚持使用link标签,因为它的行为是可预测的,并且将把CSS导入HTML的任务降级。

在head中放置CSS

你应该尽早在文档中放置对CSS的引用,最早可以加载CSS的位置是head标签。这样做可以缓解两个问题:无样式内容的闪烁问题,以及在加载时提高页面的渲染性能。

将CSS保留在HTML head标签中的一个强有力的原因是,它可以防止用户看到你的网站处于无样式状态。这种现象被称为无样式内容闪烁(Flash of UnstyledContent)。当用户在没有应用任何CSS的情况下短暂(但很明显)看到你的网站时,就会出现这种现象。图3-16显示了这种紊乱的效果,因为CSS在文档中加载得太晚了。

将CSS放在HTML的head标签中,不仅可以防止无样式内容出现,还可以加快网站在初始页面加载时的渲染速度。如果样式表稍后才包含在文档中,浏览器必须比在head中加载样式表时做更多工作,因为它必须重新渲染和绘制整个DOM。

渲染内容时,flexbox往往是性能更优的解决方案

◆ 3.4 使用CSS过渡

CSS过渡没有开销,因为它随浏览器一起提供。对于具有简单动画需求的网站来说,使用内置功能而不是将JavaScript库的开销添加到页面中,更为合理。

可以将will-change这个属性视为对transition属性的补充

will-change接受任何有效的CSS属性,或以逗号分隔的属性列表,用来设置动画。但是要小心:误用它会影响设备上资源的分配方式

关于这个属性,需要记住的关键一点是,你可以预测元素的潜在更改,而不是假设它们会发生。

◆ 第4章 理解关键CSS

优先渲染首屏内容加快页面的渲染速度,这种技术被称为关键CSS。

◆ 4.1 关键CSS及其解决的问题

关键CSS,即折叠之上的内容——这些是用户会立即看到的内容样式,需要尽快加载。❏ 非关键CSS,即折叠之下的内容——这些是用户开始向下滚动页面之前看不到的内容样式。这种CSS也应该尽快加载,但不能在关键CSS之前加载。[插图]

◆ 4.2 关键CSS的原理

实,关键CSS在一定程度上已经解释了这一点。只将首屏样式存储到style标签中,并将其内联到HTML,剩下的样式将从外部文件加载。这是否会使一些CSS在随后的页面加载中变得多余?确实,但只涉及网站首屏的一小部分内容。首次绘制的时间减少将抵消这种冗余的损害。即使使用的是CSS框架,你仍然可以内联用于特定页面的框架部分。当浏览器开始更快地绘制页面时,就会达到预期的效果,并且用户不会注意到少量冗余CSS对性能的损害。

关键CSS的另一半是加载首屏以外内容的样式。这些样式是使用link标签加载的,但是你不是以常规的方式使用它,而是要使用preload资源提示加载CSS,同时不会阻塞渲染。你还将加载一个脚本,该脚本可以为不支持它的浏览器polyfillpreload功能。以上操作看起来有点烦琐,但它立竿见影,当与首屏内容的内联CSS结合时,尤其如此。浏览器立即渲染首屏内容的CSS,而preload资源提示会在后台获取页面其余部分的样式。

preload资源提示加载首屏以外内容的外部CSS。这种加载外部样式表的方式不会阻塞渲染。CSS完成加载时,onload事件会触发,并修改link标签的rel值使样式渲染

◆ 第5章 响应式图像

❏ 使用CSS媒体查询,为用户设备传输正确的背景图像❏ 在HTML中使用srcset和picture元素传输响应式图像❏ 使用Picturefill在特定浏览器中提供srcset和picture的polyfill❏ 在CSS和HTML中使用SVG图像

图像传输的目的不仅是提供尽可能好的视觉体验,而且要提供适合设备功能的图像。知道如何正确传输图像后,就可以确保功能较弱的设备永远不会承受超出其承受能力的负担,同时确保功能最强的设备接收到尽可能好的体验。保持这种平衡即可确保视觉吸引力和性能的完美融合。

◆ 5.2 理解图像类型及其应用

除非必须全透明,否则照片内容通常最好保留为JPEG格式。

颜色少的简单图像应该使用8位无损格式;不适合有损格式或需要完全透明的图像应使用全彩PNG格式。

◆ 5.4 在HTML中传输图像

图像的全局max-width规则

对所有img元素设置的全局max-width规则

使用srcset显示响应式图像的方法之一,是在img标签中使用名为srcset的HTML5特性。img标签的这个可选属性不是替换src属性,而是对其进行补充。

srcset的优点是,它不需要媒体查询就可以工作。浏览器获取给定的信息,并根据视口的当前状态选择最适合的图像。在给定img标签中所有图像具有相同的处理方式但长宽比不同的情况下,使用srcset非常适合

在优化方面,srcset是高效的。这是因为浏览器只会下载最佳视觉质量所需的内容。本例中,如果在大屏幕上加载页面,浏览器将加载amp-large.jpg;但如果缩小页面,浏览器将不会请求amp-medium.jpg、amp-small.jpg等。浏览器将调整已经加载的图像,这可以防止不必要的图像资源获取

如果以较小的屏幕加载页面并放大,浏览器将下载所需内容,以确保良好的图像质量。所以鱼与熊掌可以兼得。一言以蔽之:srcset只在需要时获取它需要的东西。

你可能需要比srcset所能提供的更大的灵活性。也许需要图像根据屏幕的宽度改变大小,而这就是size属性的用途。size属性与srcset一样,也用于img标签。它接受一组媒体查询和宽度。媒体查询与典型的CSS媒体查询一样,定义了图像应该更改的临界点。媒体查询起作用时,它后面的宽度设置图像应显示的宽度。这些媒体查询和图像大小可以通过逗号分隔成多对使用

将sizes属性添加到这个img标签,图像行为会在不同的临界点中受到影响

使用picture元素

srcset是个好功能,但当你的图像需要美术设计时,它就显得力不从心了。美术设计是一种应用于响应式图像的技术,指的是为不同的屏幕提供不同的裁剪和焦点的实践。例如,当一个针对大屏幕的图像对于较小的屏幕不是最优的时候,就可以这么做

img标签上使用的srcset功能不适用于在不同临界点中需要不同处理的图像,因为它要求一组图像保持相同的长宽比。而picture元素没有这样的要求,它可以在定义的任何转换点显示任何图像。

要在picture元素中创建一系列回退,可以在source元素上使用type属性。type接受图像的文件类型作为srcset属性中指定的图像的参数。

现在两全其美:可以处理WebP的浏览器可以得到WebP,不能处理的浏览器将回退到JPEG。此外,由于picture元素中的img标签是回退标签,因此它可以在不支持picture本身的浏览器中工作,因此该方法可以使用任何格式而不必担心不兼容。

使用Picturefill提供polyfill支持

尽管srcset和picture都很有用,但它们的浏览器支持并不是通用的。值得庆幸的是,可以通过一个名为Picturefill的11KB小脚本,在不支持这些标签浏览器中使用它们。

使用Modernizr选择性加载Picturefill

Modernizr是一个健壮的特性检测库,它为检测浏览器对某些特性的支持提供了一种简单的方法。我在本节构建了一个1.8KB的自定义Modernizr,它只包含picture和srcset的特性检测代码。

使用Modernizr来避免在现代浏览器中加载11KB的Picturefill库,方法是首先检查浏览器是否需要它。如果任意一个特性的检测失败,则加载PictureFill;如果两者都成功,则不加载Picturefill,这样也就省去了浏览器下载非必要代码的麻烦。

◆ 7.4 优化字体加载

不可见文本的闪烁类似于无样式内容的闪烁(Flash of Unstyled Content,FOUC)异常,只是前者发生在文档的字体完全加载之前,处理的是不可见的文本,而不是无样式的内容

使用CSS font-display属性

控制此行为的一种方法是使用CSS中的font-display属性。虽然不受普遍支持,但此属性可以使你在很大程度上控制字体的显示方式。该属性需要放在@font-face声明中,并接受以下值之一。❏ auto——默认值。在大多数浏览器中,这类似于block。❏ block——阻塞文本的渲染,直到关联的字体加载完成。这是上一节中描述的效果,也是你试图克服的。❏ swap——首先显示回退文本。加载字体后,将字体切换为自定义字体。❏ fallback——auto和swap的折衷方案。短时间内(大约100毫秒)文本是不可见的。如果之后字体仍未加载,则会显示回退文本。加载字体后,将字体切换为自定义字体。❏ optional——几乎和fallback一样,只是浏览器有更大的自由来决定是否下载或应用字体。用户的网络连接足够慢时,此设置生效。该设置对于自定义字体完全可选的网站特别有用。

设置该属性后,在较慢的网络节流配置下重新加载页面,将看到文本逐步显示,而不会被浏览器隐藏。

◆ 第8章 保持JavaScript的简洁与快速

❏ 影响script标签的加载行为❏ 将jQuery替换为更小、更快、API兼容的替代方案❏ 使用原生JavaScript方法替换jQuery功能❏ 使用requestAnimationFrame方法实现动画

◆ 8.1 影响脚本加载行为

使用异步脚本加载

如果没有任何脚本具有依赖项,则可以自由使用async。但是当脚本具有依赖性时,事情就会变得棘手。解决此问题的一种方法是将依赖脚本组合起来,以便将这些依赖打包为单个资源。在本例中,可以按顺序组合jquery.min.js和behaviors.js。在命令行中运行此命令,将两个脚本合并为scripts.js

◆ 8.2 使用更简洁的兼容jQuery的替代方案

比较三个兼容jQuery的库:Zepto、Shoestring和Sprint,它们各自的详细信息如下。❏ Zepto被描述为一个轻量级的兼容jQuery的JavaScript库。在所有jQuery备选方案中,它是最富开箱即用特性的,并且可以扩展以完成更多工作。它是这个领域最受欢迎的选择。❏ Shoestring是由Filament Group编写的。它提供的功能比Zepto少,但提供了jQuery提供的大多数核心DOM遍历和操作方法,对$.ajax方法的支持有限。❏ Sprint是最轻量、功能最简单的jQuery替代品,但其性能很高。虽然它提供的功能并没有那么多,但是当你想从很少的功能开始时,它很适合,并且在需要时可以添加一个更为强大的库。比较这些库时,要考虑每个库的大小和等效方法的性能。

◆ 8.3 脱离jQuery编码

脱离jQuery编码

使用querySelector和querySelectorAll选择元素、使用addEventListener绑定事件、使用classList操作元素上的类、使用setAttribute和innerHTML修改属性和元素内容,以及使用Fetch API进行AJAX调用。

jQuery检查DOM是否准备就绪

在jQuery中,任何封装在$(function(){})中的内容都要在加载并准备好文档后才能执行

为了在原生JavaScript中实现这一点,使用addEventListener

检查DOM是否准备就绪

使用addEventListener检查DOM是否准备就绪

如果需要支持IE9之前的IE版本,可以使用document.onreadystatechange方法监听DOM就绪情况。这种方法也适用于较新的浏览器。

querySelector返回与表达式匹配的第一个元素,而querySelectorAll返回所有匹配的元素

使用classList操作元素上的类

客户网站的JavaScript广泛使用jQuery的addClass和removeClass方法来添加和删除类。而原生方法classList也提供了相同的功能

如果不支持classList怎么办?你可能需要支持IE9或更低版本。此时可以改用className属性,该属性没有用于添加、删除或切换类的方法。相反,它是一个字符串,你可以使用它将所需的类分配给目标元素。虽然它不像classList那么方便,但可以在紧要关头正常工作。

在没有jQuery的$(this)对象的情况下,可以使用事件对象的target属性,引用事件在事件代码内部绑定的元素

使用Fetch API发起AJAX请求

在过去的AJAX请求中,必须使用XMLHttpRequest请求对象。这是一种处理AJAX请求的笨办法,不同浏览器需要不同的方法。jQuery通过围绕XMLHttpRequest对象包装了自己的AJAX功能,使AJAX请求成为一个更简便的任务。到目前为止它仍然工作得很好,但有一些浏览器已经实现了名为Fetch API的原生资源获取API。

可以在https://github.com/github/fetch上找到Fetch API的一个健壮的polyfill。

◆ 8.4 使用requestAnimationFrame设置动画

计时器函数驱动的动画和requestAnimationFrame

在JavaScript中设置动画时,可以通过元素的style对象,使用计时器函数更改元素在屏幕上的外观或位置,以呈现运动迹象。可以说,setTimeout和setInterval过去就是用于在一个间隔内设置元素动画的计时器函数,间隔通常为1000 ms/60,目的是以大约每秒60帧的速度设置效果动画

计时器本身成本不高,但它们对于动画代码来说不是最佳选择。为了解决这一问题,人们开发了requestAnimationFrame方法

因为与setTimeout不同,requestAnimationFrame不允许用户以毫秒为单位指定间隔。那么它是如何工作的呢?原理很简单:间隔在requestAnimationFrame内部处理,它根据显示的刷新率(大多数设备上的刷新率往往为60 Hz)起作用;requestAnimationFrame在典型设备上的目标是60帧速率。如果设备具有不同的刷新率,那么requestAnimationFrame将相应匹配。

Velocity.js,这是一个简单的requestAnimationFrame驱动的JavaScript动画库。

了解Velocity.js

上述对requestAnimationFrame的这种尝试可能会给你留下更多问题,而不是答案。使用此方法代替CSS过渡或jQuery动画可能会很有挑战性,特别是在需求很复杂的情况下。本节简要介绍Velocity.js,它可以使动画与jQuery的动画方法一样方便。Velocity.js是一个动画库,它使用的API类似于jQuery的animate方法。Velocity最好的一点是它独立于jQuery。但如果你的项目使用jQuery,且严重依赖其animate方法,那么加入Velocity.js将使动画过程与jQuery相同

简单地从animate改为velocity,你就使用Velocity动画引擎代替了jQuery,它提供了平滑的requestAnimationFrame驱动的性能,以及使动画具有自然运动感的简化功能。与jQuery的animate方法不同,它允许为颜色、过渡和滚动设置动画

如果使用不带jQuery的Velocity.js,语法会有一些变化。当Velocity加载时,它将检查是否加载了jQuery

除了语义之外,这个语法没有太大的区别。无论有没有jQuery, Velocity.js都可以使JavaScript驱动的动画更加流畅和高效,而用户不必编写自己的动画代码。

请注意,这个库经过最小化和压缩后大约是13KB,所以只有在网站动画和性能非常重要时才应考虑使用它。在一个没有太多动画的网站上增加13KB的开销将影响用户体验。你也可以通过编写自己的动画代码或使用CSS过渡来更好地提供服务。

通过原生JavaScript方法代替jQuery,可以实现更多功能。我推荐一个很好的网站:You Might Not Need jQuery。该网站上有很多常见(和不常见)jQuery行为的代码片段,及其原生等价方法。它还允许你指定所需的浏览器兼容性级别。如果本附录未提及你想知道的内容,可以看看这个网站。如果依然无法解决,就使用Google搜索吧!总有人会发现解决方案的!