我們將通過一個完整的例項, 一步步的優化載入, 渲染等各方面的體驗.
首先我們先看一下專案的檔案構成
這之中包含了一個基本網頁的元素, js(React App), css, 還有圖片.
我們先來看一下來serve整個網頁的部分.
server.js
這段程式碼只是簡單的將dist目錄下的檔案給轉發一下.
開啟網頁便可以看到相關載入情況.
我們可以看到, 整個app.js共277kb, 在模擬3G網路的情況下(藍色框框),每次載入需要花費999ms, 其中下載花費了911ms(紅色框框).
接下來我們將逐步優化, 然後每次將結果進行比較.
優化(一) — 強制快取
我們可以注意到優化一種, 一個304的請求仍然花掉了100多毫秒, 對於大型網站, 資源特別多的情況, 這仍然是一個不小的開支. 那我們可以把這個省掉嗎? 答案是可以的.
瀏覽器快取當中, 還有一個特別的欄位. Expires , 它可以指定檔案的過期時間, 直到那一刻位置, 瀏覽器都不會再重新發起請求, 而是直接從本地快取中讀取.
但是, 這仍舊需要每隔一段時間去請求. 我們該如何做呢? 答案就是, 設定超長的快取時間, 例如10年. 但是這樣我們便無法更新任何內容了. 我們該如何用到這樣的特性, 而又很方便的更新呢.
我們可以給檔名加上 hash特徵值 , 這樣只有當檔案內容有改動時, 纔會重新載入, 而且這樣適合於分散式CDN的, 非覆蓋式的釋出, 可以使其在引用頁面(首頁)已經改變的情況下(當前伺服器已經發布), 纔會用到新資源, 而訪問到未釋出的伺服器時, 還是會引用老的資源, 使得釋出再也不需要熬夜
具體細節改動見git branch step-3.
實現效果:
可以從藍色方框出看見, 快取已經生效, 而整體的讀取時間才只有20毫秒不到.
從原始的1000毫秒, 到現在的20毫秒, 簡簡單單的三個步驟便可以讓你的網頁載入提速50倍
優化(二)
網頁載入優化中最常見的就是304 Not Modified了, 具體機制是瀏覽器發起請求, headers中包含If-Modified-Since,(如無快取, 則無此頭欄位), 伺服器對比硬碟上(或記憶體中)檔案最後修改的時間, 如果小於或等於請求的時間, 則返回304. 否則, 則返回200, 並加上Last-Modified欄位, 告訴客戶端下次請求可以嘗試請求是否有快取.
具體程式碼如下:
(模擬實際情況中, 首頁會動態生成, 加入一些廣告,追蹤或個性化資料, index.html並未快取)
最終效果:
我們可以看見, 下載時間為2ms, 可以幾乎忽略掉(只有HTTP Headers), 總共的載入時間也只有了120ms, 相比之前, 整整少了 869ms.
但是, 我們滿足了嗎?
優化(三) — 分別打包
我們可以注意到, 我們打包出來最終只有一個js檔案, 當依賴變多後(此例中只有react和react-dom, 每次修改都導致整個js檔案被重新請求.所以我們想要把不同的library(甚至是專案內部公用的程式碼模組)提取出來.
我們首先要建立一個 webpack.vendors.config.js 來構建這些library, 或者vendor.
注意到
意味著我們可以將同一型別的包打包成一個js檔案.
當然, 我們也要對webpack.production.js做一些修改.
在這, 我們將自動掃描vendor目錄下面的檔案, 自動將所有的vendor載入進來.
這樣我們就實現了分包載入(還有一些細節的修改, 包括index.html, 請參見github上, step-2分支
效果還是不錯的, app.js單獨載入只需要400多ms, 比起所有依賴一起載入要快了至少一半以上.
對於一般型別網站, 優化到這已經可以取得非常不錯的效果了, 但是對於大型網站來說, 我們可以做的還有很多.
擴充套件閱讀
-
在實際生產中, 我們通常看到的是載入的CDN域名, 這是為何呢?
這是因為, 一個大型的網站, 請求當中會帶上很多Cookie, 有的甚至於接近1KB, 而100個圖片的載入, 就是整整100KB. 通過第三方域名(不同於當前域名), 我們可以節省掉許多不必要的請求頭, Cookie頭. 同樣達到提速的目的
2.還有一種情況是, 資源分佈在不同的伺服器上
這是因為瀏覽器對於同一域名下資源的並行下載數量有限制.
使用不同的資源伺服器可以避開這種限制, 加大下載併發數. 但是, 這樣同樣帶來的快取命中率的問題, 所以還需要儲存使用者快取相關的資料. 合理的利用下, 對於頁面整體的載入速度還是很有好處的.
3.其他的方法
在技術飛速發展的當下, 還有很多技術都是可以對終端使用者的體驗帶來提升的.
BigPipe + Server-Side Rendering, 加速首頁載入速度
Goole AMP
HTTP/2