人法地,地法天,天法道,道法天然。javascript
若是想看更深刻的原理,能夠看:java
別人翻譯的外國友人的渲染原理node
關鍵渲染路徑(Critical Rendering Path)
是指與當前用戶操做有關的內容。例如用戶剛剛打開一個頁面,首屏的顯示就是當前用戶操做相關的內容,具體就是瀏覽器收到HTML、CSS 和 JavaScript
等資源並對其進行處理從而渲染出 Web
頁面。 以下圖所示渲染流程: web
- 瀏覽器首先經過
HTTP
協議或者HTTPS
協議,向服務器請求頁面,固然這個其中也可能有緩存什麼的;- 把請求回來的
HTML
代碼通過解析,構建成DOM
樹;- 計算
DOM 樹
上的CSS
屬性,生成CSSOM 樹(CSS Object Model)
;- 將
DOM 樹
和CSSOM 樹
合併成一個渲染樹(rendering tree)
;- 渲染樹的每一個元素包含的內容都是
計算
過的,它被稱之爲佈局 layout
。瀏覽器使用一種流式處理的方法,只須要一次pass 繪製
操做就能夠佈局全部的元素;- 將渲染樹的各個節點繪製到屏幕上,這一步被稱爲
繪製 painting
;- 按照合理的順序合併圖層而後顯示到屏幕上
Composite(渲染層合併)
在咱們在瀏覽器中輸入完網址的時候,瀏覽器其實會先作如下幾小步:瀏覽器
DNS 查詢
(就是把當前域名解析成爲 ip 地址)TCP 鏈接
HTTP 請求響應
- 服務器返回數據
在構建 DOM 樹
的時候又能夠分爲幾小步:緩存
- 字符流經過狀態機解析成爲
詞 token
- 詞
token => prase => DOM 樹
構建 DOM
的過程是:從父到子,從先到後,一個一個節點構造,DOM 樹
結構和 HTML 標籤一一對應。服務器
在計算 css 規則
的時候,咱們會在已經構建好的元素上,去檢查它匹配到了哪些規則,再根據規則的優先級,作覆蓋和調整。而且 CSSOM
主要是DOM 結構
上的盒的描述,他基本上是依附於 DOM 樹
的。 CSS 計算
是把 CSS 規則
應用到 DOM 樹
上,爲 DOM 結構
添加顯示相關屬性過程。 CSSOM
是有 rule
部分和 view
部分的,rule 部分
是在 dom
開始以前就構件完成的,而 view
部分是跟着 dom
同步構建的。
經過 DOM 樹
和 CSS 規則樹
,瀏覽器就能夠經過它兩構建渲染樹
了。 渲染樹
和 DOM 元素
相對應的,但並不是一一對應。非可視化的 DOM 元素
不會插入呈現樹中,例如「head」元素
。若是元素的 display
屬性值爲「none」
,那麼也不會顯示在呈現樹中(可是 visibility
屬性值爲「hidden」
的元素仍會顯示)。
呈現器在建立完成並添加到呈現樹時,並不包含位置和大小信息。計算這些值的過程稱爲佈局
或重排
。 佈局階段會從渲染樹
更新節點開始遍歷,因爲渲染樹的每一個節點都是一個 Render Object
對象,包含寬高,位置,背景色等樣式信息。瀏覽器中渲染這個過程,就是把每個元素對應的盒變成位圖
,再把位圖合成一個大的位圖
。 佈局又分爲全局佈局和增量佈局,詳情請看
在繪製階段,系統會遍歷呈現樹
,並調用呈現器的「paint」方法
,將呈現器
的內容顯示在屏幕上。繪製工做是使用用戶界面基礎組件完成的。 繪製又分爲全局繪製
和增量繪製
,而且繪製的屬性也會有先後之分,詳情請看
渲染過程把元素變成位圖
,合成把一部分位圖變成合成層
,最終的繪製過程把合成層
顯示到屏幕上。 對於 transform/opacity
這兩種變換,瀏覽器不會用 repaint/reflow
處理,而是在已經渲染的元素基礎上進行附加工做。 他的渲染流程爲下圖所示:
談論資源的阻塞時,咱們要清楚,現代瀏覽器老是並行加載資源。例如,當 HTML 解析器(HTML Parser)
被腳本阻塞時,解析器雖然會中止構建 DOM,但仍會識別該腳本後面的資源,並進行預加載。 同時,因爲下面兩點:
- 默認狀況下,
CSS 被視爲阻塞渲染的資源
,這意味着瀏覽器將不會渲染任何已處理的內容,直至CSSOM 構建完畢
。JavaScrip
t 不只能夠讀取和修改 DOM 屬性
,還能夠讀取和修改 CSSOM
屬性,所以CSS 解析
與script 的執行
互斥。- 存在
阻塞的 CSS 資源
時,瀏覽器會延遲 JavaScript 的執行和 DOM 構建
。
正是因爲以上這些緣由,script 標籤
的位置很重要咱們在實際開發中應該儘可能堅持如下兩個原則: 在引入順序上,CSS 資源先於 JavaScript 資源。 JavaScript 應儘可能少的去影響 DOM 的構建。 想理清楚 CSS、JavaScript、DOM
之間的相互阻塞關係
咱們熟知的javascript
標籤上defer
和async
屬性,還有可能不太熟知的link
標籤上的preload
屬性。
在介紹 async 和 defer 以前咱們要先看了解兩個概念,load
和DOMContentLoaded
的執行時機
load Load 事件觸發表明頁面中的 DOM,CSS,JS,圖片已經所有加載完畢。 DOMContentLoaded DOMContentLoaded 事件觸發表明初始的 HTML 被徹底加載和解析,不須要等待 CSS,JS,圖片加載。
async 和 defer 他們對於內聯腳本無做用(即沒有 src 屬性的腳本) async 該布爾屬性指示瀏覽器是否在容許的狀況下異步執行該腳本。async 與 defer 的區別在於,若是已經加載好,就會開始執行——不管此刻是 HTML 解析階段仍是 DOMContentLoaded 觸發以後。須要注意的是,這種方式加載的 JavaScript 依然會阻塞 load 事件。換句話說,async-script 可能在 DOMContentLoaded 觸發以前或以後
執行,但必定在 load 觸發以前
執行。而且多個 async-script 的執行順序是不肯定
的。
defer defer 屬性表示延遲執行引入的 JavaScript,即這段 JavaScript 加載時 HTML 並未中止
解析,這兩個過程是並行
的。整個 document 解析完畢且 defer-script 也加載完成以後(這兩件事情的順序無關),會執行全部由 defer-script 加載的 JavaScript 代碼,而後觸發
DOMContentLoaded 事件。
defer 與相比普通 script,有兩點區別:載入 JavaScript 文件時不阻塞
HTML 的解析,執行階段被放到 HTML 標籤解析完成
以後。
preload
<link>
元素的 rel 屬性
的屬性值preload
可以讓你在你的HTML頁面
中 <head>元素
內部書寫一些聲明式的資源獲取請求,能夠指明哪些資源是在頁面加載完成後即刻須要的
。對於這種即刻須要的資源,你可能但願在頁面加載的生命週期的早期階段就開始獲取,在瀏覽器的主渲染機制介入前就進行預加載
。這一機制使得資源能夠更早的獲得加載並可用,且更不易阻塞頁面的初步渲染,進而提高性能。 預加載能夠必定程度上下降首屏的加載時間,由於能夠將一些不影響首屏但重要的文件延後加載,惟一缺點就是兼容性很差.
prerender 能夠經過預渲染將下載的文件預先在後臺渲染
,可使用如下代碼開啓預渲染
預渲染雖然能夠提升頁面的加載速度
,可是要確保該頁面百分百會被用戶在以後打開,不然就白白浪費資源去渲染
這個裏面基本上了解了瀏覽器的渲染過程,可是有不少細節沒有套路好比說咱們都知道瀏覽器是單線程的,ui 線程
和 javascript 線程
是怎麼協調的,還有一個比較重要的是重繪和迴流(重排)
。