OM 節點
根據 W3C 的 HTML DOM 標準,HTML 文檔中的所有內容都是節點:
整個文檔是一個文檔節點
每個 HTML 元素是元素節點
HTML 元素內的文本是文本節點
每個 HTML 屬性是屬性節點
注釋是注釋節點
HTML DOM 節點樹
HTML DOM 將 HTML 文檔視作樹結構。這種結構被稱為節點樹:
HTML DOM Tree 實例
通過 HTML DOM,樹中的所有節點均可通過 JavaScript 進行訪問。所有 HTML 元素(節點)均可被修改,也可以創建或刪除節點。
節點父、子和同胞
節點樹中的節點彼此擁有層級關系。
父(parent)、子(child)和同胞(sibling)等術語用于描述這些關系。父節點擁有子節點。同級的子節點被稱為同胞(兄弟或姐妹)。
在節點樹中,頂端節點被稱為根(root)
每個節點都有父節點、除了根(它沒有父節點)
一個節點可擁有任意數量的子
同胞是擁有相同父節點的節點
下面的圖片展示了節點樹的一部分,以及節點之間的關系:
請看下面的 HTML 片段:
<html>
<head>
<title>DOM 教程</title>
</head>
<body>
<h1>DOM 第一課</h1>
<p>Hello world!</p>
</body>
</html>
并且:
并且:
警告!
DOM 處理中的常見錯誤是希望元素節點包含文本。
在本例中:<title>DOM 教程</title>,元素節點 <title>,包含值為 "DOM 教程" 的文本節點。
可通過節點的 innerHTML 屬性來訪問文本節點的值。
來看html網頁的代碼和瀏覽器展現的結果(下圖1和圖2):
圖1
圖2
然后對照著下圖的DOM樹,分析DOM的節點層次和定義:
除了html中的<!DOCTYPE>和<meta>聲明外,
1.第一級是html文件中的根元素:<html></html>標簽
2.第二級是根元素html下面的子元素:<head></head>和<body></body>標簽
3.第三級是<head>元素的子元素:<title></title>標簽以及
<body>元素的子元素:<a></a>和<h1></h1>標簽
4.title元素中有一個文本“這是網頁標題”;
a元素中有一個href屬性和一個文本“這是鏈接”;
h1元素中有一個文本“這是網頁內容中的標題”。
綜上:
1.html文檔是一個文檔節點,
2.每個html元素是元素節點,
3.html元素內的文本是文本節點,
4.每個html屬性是屬性節點。
節點的層級關系用術語來描述:
父(parent)、子(child)和同胞(sibling)等。
在節點樹中,頂端節點被稱為根(root),在頁面中對應的是<html></html>標簽,
每個節點都有父節點、除了根(它沒有父節點),<head></head>和<body></body>的父節點就是<html></html>,
一個節點可擁有任意數量的子節點,<body></body>的子節點有<a></a>和<h1></h1>,
同胞是擁有相同父節點的節點,<a></a>和<h1></h1>有相同的父節點,因此它倆就是同胞。
覽器渲染頁面的原理及流程
瀏覽器將域名通過網絡通信從服務器拿到html文件后,如何渲染頁面呢?
1.根據html文件構建DOM樹和CSSOM樹。構建DOM樹期間,如果遇到JS,阻塞DOM樹及CSSOM樹的構建,優先加載JS文件,加載完畢,再繼續構建DOM樹及CSSOM樹。
2.構建渲染樹(Render Tree)。
3.頁面的重繪(repaint)與重排(reflow,也有稱回流)。頁面渲染完成后,若JS操作了DOM節點,根據JS對DOM操作動作的大小,瀏覽器對頁面進行重繪或是重排。
1.1構建DOM樹
HTML 文檔中的所有內容皆是節點,各節點之間擁有層級關系,如父子關系、兄弟關系等,彼此相連,構成DOM樹。最常見的幾種節點有:文檔節點、元素節點、文本節點、屬性節點、注釋節點。
DOM節點樹中節點與HTML文檔中內容一一對應,DOM樹構建過程:讀取html文檔,將字節轉換成字符,確定tokens(標簽),再將tokens轉換成節點,以節點構建 DOM 樹。如下圖所示:
1.2構建CSSOM樹
CSS文檔中,所有元素皆是節點,與HTML文件中的標簽節點一一對應。CSS中各節點之間同樣擁有層級關系,如父子關系、兄弟關系等,彼此相連,構成CSSOM樹。
在構建DOM樹的過程中,在 HTML 文檔的 head 標簽中遇到 link 標簽,該標簽引用了一個外部CSS樣式表。由于預見到需要利用該CSS資源來渲染頁面,瀏覽器會立即發出對該CSS資源的請求,并進行CSSDOM樹的構建。
CSSOM樹構建過程與DOM樹構建流程一致:讀取CSS文檔,將字節轉換成字符,確定tokens(標簽),再將tokens轉換成節點,以節點構建 CSSOM 樹。如下圖所示:
CSS文件,又名層疊樣式表。當CSSOM樹生成節點時,每一個節點首先會繼承其父節點的所有樣式,層疊覆蓋,然后再以"向下級聯"的規則,為該節點應用更具體的樣式,遞歸生成CSSOM樹。譬如,上右圖中第二層的p節點,有父節點body,因此該p將繼承body節點的樣式:"font-size: 16px;"。然后再應用該p節點自身的樣式:"font-weight: bold;"。所以最終該p節點的樣式為:"font-size: 16px;font-weight: bold;"。
1.3加載JS
若在構建DOM樹的過程中,當 HTML 解析器遇到一個 script 標記時,即遇到了js,將立即阻塞DOM樹的構建,將控制權移交給 JavaScript 引擎,等到 JavaScript 引擎運行完畢,瀏覽器才會從中斷的地方恢復DOM樹的構建。
其根本原因在于,JS會對DOM節點進行操作,瀏覽器無法預測未來的DOM節點的具體內容,為了防止無效操作,節省資源,只能阻塞DOM樹的構建。譬如,若不阻塞DOM樹的構建,若JS刪除了某個DOM節點A,那么瀏覽器為構建此節點A花費的資源就是無效的。
若在HTML頭部加載JS文件,由于JS阻塞,會推遲頁面的首繪。為了加快頁面渲染,一般將JS文件放到HTML底部進行加載,或是對JS文件執行async或defer加載。
渲染樹(Render Tree)由DOM樹、CSSOM樹合并而成,但并不是必須等DOM樹及CSSOM樹加載完成后才開始合并構建渲染樹。三者的構建并無先后條件,亦非完全獨立,而是會有交叉,并行構建。因此會形成一邊加載,一邊解析,一邊渲染的工作現象。
構建渲染樹,根據渲染樹計算每個可見元素的布局,并輸出到繪制流程,將像素渲染到屏幕上。
3.1重繪(repaint):屏幕的一部分要重繪。渲染樹節點發生改變,但不影響該節點在頁面當中的空間位置及大小。譬如某個div標簽節點的背景顏色、字體顏色等等發生改變,但是該div標簽節點的寬、高、內外邊距并不發生變化,此時觸發瀏覽器重繪(repaint)。
3.2重排(reflow):也有稱回流,當渲染樹節點發生改變,影響了節點的幾何屬性(如寬、高、內邊距、外邊距、或是float、position、display:none;等等),導致節點位置發生變化,此時觸發瀏覽器重排(reflow),需要重新生成渲染樹。譬如JS為某個p標簽節點添加新的樣式:"display:none;"。導致該p標簽被隱藏起來,該p標簽之后的所有節點位置都會發生改變。此時瀏覽器需要重新生成渲染樹,重新布局,即重排(reflow)。
注意:重排必將引起重繪,而重繪不一定會引起重排。
何時回引起重排?
當頁面布局和幾何屬性改變時就需要重排。下述情況會發生瀏覽器重排:
1、添加或者刪除可見的DOM元素;
2、元素位置改變——display、float、position、overflow等等;
3、元素尺寸改變——邊距、填充、邊框、寬度和高度
4、內容改變——比如文本改變或者圖片大小改變而引起的計算值寬度和高度改變;
5、頁面渲染初始化;
6、瀏覽器窗口尺寸改變——resize事件發生時;
3.3如何減少和避免重排
Reflow 的成本比 Repaint 的成本高得多的多。一個節點的 Reflow 很有可能導致子節點,甚至父節點以及兄弟節點的 Reflow 。在一些高性能的電腦上也許還沒什么,但是如果 Reflow 發生在手機上,那么這個過程是延慢加載和耗電的。----瀏覽器的渲染原理簡介
1. 直接改變className,如果動態改變樣式,則使用cssText(考慮沒有優化的瀏覽器);
2. 讓要操作的元素進行”離線處理”,處理完后一起更新;
a) 使用DocumentFragment進行緩存操作,引發一次回流和重繪;
b) 使用display:none技術,只引發兩次回流和重繪;
c) 使用cloneNode(true or false) 和 replaceChild 技術,引發一次回流和重繪;
3.不要經常訪問會引起瀏覽器flush隊列的屬性,如果你確實要訪問,利用緩存;
4. 讓元素脫離動畫流,減少回流的Render Tree的規模;
結合上文和我看到的一些文章,有以下幾點可以優化渲染效率
1.合法地去書寫 HTML 和 CSS ,且不要忘了文檔編碼類型。
2.樣式文件應當在 head 標簽中,而腳本文件在 body 結束前,這樣可以防止阻塞的方式。
3.簡化并優化CSS選擇器,盡量將嵌套層減少到最小。
4.盡量減少在 JavaScript 中進行DOM操作。
5.修改元素樣式時,更改其class屬性是性能最高的方法。
6.盡量用 transform 來做形變和位移
*請認真填寫需求信息,我們會在24小時內與您取得聯系。