漫談網站優化提速
前幾天的一個晚上,在和一個偶然認識的小白,聊了半個晚上的網站加速的事情,總覺自己最後沒有講清楚,固有此文產生。
本篇文章只涉及前端優化,暫不涉及後端操作,預設後端能抗住所有訪問,算力無限大,響應時間無限小。因為加上後端的話,這個命題不是短短的幾篇文章搞的定的,大多數都要依據具體的業務來確定。
本文涉及到的瀏覽器為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,有效緩解所有流量全部回源對我們的主服務造成的流量衝擊,帶來更高的可用性,當然,這樣會造成靜態資源快取,如果我們需要更新靜態資源,面臨著快取的挑戰,不過,也不是沒有解決方案的,所有的靜態資源在訪問的時候都應該加上版本號,防止訪問到前面版本的資源,造成更新不及時。
參考: