web頁面載入、解析、渲染過程
對web專案進行優化首先得知道瀏覽器是怎麼工作的這裡推薦中文版;
一、瀏覽器
瀏覽器的主要功能是將使用者選擇的web資源呈現出來,它需要從伺服器請求資源,並將其顯示在瀏覽器視窗中,資源的格式通常是HTML,也包括PDF、image及其他格式。使用者用URI(Uniform Resource Identifier統一資源識別符號)來指定所請求資源的位置,通過DNS查詢,將網址轉換為IP地址。整個瀏覽器工作的流程:
1、輸入網址。
2、瀏覽器查詢域名的IP地址。
3. 瀏覽器給web伺服器傳送一個HTTP請求
4. 網站服務的永久重定向響應
5. 瀏覽器跟蹤重定向地址 現在,瀏覽器知道了要訪問的正確地址,所以它會發送另一個獲取請求。
6. 伺服器“處理”請求,伺服器接收到獲取請求,然後處理並返回一個響應。
7. 伺服器發回一個HTML響應
8. 瀏覽器開始顯示HTML
9. 瀏覽器傳送請求,以獲取嵌入在HTML中的物件。在瀏覽器顯示HTML時,它會注意到需要獲取其他地址內容的標籤。這時,瀏覽器會發送一個獲取請求來重新獲得這些檔案。這些檔案就包括CSS/JS/圖片等資源,這些資源的地址都要經歷一個和HTML讀取類似的過程。所以瀏覽器會在DNS中查詢這些域名,傳送請求,重定向等;
瀏覽器的主要元件包括
1. 使用者介面: 包括位址列、後退/前進按鈕、書籤目錄等,也就是你所看到的除了用來顯示你所請求頁面的主視窗之外的其他部分;
2. 瀏覽器引擎:用來查詢及操作渲染引擎的介面;
3. 渲染引擎: 用來顯示請求的內容,例如,如果請求內容為html,它負責解析html及css,並將解析後的結果顯示出來;
4. 網路:用來完成網路呼叫,例如http請求,它具有平臺無關的介面,可以在不同平臺上工作;
5. UI 後端:用來繪製類似組合選擇框及對話方塊等基本元件,具有不特定於某個平臺的通用介面,底層使用作業系統的使用者介面;
6. JS直譯器:用來解釋執行JS程式碼;
7. 資料儲存:H5定義了web database技術,這是一種輕量級完整的客戶端儲存技術;
二、頁面生成過程
1、DNS伺服器通過域名查詢對應的web 伺服器ip地址;
2、瀏覽器訪問web伺服器;
3、伺服器處理完成返回html;
4、瀏覽器解析、載入頁面
解析html 構建dom樹 -> 構建render樹 -> 佈局render樹 -> 繪製render樹 :
我們知道瀏覽器為了體驗友好,並不是文件全部都解析才繪製到螢幕上,而是從上至下開始解析html,遇到css 會開啟執行緒下載css;
解析:
1、將HTML構建成一個DOM樹(DOM = Document Object Model 文件物件模型),DOM 樹的構建過程是一個深度遍歷過程:當前節點的所有子節點都構建好後才會去構建當前節點的下一個兄弟節點。
2、將CSS解析成CSS去構造CSSOM樹( CSSOM = CSS Object Model CSS物件模型)
3、根據DOM樹和CSSOM來構造 Rendering Tree(渲染樹)。注意:Rendering Tree 渲染樹並不等同於 DOM 樹,因為一些像 Header 或 display:none 的東西就沒必要放在渲染樹中了。
4.有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關係。
5.下一步操作稱之為Layout,顧名思義就是計算出每個節點在螢幕中的位置 layout render tree。
6.再下一步就是繪製,即遍歷render樹,並使用瀏覽器UI後端層繪製每個節點。
效能優化中重繪、重排:
(1)Reflow(迴流/重排):當它發現了某個部分發生了變化影響了佈局,渲染樹需要重新計算。
(2)Repaint(重繪):改變了某個元素的背景顏色,文字顏色等,不影響元素周圍或內部佈局的屬性,將只會引起瀏覽器的repaint,根據元素的新屬性重新繪製,使元素呈現新的外觀。重繪不會帶來重新佈局,並不一定伴隨重排;
Reflow要比Repaint更花費時間,也就更影響效能。所以在寫程式碼的時候,要儘量避免過多的Reflow。
reflow的原因:
(1)頁面初始化的時候;
(2)操作DOM時;
(3)某些元素的尺寸變了;
(4)如果 CSS 的屬性發生變化了。
減少 reflow/repaint
(1)不要一條一條地修改 DOM 的樣式。與其這樣,還不如預先定義好 css 的 class,然後修改 DOM 的 className。
(2)不要把 DOM 結點的屬性值放在一個迴圈裡當成迴圈裡的變數。
(3)為動畫的 HTML 元件使用 fixed 或 absoult 的 position,那麼修改他們的 CSS 是不會 reflow 的。
(4)千萬不要使用 table 佈局。因為可能很小的一個小改動會造成整個 table 的重新佈局。
三、影響頁面渲染
css注意事項
css選擇符是從右到左進行匹配的。所以,#nav li 我們以為這是一條很簡單的規則,秒秒鐘就能匹配到想要的元素,所以,會去找所有的li,然後再去確定它的父元素是不是#nav。因此,寫css的時候需要注意:
- dom深度儘量淺。
- 減少inline javascript、css的數量。
- 使用現代合法的css屬性。
- 不要為id選擇器指定類名或是標籤,因為id可以唯一確定一個元素。
- 避免後代選擇符,儘量使用子選擇符。原因:子元素匹配符的概率要大於後代元素匹配符。後代選擇符;#tp p{} 子選擇符:#tp>p{}
- 避免使用萬用字元,舉一個例子,.mod .hd *{font-size:14px;} 根據匹配順序,將首先匹配萬用字元,也就是說先匹配出萬用字元,然後匹配.hd(就是要對dom樹上的所有節點進行遍歷他的父級元素),然後匹配.mod,這樣的效能耗費可想而知.
javascript 位置
如果在解析html的時候遇到js會阻塞頁面渲染,所以一般我們會將所有的script標籤放到頁面底部,也就是body閉合標籤之前,這能確保在指令碼執行前頁面已經完成了DOM樹渲
染。儘可能地合併指令碼。頁面中的script標籤越少,載入也就越快,響應也越迅速。無論是外鏈指令碼還是內嵌指令碼都是如此。
採用無阻塞下載 JavaScript 指令碼的方法:
(1)使用script標籤的 defer、async 屬性、;
(2)使用動態建立的script元素來下載並執行程式碼等非同步載入等方法;
defer、async 區別:
defer、async都是非同步下載,但是執行時刻不一致;
相同點:
- 載入檔案時不阻塞頁面渲染;
- 使用這兩個屬性的指令碼中不能呼叫document.write方法;
- 允許不定義屬性值,僅僅使用屬性名;
不同點:
- html的版本html4.0中定義了defer,html5.0中定義了async;這將造成由於瀏覽器版本的不同而對其支援的程度不同;
- 每一個async屬性的指令碼都在它下載結束之後立刻執行,同時會在window的load事件之前執行,所以就有可能出現指令碼執行順序被打亂 的情況;
- 每一個defer屬性的指令碼都是在頁面解析完畢之後,按照原本的順序執行,同時會在document的DOMContentLoaded之前執行;
動態建立DOM方式
function downloadJSAtOnload() {
var urlList = [
"@ViewHelper.Content("/Content/plugin/alert/js/j_alert.js", "20170111")",
"@ViewHelper.Content("/Content/js/swiper.min.js")",
"@ViewHelper.Content("/Content/js/imageview_new.js", "201702271")"
];
for (var i = 0; i <= urlList.length - 1; i++) {
var element = document.createElement("script");
element.src = urlList[i];
document.body.appendChild(element);
}
}