前端性能优化(二)
前端优化之渲染网页
优化关乎速度与满意度。
- 从用户体验(UX)角度,我们希望前端页面可以快速加载。
- 从开发体验(DX)角度,我们希望前端是快速、简洁、规范的。
浏览器如何渲染网页
- 使用HTML创建文档对象模型(DOM)。
- 使用CSS创建CSS对象模型(CSSOM)。
- 基于DOM和CSSOM执行脚本(Script)。
- 合并DOM和CSSOM形成渲染树(Render Tree)。
- 使用渲染树布局(Layout)所有元素。
- 渲染(Paint)所有元素。
步骤一 HTML
浏览器从上到下读取标签,把他们分解成节点,从而创建DOM。
HTML加载优化策略
-
样式在顶部,脚本在底部。 总体思路是尽可能早的加载样式,尽可能晚的加载脚本。原因是脚本执行之前,需要HTML和CSS解析完成,因此,样式尽可能的往顶部放,当底部脚本开始执行之前,样式有足够的时间完成计算。
-
最小化和压缩 方法可用于所有内容,包括HTML,CSS,Javascript,图片和其他资源。 最小化是移除所有多余的字符,包括空格,注释,多余的分号,等等。 压缩比如GZip,大大的压缩下载文件的大小。 两种方法都用的情况下,资源加载量大大的减少80%-90%。
-
无障碍 不会提升页面的下载速度,但会大大提升满意度,给元素加上aria属性,图片加上alt属性。
步骤二 CSS
当浏览器发现任何与节点相关的样式时,比如外部,内部,行内样式,立即停止渲染DOM,并利用这些节点创建CSSOM,这就是CSS“渲染阻塞”的由来。
// 外部样式
<link rel="stylesheet" href="style.css" />
// 内部样式
<style>
h1 {
font-size: 18px;
}
</style>
// 行内样式
<button style="background-color: blue">按钮</button>
CSSOM节点创建与DOM节点创建类似,随后,两者合并如下:
CSSOM的构建会阻塞页面的渲染,因此我们要尽早的加载样式。
步骤三 Javascript
浏览器不断构建DOM/CSSOM节点,直到发现外部或行内的脚本。由于脚本可能需要访问或操作之前的HTML或样式,我们必须要等他们构建完成。因此浏览器必须停止解析节点,完成构建CSSOM,执行脚本,然后再继续。这就是Javascript被称作为“解析器阻塞”的原因。
脚本只能等到先前的CSS节点构建完成。
Javascript加载优化策略
- 异步加载脚本 脚本添加async属性,可以通知浏览器不要阻塞其余页面的加载,下载脚本处于较低的优先级。一旦下载完成,就可以执行。
async
适用于不影响DOM或CSSOM的脚本,对一些跟我们的代码无关的,不影响用户体验的外部脚本尤其适用,比如分析统计脚本。
- 延迟加载脚本
defer
和async
非常类似,不会阻塞页面的加载,但会等到HTML完成解析后再执行。
使用defer
策略的另一个好的选择是addEventListener
。注意,async
和defer
对于行内的脚本不起作用,浏览器默认会编译执行他们。
- 操作之前克隆节点 多次操作DOM时可以尝试,首先克隆整个DOM节点更加高效,操作克隆后的节点,然后替换先前的节点,避免了多次重绘,降低CPU和内存消耗,同时也避免了不必要的页面闪烁。注意,克隆的时候并没有克隆事件监听。
步骤四 渲染树
一旦所有的节点已被解析,DOM和CSSOM准备合并,浏览器便会构建渲染树,如果我们把节点想象成单词,那么对象模型就是句子,渲染树就是整个页面。
步骤五 布局
布局阶段需要确定页面上所有元素的大小和位置。
步骤六 渲染
最终的渲染阶段,会真正的光栅化屏幕上的像素,把页面呈现给用户。
整个过程耗时1秒或十分之一秒,我们的任务是让他更快。
如果Javascript事件改变了页面的部分,便会引起渲染树的重绘,并且迫使布局和渲染过程再次进行。
前端优化之网络请求
浏览器如何发起请求
当浏览器请求一个URL,服务端会响应一些HTML。
我们需要认识一个新术语,关键渲染路径(Critical Rendering Path(CRP)),就是浏览器渲染页面的步骤数,如下图。
关键路径长度
关键渲染路径的度量标准是路径长度,最理想的的关键路径长度是1。
如果一个页面包含一些内部样式和Javascript,关键路径会发生以下改变。
新增两步,构建CSSOM和执行脚本,因为我们的HTML有内部样式和脚本需要计算,由于没有外部请求,我们的关键路径长度没变。但是,我们的HTML大小增加到了2kb,某些地方还是有影响的。
关键字节数
三个度量标准之二出现了,关键字节数,他用来衡量渲染页面需要传送多少字节数。
如果你认为不需要外部资源就大错特错了,外部资源可以被缓存。
我们使用一个外部CSS文件,一个外部JavaScript文件,和一个外部带async
属性的Javascript文件。关键路径图如下:
浏览器请求页面,构建DOM,发现外部资源后开始下载,CSS和JavaScript有较高的优先级,其他资源次之。
styles.css
和app.js
通过另一个关键路径获取。暂时不获取analytics.js
,是因为加了async
属性,浏览器将用另一个线程下载它,它处于较低优先级,不会阻塞页面渲染,也不影响关键路径。
关键文件
最后一个度量标准就是关键文件,浏览器渲染页面需要下载的文件总量。以上例子,HTML文件,CSS和JavaScript文件算关键文件,async
的脚本不算。当然是文件越少越好。
回到关键路径长度
以上例子就是最短的渲染路径么?我认为渲染页面时,我们仅需要下载HTML,CSS和Javascript文件,仅通过两次服务器往返就做到了。
HTTP1 文件限制
我们浏览器的http1协议,在同一个域名,同一次,允许下载的文件数有最大限制,范围从2到6。 通过把一些资源放到影子域名,可以绕过这个限制,以达到最佳优化效果。
注意:不要把关键的CSS放到根域名以外的其它域名,有些场景会对DNS查找和延迟起反作用。
HTTP2
如果网站使用HTTP2,并且用户的浏览器也兼容,则可以完全避免这个下载限制。
TCP往返限制
每一次服务器往返可以传送的最大数据量是14kb,包括所有HTML,CSS和脚本的网络请求。
如果我们HTML,或积累的资源请求超过14kb时,需要多做一次服务器往返。
大魔法师
我们整个HTML页面可以很好的压缩,GZip可以压缩到2kb,远低于14kb的限制,因此,一次服务器往返就可以搞定。
- 关键路径度量:长度 1,文件数 1,字节数 2kb。
浏览器发现外部资源(CSS和JavaScript)时,发起请求开始下载它们。需要下载的CSS文件是14kb,达到了往返传输的最大限制,因此增加了一条关键路径。
- 关键路径度量:长度 2,文件数 2,字节数 16kb。
余下的资源低于14kb,但是总共有7个资源,由于网站未启用HTTP2,我们的Chrome,每一次往返仅可以下载6个文件。
- 关键路径度量:长度 3,文件数 8,字节数 28kb
下载完最终文件,并开始渲染DOM。
- 关键路径度量:长度 4,文件数 9,字节数 30kb
基于以上的信息和知识,发起每一个链接时,就可以准确地预估页面的性能了。
浏览器网络优化策略
-
Pagespeed Insights 使用Insights鉴别性能问题,Chrome DevTools也有个audit标签。
-
充分利用Chrome开发者工具 这篇文章值得一读,帮你理解网络资源。
-
在优质的环境里开发,在艰苦的环境里测试 开发时大可以使用1Tb SSD,32G内存的Macbook Pro,但是性能测试时候还是要到Chrome的network下模拟低带宽的情况,从而获取有价值的信息。
-
合并资源/文件 基本上,每接受到一个外部CSS和JavaScript文件,浏览器都会构建CSSOM,执行脚本。尽管几个文件可以在一次往返中传送,但也浪费了浏览器的宝贵时间和资源,最好还是合并文件,减少不必要的加载。
-
首屏内容使用内部样式 内部CSS和Javascript不需要请求外部资源,相反,外部资源可以被缓存,并保持DOM轻量,两者没有非黑即白。
但是一个非常好的论点就是首屏关键内容使用内部样式,可以避免请求额外的资源,节省时间做最有意义的渲染。
- 最小化/压缩图片
- 延迟加载图片
- 异步记载字体
- 是否真的需要Javascript/CSS?
原生HTML元素可以实现的行为是否用了脚本,是否有样式或图标可以行内创建的,不需要内部/外部资源,比如行内SVG
- CDN
可以利用CDN(内容分发网络)存储资源,它会从离用户最近,延迟最低的位置分发到用户设备,加载时间更快。
延伸阅读
综述
关键渲染路径是最重要的,它使得网站优化有规律可循。
需要关注的3个指标: 1.关键字节数 2.关键文件数 3.关键路径长度