對web項目進行優化首先得知道瀏覽器是怎么工作的這里推薦 how browsers work 中文版;
一、瀏覽器
瀏覽器的主要功能是將用戶選擇的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服務器;
這里涉及到客戶端與服務器的tcp 三次握手與四次揮手,可以參考上篇博文《TCP的三次握手(建立連接)與 四次揮手(關閉連接)》;
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); } }