CRPとは何ですか?
CRP
MDN
説明を引用すると、クリティカルレンダリングパスとも呼ばれます。
❝クリティカルレンダリングパスとは、ブラウザがHTML、CSS、JavaScriptを画面上のピクセルに変換する一連のステップを指します。クリティカルレンダリングパスを最適化すると、レンダリングパフォーマンスが向上します。クリティカルレンダリングパスには、ドキュメントオブジェクトモデル(DOM)、CSSオブジェクトモデル(CSSOM)、レンダリングツリーおよびレイアウトが含まれます。
❞
クリティカルレンダリングパスを最適化すると、最初の画面のレンダリング時間が短縮されます。クリティカルレンダリングパスを理解して最適化することは、毎秒60フレームでリフローと再描画を確実に実行し、高性能なユーザー操作を保証し、無意味なレンダリングを回避するために不可欠です。
CRP
パフォーマンスの最適化を組み合わせる方法は?
誰もがパフォーマンスの最適化に慣れていると思います。それが通常の仕事であろうと面接であろうと、それは共通のトピックです。
一般的にいくつかのポイントについてのみ話すと、それほど厳密ではないと思います。
今日は非常に古典的なインタビューの質問を組み合わせ从输入URL到页面展示,这中间发生了什么?
ます前端性能优化 CRP
。いくつかのリンクから詳細に話しましょう。
URLの入力からページの表示まで、何が起こりましたか?
この質問の従来のレベルについてこれ以上説明する必要はありません。ここでは、画像を使用して一般的なプロセスを整理します。このプロセスは、次のように大まかに説明できます。
1、URI 解析
2. DNS解決(DNSサーバー)
3. TCP 3ウェイハンドシェイク(クライアントとサーバー間の接続チャネルを確立)
4. HTTPリクエストを送信する
5.サーバーの処理と応答
6. TCPが4回振られた(クライアントとサーバー間の接続を閉じる)
7.ブラウザの解析とレンダリング
8.ページが読み込まれます
この記事では、ブラウザーのレンダリングプロセス、キャッシュ、DNS最適化によるパフォーマンスの最適化について説明します。
ブラウザのレンダリングプロセス
DOMツリーを構築する
DOM
ツリーを構築する一般的なプロセスを次の図にまとめます。
分析の例として、次のコードを取り上げます。
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
<title>构建DOM树</title>
</head>
<body>
<p>森林</p>
<div>之晨</div>
</body>
</html>
最初に、ブラウザーはディスクまたはネットワークのHTML
未加工バイトを読み取り、指定されたファイルに従ってコード化して、それらを文字に変換します。
然后通过分词器将字节流转换为 Token
,在Token
(也就是令牌)生成的同时,另一个流程会同时消耗这些令牌并转换成 HTML head
这些节点对象,起始和结束令牌表明了节点之间的关系。
当所有的令牌消耗完以后就转换成了DOM
(文档对象模型)。
最终构建出的DOM
结构如下:
构建 CSSOM 树
DOM
树构建完成,接下来就是CSSOM
树的构建了。
与HTML
的转换类似,浏览器会去识别CSS
正确的令牌,然后将这些令牌转化成CSS
节点。
❝子节点会继承父节点的样式规则,这里对应的就是层叠规则和层叠样式表。
❞
构建DOM
树的大致流程可梳理为下图:
我们这里采用上面的HTML
为例,假设它有如下 css:
body {
font-size: 16px;
}
p {
font-weight: bold;
}
div {
color: orange;
}
那么最终构建出的CSSOM
树如下:
有了 DOM
和 CSSOM
,接下来就可以合成布局树(Render Tree)了。
构建渲染树
等 DOM
和 CSSOM
都构建好之后,渲染引擎就会构造布局树。布局树的结构基本上就是复制 DOM
树的结构,不同之处在于 DOM
树中那些不需要显示的元素会被过滤掉,如 display:none
属性的元素、head
标签、script
标签等。
复制好基本的布局树结构之后,渲染引擎会为对应的 DOM
元素选择对应的样式信息,这个过程就是样式计算。
样式计算
样式计算的目的是为了计算出 DOM
节点中每个元素的具体样式,这个阶段大体可分为三步来完成。
把 CSS 转换为浏览器能够理解的结构
和 HTML
文件一样,浏览器也是无法直接理解这些纯文本的 CSS
样式,所以当渲染引擎接收到 CSS
文本时,会执行一个转换操作,将 CSS
文本转换为浏览器可以理解的结构——styleSheets
。
转换样式表中的属性值,使其标准化
现在我们已经把现有的 CSS 文本转化为浏览器可以理解的结构了,那么接下来就要对其进行属性值的标准化操作。
什么是属性值标准化?我们来看这样的一段CSS
:
body {
font-size: 2em;
}
div {
font-weight: bold;
}
div {
color: red;
}
可以看到上面的 CSS
文本中有很多属性值,如 2em、bold、red,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。
那标准化后的属性值是什么样子的?
从图中可以看到,2em
被解析成了 32px
,bold
被解析成了 700
,red
被解析成了 rgb(255,0,0)
……
计算出 DOM 树中每个节点的具体样式
现在样式的属性已被标准化了,接下来就需要计算 DOM
树中每个节点的样式属性了,如何计算呢?
这其中涉及到两点:CSS 的继承规则
和层叠规则
。
这里由于不是本文的重点,我简单做下说明:
CSS
继承就是每个DOM
节点都包含有父节点的样式CSS
基本的な機能に積層されており、複数のソースからアルゴリズムをマージする方法を定義するプロパティ値です。それCSS
がCSS
まさにこの点を強調するための核となる「カスケードスタイルシート」の正式名称です。
スタイルの計算が完了したら、レンダリングエンジンは、レイアウトツリー内の各要素の幾何学的位置も計算する必要があります。このプロセスでは、レイアウトを計算します。
計算レイアウト
さて、DOM
ツリーにはツリーとDOM
スタイル要素がありますが、DOM
情報要素の幾何学的位置がわからないため、ページを表示するのに十分ではありません。したがってDOM
、ツリー表示要素の幾何学的位置を計算する必要があります布局
。この計算はと呼ばれます。
ドロー
最終的なレイアウトツリーの構築は、スタイル計算とレイアウト計算によって完了します。その後、次の描画操作の時間です。
この時点で、ブラウザのレンダリングプロセスは基本的に終了しており、次の図で整理されています。
この時点で、ブラウザーの解析とレンダリングのプロセス全体を整理したので、どこでパフォーマンスを最適化できますか?
ブラウザーのレンダリングプロセスから実行できる最適化ポイント
通常、ページには3つのステージがあります。ロードステージ、インタラクティブステージ、クローズステージです。
ロードフェーズは、プロセスの完全なページをレンダリングする要求からのもの
JavaScript
です。この段階に影響を与える主な要因は、ネットワークとスクリプトです。インタラクティブステージ、特にページからロードユーザーインタラクションまでの統合プロセスの完了
JavaScript
。このステージに影響を与える主な要因はスクリプトです。クローズフェーズでは、主に、ユーザーがクローズ命令を発行した後にページによって実行されるクリーニング操作です。
ここでは、に焦点を当てる必要がある加载阶段
と交互阶段
私たちの経験に影響を与える要因はこれらの二つの段階に主にあるため、のいずれかで、詳細1に分析してみましょう。
読み込みフェーズ
次の図に示すように、最初に読み込みフェーズでページを体系的に最適化する方法を分析し、一般的なレンダリングパイプラインを見てみましょう。
通过上面对浏览器渲染过程的分析我们知道JavaScript
、首次请求的 HTML
资源文件、CSS
文件是会阻塞首次渲染的,因为在构建 DOM
的过程中需要 HTML
和 JavaScript
文件,在构造渲染树的过程中需要用到 CSS
文件。
这些能阻塞网页首次渲染的资源称为关键资源
。而基于关键资源,我们可以继续细化出三个影响页面首次渲染的核心因素:
关键资源个数
。关键资源个数越多,首次页面的加载时间就会越长。关键资源大小
。通常情况下,所有关键资源的内容越小,其整个资源的下载时间也就越短,那么阻塞渲染的时间也就越短。请求关键资源需要多少个RTT(Round Trip Time)
。RTT
是网络中一个重要的性能指标,表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延。
了解了影响加载过程中的几个核心因素之后,接下来我们就可以系统性地考虑优化方案了。总的优化原则就是减少关键资源个数
,降低关键资源大小
,降低关键资源的 RTT 次数
:
如何减少关键资源的个数?一种方式是可以将
JavaScript
和CSS
改成内联的形式,比如上图的JavaScript
和CSS
,若都改成内联模式,那么关键资源的个数就由 3 个减少到了 1 个。另一种方式,如果JavaScript
代码没有DOM
或者CSSOM
的操作,则可以改成sync
或者defer
属性如何减少关键资源的大小?可以压缩
CSS
和JavaScript
资源,移除HTML
、CSS
、JavaScript
文件中一些注释内容如何减少关键资源
RTT
的次数?可以通过减少关键资源的个数和减少关键资源的大小搭配来实现。除此之外,还可以使用CDN
来减少每次RTT
时长。
交互阶段
接下来我们再来聊聊页面加载完成之后的交互阶段以及应该如何去优化。
先来看看交互阶段的渲染流水线:其实这块大致有以下几点可以优化:
避免DOM的回流
。也就是尽量避免重排
和重绘
操作。减少 JavaScript 脚本执行时间
。有时JavaScript
函数的一次执行时间可能有几百毫秒,这就严重霸占了主线程执行其他渲染任务的时间。针对这种情况我们可以采用以下两种策略:-
一种是将一次执行的函数分解为多个任务,使得每次的执行时间不要过久。
另一种是采用
Web Workers
。
DOM操作相关的优化
。浏览器有渲染引擎
和JS引擎
,所以当用JS
操作DOM
时,这两个引擎要通过接口互相“交流”,因此每一次操作DOM
(包括只是访问DOM
的属性),都要进行引擎之间解析的开销,所以常说要减少 DOM 操作。总结下来有以下几点:-
缓存一些计算属性,如
let left = el.offsetLeft
。通过
DOM
的class
来集中改变样式,而不是通过style
一条条的去修改。分离读写操作。现代的浏览器都有渲染队列的机制。
放弃传统操作
DOM
的时代,基于vue/react
等采用virtual dom
的框架
合理利用 CSS 合成动画
。合成动画是直接在合成线程上执行的,这和在主线程上执行的布局、绘制等操作不同,如果主线程被JavaScript
或者一些布局任务占用,CSS
动画依然能继续执行。所以要尽量利用好CSS
合成动画,如果能让CSS
处理动画,就尽量交给CSS
来操作。CSS选择器优化
。我们知道CSS引擎
查找是从右向左匹配的。所以基于此有以下几条优化方案:-
尽量不要使用通配符
少用标签选择器
尽量利用属性继承特性
CSS属性优化
。浏览器绘制图像时,CSS
的计算也是耗费性能的,一些属性需浏览器进行大量的计算,属于昂贵的属性(box-shadows
、border-radius
、transforms
、filters
、opcity
、:nth-child
等),这些属性在日常开发中经常用到,所以并不是说不要用这些属性,而是在开发中,如果有其它简单可行的方案,那可以优先选择没有昂贵属性的方案。避免频繁的垃圾回收
。我们知道JavaScript
使用了自动垃圾回收机制,如果在一些函数中频繁创建临时对象,那么垃圾回收器也会频繁地去执行垃圾回收策略。这样当垃圾回收操作发生时,就会占用主线程,从而影响到其他任务的执行,严重的话还会让用户产生掉帧、不流畅的感觉。
缓存
缓存可以说是性能优化中简单高效的一种优化方式了。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。下图是浏览器缓存的查找流程图:浏览器缓存相关的知识点还是很多的,这里我有整理一张图:关于浏览器缓存的详细介绍说明,可以参考我之前的这篇文章,这里就不赘述了。
DNS 相关优化
DNS
全称Domain Name System
。它是互联网的“通讯录”,它记录了域名与实际ip
地址的映射关系。每次我们访问一个网站,都要通过各级的DNS
服务器查询到该网站的服务器ip
,然后才能访问到该服务器。
DNS
相关的优化一般涉及到两点:浏览器DNS
缓存和DNS
预解析。
DNS
缓存
一图胜千言:
浏览器会先检查浏览器缓存(浏览器缓存有大小和时间限制),时间过长可能导致
IP
地址变化,无法解析正确IP
地址,过短就会让浏览器重复解析域名,一般为几分钟。如果浏览器缓存没有对应域名,则会去操作系统缓存中查找。
如果还没有找到,域名就会发送到本地区的域名服务器(一般由互联网供应商提供,电信、联通之类),一般在本地区的域名服务器上都能找到了。
当然也可能本地域名服务器也没找到,那本地域名服务器就开始递归查找。
一般而言,浏览器解析DNS
需要20-120ms
,因此DNS
解析可优化之处几乎没有。但存在这样一个场景,网站有很多图片在不同域名下,那如果在登录页就提前解析了之后可能会用到的域名,使解析结果缓存过,这样缩短了DNS
解析时间,提高网站整体上的访问速度了,这就是DNS预解析
。
DNS
预解析
来看下 MDN 对于DNS预解析
的定义吧:
❝❞
X-DNS-Prefetch-Control
头控制着浏览器的DNS
预读取功能。DNS
预读取是一项使浏览器主动去执行域名解析的功能,其范围包括文档的所有链接,无论是图片的,CSS
的,还是JavaScript
等其他用户能够点击的URL
。
先読みはバックグラウンドで実行DNS
されるため、リンクが対応しているように見える前に解決されている可能性があります。これにより、ユーザーがリンクをクリックしたときの遅延を減らすことができます。
ここでそれを行う方法を簡単に見てみましょうDNS预解析
:
ブラウザがページ全体を事前に解析できるように、ページのヘッダーに追加します
<meta http-equiv="x-dns-prefetch-control" content="on">
リンクタグを使用して、解決するドメイン名を手動で追加します。次に例を示します。
<link rel="dns-prefetch" href="//img10.360buyimg.com"/>
参照
Li Bing「ブラウザの動作原理と実践」
「監視と転送」が最大のサポート