漫談網站優化提速
前幾天的一個晚上,在和一個偶然認識的小白,聊了半個晚上的網站加速的事情,總覺自己最后沒有講清楚,固有此文產生。
本篇文章只涉及前端優化,暫不涉及后端操作,默認后端能抗住所有訪問,算力無限大,響應時間無限小。因為加上后端的話,這個命題不是短短的幾篇文章搞的定的,大多數都要依據具體的業務來確定。
本文涉及到的瀏覽器為Chrome瀏覽器,不具有統一性,僅供參考使用。
用戶和網站的交互是通過瀏覽器來完成的,要談前端優化,那么,我們就要搞清楚,從用戶輸入了一串url以后,瀏覽器到底做了什么。
1. 瀏覽器如何打開一個網頁
這里我們先不考慮路由尋址的事情,后面我們再細細道來,在Chrome瀏覽器中先打開F12,打開network,可以看到一個網站從輸入url到頁面顯示,具體發送了多少請求。我們以百度為示例,看一下:
首先第一行,可以看到瀏覽器請求了百度這個頁面的主題文件HTML,當瀏覽器收到這個HTML之后,瀏覽器和這個頁面的緣分,就此開始。
1.1 瀏覽器渲染流程
用戶請求的HTML文本(text/html)通過瀏覽器的網絡層到達渲染引擎后,渲染工作開始。每次通常渲染不會超過8K的數據塊,其中基礎的渲染流程圖:
webkit引擎渲染的詳細流程,其他引擎渲染流程稍有不同:
渲染流程有四個主要步驟:
-
解析HTML生成DOM樹 - 渲染引擎首先解析HTML文檔,生成DOM樹
-
構建Render樹 - 接下來不管是內聯式,外聯式還是嵌入式引入的CSS樣式會被解析生成CSSOM樹,根據DOM樹與CSSOM樹生成另外一棵用於渲染的樹-渲染樹(Render tree),
-
布局Render樹 - 然后對渲染樹的每個節點進行布局處理,確定其在屏幕上的顯示位置
-
繪制Render樹 - 最后遍歷渲染樹並用UI后端層將每一個節點繪制出來
以上步驟是一個漸進的過程,為了提高用戶體驗,渲染引擎試圖盡可能快的把結果顯示給最終用戶。它不會等到所有HTML都被解析完才創建並布局渲染樹。它會在從網絡層獲取文檔內容的同時把已經接收到的局部內容先展示出來。
1.2 渲染細節
瀏覽器渲染一個頁面的過程叫做“關鍵渲染路徑(Critical Rendering Path 簡稱CRP)”,下面我們來聊一下什么是CRP,這對我們進行代碼級的優化有很大的指導意義。
- CRP的相關知識對於如何提升網站性能是相當有用的,共有6個步驟:
- 構建DOM樹
- 構建CSSOM樹
- 運行JavaScript
- 創建渲染樹
- 生成布局
- 繪制頁面
如下圖:
下面詳細聊一下什么是CRP,不感興趣的童鞋可以直接跳過這一段看結論。
1.1.1 構建DOM樹
DOM(Document Object Model)樹是一個表示整個解析過的HTML頁面的對象,從根節點<html>開始,會創建頁面中的每個元素/文本節點。
舉個栗子:
<html>
<head>
<title>Understanding the Critical Rendering Path</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Understanding the Critical Rendering Path</h1>
</header>
<main>
<h2>Introduction</h2>
<p>Lorem ipsum dolor sit amet</p>
</main>
<footer>
<small>Copyright 2017</small>
</footer>
</body>
</html>
上面的 HTML 將會被解析成下面的DOM樹:
HTML的優點在於它不必等待整個頁面加載完成才呈現頁面,可以解析一部分,顯示一部分。
- 但是像CSS、JavaScript等其他資源會阻止頁面渲染。
1.1.2 構建CSSOM樹
CSSOM(CSS Object Model) 是一個跟DOM相關的樣式對象。它跟DOM的表示方法是相似的,但是不論顯式聲明還是隱式繼承,每個節點都存在關聯樣式。
在上面提到的html頁面的style.css中的樣式如下
body { font-size: 18px; }
header { color: plum; }
h1 { font-size: 28px; }
main { color: firebrick; }
h2 { font-size: 20px; }
footer { display: none; }
它會被構建成下面的CSSOM樹
CSS 被認為是 “渲染阻塞資源”,它意味着如果不首先完全解析資源,渲染樹是無法構建的。CSS由於它的層疊繼承的性質,不能像HTML一樣解析一部分,顯示一部分。定義在文檔后面的樣式會覆蓋或改寫之前定義的樣式,因為在整個樣式表都被解析之前,如果我們使用了在樣式表中較早定義的樣式,那錯誤的樣式將被應用。這意味着CSS必須被全部解析之后,才能開始下一步。
1.1.3 運行JavaScript
JavaScript被認為是解析阻塞資源,這意味着HTML的解析會被JavaScript阻塞。
當解析器解析到 <script> 標簽時,無論該資源是內部還是外鏈的都會停止解析,先去下載資源。這也是為什么,當頁面內有引用JavaScript文件時,引用標簽要放到可視元素之后了。
為避免JavaScript解析阻塞,它可以通過設定 async 屬性來要求其異步加載。
<script async src="script.js">
1.1.4 創建渲染樹
渲染樹是DOM和CSSOM的結合體,它代表最終會渲染在頁面上的元素的結構對象。這意味着它只關注可見內容,對於被隱藏或者CSS屬性 display:none 的屬性,不會被包含在結構內。
使用上面例子的DOM和CSSOM,渲染樹被創建如下:
1.1.5 生成布局
布局決定了瀏覽器視窗的大小,它提供了上下文依賴的CSS樣式,如百分比或窗口的單位。視窗尺寸通常通過 <head> 標簽中的 <meta> 中的 viewport 設定來決定。如果不存在該標簽,則通常默認為 980px
例如,最常用的 meta veiwport 的值將會被設置為和設備寬度相符:
<meta name="viewport" content="width=device-width,initial-scale=1">
如果用戶訪問網頁的設備寬度為1000px。然后整體視窗尺寸就會基於這個寬度值了,比如 50% 就是500px, 10vw 就是100px 等等。
1.1.6 繪制頁面
最后,在繪制頁面步驟。頁面上的所有可見內容都會被轉換為像素並呈現在屏幕上。
具體的繪制時間跟DOM數以及應用的樣式有關。有些樣式會花費更多的執行時間,比如復雜的漸變背景圖片所需要的計算時間遠超過簡單固定背景色。
1.2 總結
縱觀整個CRP流程,CSS,JS等靜態資源加載是阻塞式的,而我們的目標是讓整個頁面最快呈現在用戶面前,並可以使用,而一個頁面的呈現和HTML、CSS息息相關,那么這兩個一定是要最先被加載的:
- 關鍵的css需要放在html的頭部位置,保證被最先加載,加載完成后可以開始渲染頁面。
JS只是關系了網站的一些交互操作,包括一些動態效果,那么:
- js應該放在html的最下部,和渲染視圖脫離,保證視圖渲染完成后再加載js,同時,js應盡量保持異步加載,不要阻塞主線程。
頁面除了HTML,JS,CSS還有一個使用最多的那就是圖片
- 圖片要做壓縮,很多時候高清大圖並不是一個好的選擇
- 圖片盡量使用jpg格式(體積小)
- 圖片除了首屏以外,最好使用懶加載的方式,減少初次頁面渲染所需要的時間
- 小的icon整合成一張大圖,減少圖片加載請求數(使用時可以使用css對圖片顯示做定位)
同時,還可以從網絡層考慮優化方向:
- 重新review整個頁面,去除所有不必要的資源加載
- css樣式表可以合並成為一個文件,減少瀏覽器請求數
- css樣式表需要做壓縮,減少網絡傳輸時間(具體有工具,大家可以自己去網上找)
還有很多我一下想不到的地方,不過總體的思想就幾句話:
- 盡一切可能減少網絡請求數
- 盡一切可能減少網絡傳輸數據
- 如果以上兩點都做到了,那么除了關鍵html和css以外,其他資源的加載請盡量走異步的形式。
2. 從url到瀏覽器之間到底都經歷了什么?
上面我們聊了瀏覽器接受到資源請求后的一些操作處理,那么從url輸入瀏覽器到瀏覽器接收到資源中間到底都經歷了什么,這一塊我們能做什么優化呢?
首先,我們來看一下從url到瀏覽器之間到底都經歷了什么?
- 首先,在瀏覽器地址欄中輸入url
- 瀏覽器先查看瀏覽器緩存-系統緩存-路由器緩存,如果緩存中有,會直接在屏幕中顯示頁面內容。若沒有,則跳到第三步操作。
- 在發送http請求前,需要域名解析(DNS解析),解析獲取相應的IP地址。
- 瀏覽器向服務器發起tcp連接,與瀏覽器建立tcp三次握手。
- 握手成功后,瀏覽器向服務器發送http請求,請求數據包。
- 服務器處理收到的請求,將數據返回至瀏覽器
- 瀏覽器收到HTTP響應
- 讀取頁面內容,瀏覽器渲染,解析html源碼
- 生成Dom樹、解析css樣式、js交互
- 客戶端和服務器交互
首先,我們能做優化的地方只有可憐的2,包括4、5、6、7、8都是標准的處理流程,我們也無法干涉。
2.1 緩存查找
- 瀏覽器緩存:瀏覽器會記錄DNS一段時間,因此,只是第一個地方解析DNS請求;
- 操作系統緩存:如果在瀏覽器緩存中不包含這個記錄,則會使系統調用操作系統,獲取操作系統的記錄(保存最近的DNS查詢緩存);
- 路由器緩存:如果上述兩個步驟均不能成功獲取DNS記錄,繼續搜索路由器緩存;
- ISP緩存:若上述均失敗,繼續向ISP搜索。
小結:
- 在很少有改動的頁面上開啟瀏覽器緩存,在打開頁面時盡可能的利用瀏覽器緩存
3. 網站訪問慢還有什么可能?
網站打開速度慢受很多因素的影響,簡單歸納下常見的幾個原因:
- 主機服務器不堪重負,響應速度慢;
- 靜態資源占用了主機服務大量的帶寬,達到了上限;
- 網站的圖片和內容太大,需要花費很多時間下載;
- 網站使用了太多不同的腳本和圖片,這些腳本和圖片沒有針對快速加載網站進行優化,加載時間長;
- 網站的服務器位置與終端用戶位於不同的地理位置。
其實還有許多其他的原因,但這些以上列舉的幾點是比較常見的。
3.1 終極大殺器——CDN
3.1.1 什么是CDN?
CDN指的是內容分發網絡。其基本思路是盡可能的避開互聯網上有可能影響數據傳輸速度和穩定性的瓶頸和環節,使內容傳輸的更快、更穩定。
通過在網絡各處放置節點服務器所構成的在現有的互聯網基礎之上的一層智能虛擬網絡,CDN系統能夠實時地根據網絡流量和各節點的連接、負載狀況以及到用戶的距離和響應時間等綜合信息將用戶的請求重新導向離用戶最近的服務節點上。其目的是使用戶可就近取得所需內容,解決Internet網絡擁擠的狀況,提高用戶訪問網站的響應速度。
簡單打個比方,我們常喜歡在京東上買東西,今天下單明天就能送到。而在淘寶上,我們享受不到這樣的速度。為什么呢?因為京東的物流體系完善。假設你在上海購買了海南的一件商品,淘寶走快遞可能要走3天才到你手上,但是京東在全國設有倉庫物流點,從就近的杭州發貨點發貨到上海,一天就可以到。
而CDN一般都由運營商或者大型雲服務提供商來提供,可以給更大的網絡帶寬,可以部署的離終端用戶位置更近,有智能DNS,有效緩解所有流量全部回源對我們的主服務造成的流量沖擊,帶來更高的可用性,當然,這樣會造成靜態資源緩存,如果我們需要更新靜態資源,面臨着緩存的挑戰,不過,也不是沒有解決方案的,所有的靜態資源在訪問的時候都應該加上版本號,防止訪問到前面版本的資源,造成更新不及時。
參考: