文章目錄
- 一、script 元素
- 1.網頁運行原理
- (1)作業流程
- (2)網頁加載流程
- (3).注意
- 2. defer屬性
- 3. async 屬性
- 4. 對比及使用
- 1.對比
- 2.使用
- 二、重繪(repaint)
- 1.介紹
- 三、回流 (Reflow)
- 1.介紹
- 2.導致回流的方式:
- 3.一些常用且會導致回流的屬性和方法:
- 四、比較及避免
- 1.比較
- 2.避免
- css:
- JS:
一、script 元素
1.網頁運行原理
(1)作業流程
瀏覽器核心是:渲染引擎和js解釋器(js引擎),其對于頁面的決議程序大概如下:

- 瀏覽器會把HTML決議成DOM,把CSS決議成CSSOM,(決議代碼)DOM和CSSOM合并就產生了Render Tree(構造render樹),
- 有了RenderTree,我們就知道了所有節點的樣式,然后計算他們在頁面上的大小和位置(布局render樹),最后把節點繪制到頁面上(繪制render樹),
注意:
瀏覽器使用流式布局模型 (Flow Based Layout), 對Render Tree的計算通常只需要遍歷一次就可以完成,但table及其內部元素除外,他們可能需要多次計算,通常要花3倍于同等元素的時間,這也是為什么要避免使用table布局的原因之一,
(2)網頁加載流程
瀏覽器通過對script元素的加載實作了對JS腳本的加載,JS作為一種單執行緒弱型別腳本語言其正常的流程如下:
- 瀏覽器一邊下載 HTML 網頁,一邊開始決議,也就是說,不等到下載完,就開始決議,
- 決議程序中,瀏覽器發現script元素,就暫停決議,把網頁渲染的控制權轉交給 JavaScript 引擎,
- 如果script元素參考了外部腳本,就下載該腳本再執行,否則就直接執行代碼,
- JavaScript 引擎執行完畢,控制權交還渲染引擎,恢復往下決議 HTML 網頁,
這里需要注意,由于其單執行緒的特點,如果你在DOM結點生成之前呼叫 DOM結點,JS就會報錯,所以可以選擇腳本在尾部加載或者采用回呼函式:
a.onload屬性(window.onload);
b.DOMContentLoaded 事件;
上代碼:
<head>
<script>
document.addEventListener(
'DOMContentLoaded',
function (event) {
console.log(document.body.innerHTML);
}
);
</script>
</head>
兩者都可以保證在DOM運行結束時,事件才被觸發,當然了,如果把腳本放到頁面底部,以上方式就不需要了,而且把腳本檔案放在后面也可以防止瀏覽器因等待網頁加載而呈現:“假死”狀態(防止“阻塞效應”),
(3).注意
- 決議和執行 CSS,也會產生阻塞,Firefox 瀏覽器會等到腳本前面的所有樣式表,都下載并決議完,再執行腳本;Webkit則是一旦發現腳本參考了樣式,就會暫停執行腳本,等到樣式表下載并決議完,再恢復執行,
- 對于來自同一個域名的資源,比如腳本檔案、樣式表檔案、圖片檔案等,瀏覽器一般有限制,同時最多下載6~20個資源,即最多同時打開的 TCP 連接有限制,這是為了防止對服務器造成太大壓力,如果是來自不同域名的資源,就沒有這個限制,所以,通常把靜態檔案放在不同的域名之下,以加快下載速度,
為了解決腳本檔案下載阻塞網頁渲染問題,有兩個異步加載JS檔案的方法:defer屬性與async屬性,
2. defer屬性
defer屬性的作用在于延遲腳本的執行:
<script src="a.js" defer></script>
<script src="b.js" defer></script>
上述代碼就實作了DOM之后執行a.js與b.js當然順序還是先a再b,
含defer的流程如下:
- 瀏覽器開始決議 HTML 網頁,
- 決議程序中,發現帶有defer屬性的script元素,
- 瀏覽器繼續往下決議 HTML 網頁,同時并行下載script元素加載的外部腳本,
- 瀏覽器完成決議 HTML 網頁,此時再回過頭執行已經下載完成的腳本,
有了defer屬性,瀏覽器下載腳本檔案的時候,不會阻塞頁面渲染,下載的腳本檔案在DOMContentLoaded事件觸發前執行(即剛剛讀取完標簽),而且可以保證執行順序就是它們在頁面上出現的順序,
對于內置而不是加載外部腳本的script標簽,以及動態生成的script標簽,defer屬性不起作用,另外,使用defer加載的外部腳本不應該使用document.write方法,
3. async 屬性
async 屬性是使用另一個行程下載腳本,下載時不會阻塞渲染,
<script src="a.js" async></script>
<script src="b.js" async></script>
含async的流程如下:
- 瀏覽器開始決議 HTML 網頁,
- 決議程序中,發現帶有async屬性的script標簽,
- 瀏覽器繼續往下決議 HTML 網頁,同時并行下載script標簽中的外部腳本,
- 腳本下載完成,瀏覽器暫停決議 HTML 網頁,開始執行下載的腳本,
- 腳本執行完畢,瀏覽器恢復決議 HTML 網頁,
async屬性主要可以保證在下載的同時,瀏覽器繼續渲染,可是這樣會不能保證腳本的執行屬性,這樣就那個腳本先下載完就執行那個腳本,此外async屬性的腳本檔案里面的代碼,不應該使用document.write方法,
4. 對比及使用
1.對比
async是亂序執行js腳本(哪個先下載完成執行哪個);
defer是按順序執行js腳本,
2.使用
腳本之間沒有依賴關系——————》》使用async屬性;
腳本之間有依賴關系————————》》使用defer屬性,
如果同時使用async和defer屬性,后者不起作用,瀏覽器行為由async屬性決定,
二、重繪(repaint)
1.介紹
了解重繪之前,我先介紹一下繪制:指的是布局顯示到頁面這個程序,具有阻塞效應;
所以重繪指的是DOM屬性發生了變化,瀏覽器通知render重新描述相應元素的程序,這會導致更多的時間和資源,
三、回流 (Reflow)
1.介紹
同樣,我們先談布局流(flow):指的是渲染樹轉換為網頁布局;
所以回流指的是當Render Tree中部分或全部元素的尺寸、結構、或某些屬性發生改變時,瀏覽器重新渲染部分或全部檔案的程序,個人理解通俗來說就是:結構發生變化,
2.導致回流的方式:
- 頁面首次渲染
- 瀏覽器視窗大小發生改變
- 元素尺寸或位置發生改變
- 元素內容變化(文字數量或圖片大小等等)
- 元素字體大小變化
- 添加或者洗掉可見的DOM元素
- 激活CSS偽類(例如::hover)
- 查詢某些屬性或呼叫某些方法
3.一些常用且會導致回流的屬性和方法:
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- scrollIntoView()、scrollIntoViewIfNeeded()
- getComputedStyle()
- getBoundingClientRect()
- scrollTo()
四、比較及避免
1.比較
重流必然導致重繪,重繪不一定要重流,
比如改變元素顏色,只會導致重繪,
改變元素布局,導致重流和重繪,
并且回流的代價更高,因為主要涉及格式的改動,有時會導致其他元素也跟著一起回流,
2.避免
css:
- 避免使用table布局,
- 盡可能在DOM樹的最末端改變class,
- 避免設定多層行內樣式,
- 將影片效果應用到position屬性為absolute或fixed的元素上,
- 避免使用CSS運算式(例如:calc()),
JS:
- 避免頻繁操作樣式,最好一次性重寫style屬性,或者將樣式串列定義為class并一次性更改class屬性,
- 避免頻繁操作DOM,創建一個documentFragment,在它上面應用所有DOM操作,最后再把它添加到檔案中,(虛擬DOM庫)
- 也可以先為元素設定display: none,操作結束后再把它顯示出來,因為在display屬性為none的元素上進行的DOM操作不會引發回流和重繪,
- 避免頻繁讀取會引發回流/重繪的屬性,如果確實需要多次使用,就用一個變數快取起來(隱藏元素),
- 對具有復雜影片的元素使用絕對定位,使它脫離檔案流,否則會引起父元素及后續元素頻繁回流,
- 使用window.requestAnimationFrame(),因為它可以把代碼推遲到下一次重繪之前執行,而不是立即要求頁面重繪,
其實由于每進行一次DOM操作都會導致不停的重排及回流,所以盡量保證把DOM操作寫到一起進而減小DOM變動代價,
參考網站:https://juejin.im/post/6844903569087266823轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/203129.html
標籤:AI
