【譯註】這部分再也不詳細翻譯,只列出核心意思。javascript
驅動 Chrome 繼續前進的核心原則包括:php
本文關將注於第一點,速度。html
一個現代瀏覽器就是一個和操做系統同樣的平臺。
在 Chrome 以前的瀏覽器都是單進程的應用,全部頁面共享相同的地址空間和資源。
引入多進程架構這是 Chrome 最爲著名的改進【譯註:省略一些反覆談論的細節】。html5
一個進程內,Web 應用主要須要執行三個任務:獲取資源,頁面 排版及渲染,和運行 JavaScript。
渲染和腳本都是在運行中交替以單線程的方式運行的,其緣由是爲了保持 DOM 的一致性,而 JavaScript 本 身也是一個單線程的語言。
因此優化渲染和腳本運行不管對於頁面開發者仍是瀏覽器開發者都是極爲重要的。java
Chrome 的渲染引擎是 WebKit, JavaScript Engine 則使用深刻優論的 V8 (「V8″ JavaScript runtime)。
可是,若是網絡不順暢,不管優化 V8 的 JavaScript 執行,仍是優化 WebKit 的解析和渲染,做用其實頗有限。
巧婦難爲無米之炊,數據沒來就得等着!android
相對於用戶體驗,做用最爲明顯的就是如何優化網絡資源的加載順序、優先級及每個資源的延遲時間(latency)。
也許你察覺不到,Chrome 網絡模塊天天都在進步,逐步下降每一個資源的加載成本:向 DNS lookups 學習,記住頁面拓撲結構(topology of the web), 預先鏈接可能的目標網址,等等,還有不少。
從外面來看就是一個簡單的資源加載的機制,但在內部倒是一個精彩的世界。web
開始正題前,仍是先來了解一下如今網頁或者 Web 應用在網絡上的需求。算法
HTTP Archive 項目一直在追蹤網頁構建。
除了頁面內容外,它還會分析流行頁面使用的資源數量,類型,頭信息以及不一樣目標地址的元數據(metadata)。
下面是 2013 年 1 月的統計資料,由 300,000 目標頁面得出的平均數據:chrome
這些數字在過去幾年中一直持續增加(steadily increasing),沒有停下的跡象。
這說明咱們正不斷地建構一個更加龐大的、野心勃勃的網絡應用。
還要注意,平均來看每一個資源不過 12KB, 代表絕大多數的網絡傳輸都是短促(short and bursty)的。
這和 TCP 針對大數據、流式(streaming)下載的方向不一致,正由於如此,而引入了一些併發症。數據庫
下面就用一個例子來抽絲剝繭,一窺究竟……
W3C 的Navigation Timing specification定義了一組 API,能夠觀察到瀏覽器的每個請求(request)的時序和性能數據。
下面瞭解一些細節:
給定一個網頁資源地址後,瀏覽器就會檢查本地緩存和應用緩存。
若是以前獲取過而且有相應的緩存信息(appropriate cache headers)(如 Expires, Cache-Control, etc.), 就會用緩存數據填充這個請求,畢竟最快的請求就是沒有請求(the fastest request is a request not made)。
不然,咱們從新驗證資源,若是已經失效(expired),或者根本就沒見過,一個耗費網絡的請求就沒法避免地發送了。
給定了一個主機名和資源路徑後,Chrome 先是檢查現有已創建的鏈接(existing open connections)是否能夠複用, 即 sockets 指定了以(scheme、host 和 port)定義的鏈接池(pool)。
但若是配置了一個代理,或者指定了proxy auto-config(PAC)腳本,Chrome 就會檢查與 proxy 的鏈接。
PAC 腳本基於 URL 提供不一樣的代理,或者爲此指定了特定的規則。
與每個代理間均可以有本身的 socket pool。
最後,上述狀況都不存在,這個請求就會從 DNS 查詢(DNS lookup)開始了,以便得到它的 IP 地址。
幸運的話,這個主機名已經被緩存過。
不然,必須先發起一個 DNS Query。
這個過程所需的時間和 ISP,頁面的知名度,主機名在中間緩存(intermediate caches)的可能性,以及 authoritative servers 的響應時間這些因素有關。
也就是說這裏變量不少,不過通常還不致於到幾百毫秒那麼誇張。
拿到解析出的 IP 後,Chrome 就會在目標地址間打開一個新 TCP 鏈接,咱們就要執行一個 3 度握手(「three-way handshake」): SYN > SYN-ACK > ACK。
這個操做每一個新的 TCP 鏈接都必須完成,沒有捷徑。
根據遠近,路由路徑的選擇,這個過程可能要耗時幾百毫秒,甚至幾秒。
而到如今,咱們連一個有效的字節都還沒收到。
當 TCP 握手完成了,若是咱們鏈接的是一個 HTTPS 地址,還有一個 SSL 握手過程,同時又要增長最多兩輪的延遲等待。
若是 SSL 會話被緩存了,就只需一次。
最後,Chrome 終於要發送 HTTP 請求了 (如上面圖示中的 requestStart)。
服務器收到請求後,就會傳送響應數據(response data)回到客戶端。
這裏包含最少的往返延遲和服務的處理時間。
而後一個請求就完成了。
可是,若是是一個 HTTP 重定向(redirect)的話?
咱們又要從頭開始這個過程。
若是你的頁面裏有些冗餘的重定向,最好三思一下!
你得出全部的延遲時間了嗎?
咱們假設一個典型的寬帶環境:沒有本地緩存,相對較快的 DNS lookup(50ms), TCP 握手,SSL 協商,以及一個較快服務器響應時間(100ms)和一次延遲(80ms,在美國國內的平均值):
一個請求花了 470 毫秒, 其中 80% 的時間被網絡延遲佔去了。
看到了吧,咱們真得有不少事情要作!
事實上,470 毫秒已經很樂觀了:
前面能夠看到服務器響應時間僅是總延遲時間的 20%,其它都被 DNS,握手等操做佔用了。
過去用戶體驗研究(user experience research)代表用戶對延遲時間的不一樣反應:
上表一樣適用於頁面的性能表現: 渲染頁面,至少要在 250ms 內給個迴應來吸引住用戶。
這就是簡單地針對速度。
從 Google, Amazon, Microsoft 以及其它數千個站點來看,額外的延遲直接影響頁面表現:流暢的頁面會吸引更多的瀏覽、以及更強的用戶吸引力(engagement) 和頁面轉換率(conversion rates).
如今咱們知道了理想的延遲時間是 250ms,而前面的示例告訴咱們,DNS Lookup, TCP 和 SSL 握手,以及 request 的準備時間花去了 370ms, 即使不考慮服務器處理時間,咱們也超出了 50%。
對於絕大多數的用戶和網頁開發者來講,DNS,TCP,以及 SSL 延遲都是透明,不多有人會想到它。
這也就是爲何 Chrome 的網絡模塊那麼的複雜。
咱們已經識別出了問題,下面讓咱們深刻一下實現的細節…
Chrome 的多進程架構爲瀏覽器的網絡請求處理帶來了重要意義,它目前支持四種不一樣的執行模式(four different execution models)。
默認狀況下,桌面的 Chrome 瀏覽器使用 process-per-site 模式, 將不一樣的網站頁面隔離起來, 相同網站的頁面組織在一塊兒。
舉個簡單的例子: 每一個 tab 獨立一個進程。
從網絡性能的角度上說,並沒什麼本質上的不一樣,只是 process-per-tab 模式更易於理解。
每個 tab 有一個渲染進程(render process),其中包括了用於解析頁面(interpreting)和排版(layout out)的 WebKit 的排版引擎(layout engine), 即上圖中的 HTML Render。
還有 V8 引擎和二者之間的 DOM Bindings,若是你對這部分很好奇,能夠看這裏(great introduction to the plumbing)。
每個這樣的渲染進程被運行在一個沙箱環境中,只會對用戶的電 腦環境作極有限的訪問–包括網絡。
而使用這些資源,每個渲染進程必須和瀏覽內核進程(browser[kernel] process)溝通,以管理每一個渲染進程的安全性和訪問策略(access policies)。
渲染進程和內核進程之間的通信是經過 IPC 完成的。
在 Linux 和 Mac OS 上,使用了一個提供異步命名管道通信方式的 socketpair()。
每個渲染進程的消息會被序列化地到一個專用的 I/O 線程中,而後再由它發到內核進程。
在接收端,內核進程提供一個過濾接口(filter interface)用於解析資源相關的 IPC 請求(ResourceMessageFilter), 這部分就是網絡模塊負責的。
這樣作其中一個好處是全部的資源請求都由 I/O 進程處理,不管是 UI 產生的活動,或者網絡事件觸發的交互。
在內核進程(browser/kernel process)的 I/O 線程解析資源請求消息,將轉發到一個 ResourceDispatcherHost 的單例(singleton)對象中處理。
這個單例接口容許瀏覽器控制每一個渲染進程對網絡的訪問,也能達到有效和一致的資源共享:
單就一個渲染進程而言, 透過 IPC 發送資源請求很容易,只要告訴瀏覽器內核進程一個惟一 ID, 後面就交給內核進程處理了。
跨平臺也是 Chrome 網絡模塊的一個主要考量,包括 Linux, Windows, OS X, Chrome OS, Android 和 iOS。 爲此,網絡模塊儘可能實現成了單進程模式(只分出了獨立的cache和proxy進程)的跨平臺函數庫, 這樣就能夠在平臺間共用基礎組件(infrastructure)並分享相同的性能優化,更有機會作到同時爲全部平臺進行優化。
相關的代碼能夠在這裏找到the 「src/net」 subdirectory)。本文不會詳細展開每一個組件,不過了解一下代碼結構能夠幫助咱們理解它的能力結構。 好比:
上面每一項都值得好好讀讀,代碼組織的很好,你還會發現大量的單元測試。
移動瀏覽器正在大發展,Chrome 團隊也視優化移動端的體驗爲最高優先級。
先要說明的是移動版的 Chrome 的並非其桌面版本的直接移植,由於那樣根本不會帶來好的用戶體驗。
移動端的先天特性就決定了它是一個資源嚴重受限的環境,在運行參數有一些基本的不一樣:
再者,不但沒有典型的樣板移動設備,反而是有一大批各色硬件的設備。
Chrome 要作的,只能是設法兼容這些設備。
好在 Chrome 有不一樣的運行模式(execution models),面對這些問題,遊刃有餘!
在 Android 版本上,Chrome 一樣運用了桌面版本的多進程架構。
一個瀏覽器內核進程,以及一個或多個渲染進程。
但由於內存的限制,移動版的 Chrome 沒法爲每個 tabl 運行一個特定的渲染進程,而是根據內存狀況等條件決定一個最佳的渲染進程個數,而後就會在多個 tab 間共享這些渲染進程。
若是內存實在不足,或其它緣由致使 Chrome 沒法運行多進程,它就會切到單進程、多線程的模式。
好比在 iOS 設備上,由於其沙箱機制的限制,Chrome 只能運行在這種模式下。
關於網絡性能,首先 Chrome 在 Android 和 iOS 使用的是 各其它平臺相同的網絡模塊。
這能夠作到跨平臺的網絡優化,這也是 Chrome 明顯領先的優點之一。
所不一樣的是須要常常根據網絡狀況和設備能力進行些調整, 包括推測優化(speculative optimization)的優先級、socket 的超時設置和管理邏輯、緩存大小等。
好比,爲了延長電池壽命,移動端的 Chrome 會傾向於延遲關閉空閒的 sockets (lazy closing of idle sockets), 一般是爲了減小信號(radio)的使用而在打開新的 socket 時關閉舊的。
另外由於預渲染(pre-rendering,稍後會介紹)會使用必定的網絡和處理資源,它一般只在 WiFi 纔會使用。
關於移動瀏覽體驗會獨立一章,也許就在 POSA 系列的下一期。
Chrome會隨着使用變得更快。
它這個特性是經過一個單例對象 Predictor 來實現的。
這個對象在瀏覽器內核進程(Browser Kernel Process)中實例化,它惟一的職責就是觀察和學習當前網絡活動方式,提早預估用戶下一步的操做。
下面是一個示例:
除了上面三項,還有不少..
Chrome 會在你使用過程當中學習 Web 的拓撲結構,而不僅僅是你的瀏覽模式。
理想的話,它將爲你省去數百毫秒的延遲,更接近於即時頁面加載的狀態。
正是爲了這個目標,Chrome 投入瞭如下的核心優化技術:
每個決策都包含着一個或多個的優化,用來克服大量的限制因素。
不過畢竟都只是預測性的優化策略,若是效果不理想,就會引入多餘的處理和網絡傳輸。
甚至可能會帶來一些加載時間上的負體驗。
Chrome 如何處理這些問題呢?
Predictor 會盡可能收集各類信息,諸如用戶操做,歷史瀏覽數據,以及來自渲染引擎(render)和網絡模塊自身的信息。
和 Chrome 中負責網絡事務調度的 ResourceDispatcherHost 不一樣,Predictor 對象會針對用戶和網絡事務建立一組過濾器(filter):
渲染進程(render process)會在一系列的事件下發送消息到瀏覽器進程(browser process), 這些事件被定義在一個枚舉(ResolutionMotivation)中以便於使用 (url_info.h):
enum ResolutionMotivation { MOUSE_OVER_MOTIVATED, // 鼠標懸停. OMNIBOX_MOTIVATED, // Omni-box建議進行解析. STARTUP_LIST_MOTIVATED, // 這是在前10個啓動項中的資源. EARLY_LOAD_MOTIVATED, // 有時須要使用prefetched來提早創建鏈接. // 下面定義了預加載評估的方式,會由一個navigation變量指定. // referring_url_也須要同時指定. STATIC_REFERAL_MOTIVATED, // 外部數據庫(External Database)建議進行解析。 LEARNED_REFERAL_MOTIVATED, // 前一次瀏覽(prior navigation建議進行解析. SELF_REFERAL_MOTIVATED, // 猜想下一個鏈接是否是須要進行解析. // <略> ... };
經過這些給定的事件,Predictor 的目標就能夠評估它成功的可能性, 而後再適時觸發操做。
每一項事件都有其成功的機率、優先級以及時間戳,這些能夠在內部維護一個用優先級管理的隊列,也是優化的一個手段。
最終,對於這個隊 列中發出的每個請求的成功率,均可以被 Predictor 追蹤到。
基於這些數據,Predictor 就能夠進一步優化它的決策。
瞭解晦澀的內部細節後,讓咱們來看一下用戶能夠感覺到的優化。
一切從全新的 Chrome 開始。
第一次啓動瀏覽器,它固然不可能瞭解你的使用習慣和喜歡的頁面。
但事實上,咱們大多數會在瀏覽器的冷啓動後作些相似的事情,好比到電子郵箱查看郵件,加一些新聞頁面、社交頁面及內部 頁面到個人最愛,諸如此類。
這些頁面各有不一樣,但它們仍然具備一些類似性,因此 Predictor 仍然能夠對這個過程提速。
Chrome 記下了用戶在全新啓動瀏覽器時最經常使用的 10 個域名。
當瀏覽器啓動時,Chrome 會提早對這些域名進行 DNS 預解析。
你能夠在 Chrome 中使用 chrome://dns
查看到這個列表。
在打開頁面的最上面的表格中會列出啓動時的備選域名列表。
引入 Omnibox 是 Chrome 的一項創新, 並非簡單地處理目標的 URL。
除了記錄以前訪問過的頁面 URL,它還與搜索引擎的整合,而且支持在歷史記錄中進行全文搜索(好比,直接輸入頁面名稱)。
當用戶輸入時,Omnibox 自動發起一個行爲,要麼查找瀏覽記錄中的 URL, 要麼進行一次搜索。
每一次發起的操做都會被加以評分,以統計它的性能。
你能夠在 Chrome 輸入 chrome://predictors
來觀察這些數據。
Chrome 維護着一個歷史記錄,內容包括用戶輸入的前置文字,採用的行爲,以命中的資數。在上面的列表,你能夠看到,當輸入 g
時,有 76% 的機會嘗試打開 Gmail. 若是再補充一個 m
(就是 gm
), 打開 Gmail 的可能性增長到 99.8%。
那麼網絡模塊會作什麼呢?
上表中的黃色和綠色對於 ResourceDispatcher 很是重要。
若是有一個通常可能性的頁面(黃色), Chrome 就是發起 DNS 預解析。
若是有一個高可能性的頁面(綠色),Chrome 還會在 DNS 解析後發起 TCP 預鏈接。
若是這兩項都完成了,用戶仍然繼續錄入,Chrome 就會在一個隱藏的頁籤進行預渲染(pre-render)。
相對的,若是輸入的前置文字找不到合適的匹配項目,Chrome 會向搜索引擎服務者發起 DNS 預解析和 TCP 預連,以獲取類似的搜索結果。
平均而言用戶從填寫查詢內容到評估給出的建議須要花費數百毫秒。此時 Chrome 能夠在後臺進行預解析,預鏈接,甚至進行預渲染。
再當用戶準備按下回車鍵時,大量的網絡延遲已經被提早處理掉了。
最快的請求就是沒有請求。
不管什麼時候討論性能,都不能不談緩存。
相信你已經爲頁面上全部資源的都提供了 Expires, ETag, Last-Modified 和 Cache-Control 這些響應頭信息(response headers)。
什麼? 尚未?那你仍是先處理好再來看吧!
Chrome 有兩種不一樣的內部緩存的實現:一種備份於本地磁盤(local disk),另外一種則存儲於內存(in-memory)中。
內存模式(in-memory)主要應用於無痕瀏覽模式(Incognito browsing mode),並在關閉窗口清除掉。
兩種方式使用了相同的內部接口(disk_cache::Backend
和 disk_cache::Entry
),大大簡化了系統架構。
若是你想實現一個本身的緩存算法,能夠很容易地實現進去。
在內部,磁盤緩存(disk cache)實現了它本身的一組數據結構, 它們被存儲在一個單獨的緩存目錄裏。
其中有索引文件(在瀏覽器啓動時加載到內存中),數據文件(存儲着實際數據,以及 HTTP 頭以及其它信息)。
比較有趣的是,16KB 如下的文件存儲於共同的數據塊文件中(data block-files,即小文件集中存儲於一個大文件中),其它較大的文件纔會存儲到本身專屬的文件中。
最後,磁盤緩存的淘汰策略是維護一個 LRU,經過好比訪問頻率和資源的使用時間(age)的度量進行管理。
在 Chrome 開個頁籤,輸入 chrome://net-internals/#httpCache
。
若是你要看到實際的 HTTP 數據和緩存的響應處理,能夠打開 chrome://cache
, 裏面會列出全部緩存中可用的資源。
打開每一項,還能夠看到詳細的數據頭等信息。
前面已經屢次提到了 DNS 預解析,在深刻實現以前,先彙總一下 DNS 預解析的場景和理由:
上述各項對於 Chrome 都只是一個線索。
Chrome 並不保證預解析必定會被執行,全部的線索會由 Predictor 進行評估,以決定後續的操做。
最壞的狀況下,可能沒法及時解析主機名,用戶就必須等待一個 DNS 解析時間,而後是 TCP 鏈接時間,最後是資源加載時間。
Predictor 會記下這個場景,在將來決策時相應地加以參考。
總之,必定是越用越快。
以前提過到 Chrome 能夠 記住每一個頁面的拓撲(topology),並能夠基於這個信息進行加速。
還記得吧,平均每一個頁面帶有 88 個資源,分別來自於 30 多個獨立的主機。
每打開這 個頁面,Chrome 會記下資源中比較經常使用的主機名,在後續的瀏覽過程當中,Chrome 就會發起針對某些主機或者所有主機的 DNS 解析,甚至是 TCP 預鏈接!
使用 chrome://dns
就能夠觀察到上面的數據(Google+ 頁面), 其中有 6 個子資源對應的主機名,並記錄了 DNS 預解析發生的次數,TCP 預鏈接發生的次數,以及到每一個主機的請求次數。
這些數據就可讓 Chrome Predictor 執行相應的優化決策。
除了內部事件通知外,頁面設計者能夠在頁面中嵌入以下的語句請求瀏覽器進行預解析:
<link rel="dns-prefetch" href="//host_name_to_prefetch.com">
之因此有這個需求,一個典型的例子是重定向(redirects)。
Chrome 自己沒辦法判斷出這種模式,經過這種方式則可讓瀏覽器提早進行解析。
具體的實現也是因版本而有所差別,整體而言,Chrome 中的 DNS 處理有兩個主要的實現:
getaddrinfo()
系統函數實現。依賴於系統的實現,代碼少並且簡單,可是 getaddrInfo()
是一個阻塞式的系統調用,沒法有效地並行多個查詢操做。
經驗數據還顯示,並行請求過多甚至會超出路由器的負額。
Chrome 爲此設計了一個複雜的機制。
對於其中帶有 worker-pool 的預解析, Chrome 只是簡單的發送 getaddrinfo()
調用, 同時阻塞 worker thread 直到收到響應數據。
由於系統有 DNS
緩存的緣由,針對解析過的主機,解析操做會當即返回。這個過程簡單,有效。
但還不夠!getaddrinfo()
隱藏了太多有用的信息,好比 Time-to-live(TTL) 時間戳, DNS 緩存的狀態等。
因而 Chrome 決定本身實現一套跨平臺的異步 DNS 解析器。
這個新技術能夠支持如下優化:
Chrome 仍然進行着持續的優化。經過 chrome://histograms/DNS
能夠觀察到 DNS 度量數據:
上面的柱狀圖展現了 DNS 預解析延遲時間的分佈:好比將近 50% (最右側)的查詢在 20ms 內完成。
這些數據基於最近的瀏覽操做(採樣 9869 次),用戶能夠選擇是否報告這些使用數據,而後這些數據會以匿名的形式交由工程團隊加以分析,這樣就能夠了解到功能的性能,以及將來如何進一步調整。
周而復始,不斷優化。
已經預解析到了主機名,也有了由 OmniBox 和 Chrome Predictor 提供信號,預示着用戶將來的操做。
爲何再進一步鏈接到目標主機,在用戶真正發起請求前完成 TCP 握手呢?
這樣就可省掉了另外一個往返的延遲,輕易地就能爲用戶節省到上百毫秒。
其實,這就是 TCP 預鏈接的工做。
經過訪問 chrome://dns
就能夠看到 TCP 預鏈接的使用狀況。
首先,Chrome 檢查它的 socket pool 裏有沒有目標主機能夠複用的 socket, 這些 sockets 會在 socket pool 裏保留一段時間,以節省 TCP 握手時間及啓動延時(slow-start penalty)。
若是沒有可用的 socket,就須要發起 TCP 握手,而後放到 socket pool 中。
這樣當用戶發起請求時,就能夠用這個 socket 當即發起 HTTP 請求。
打開 chrome://net-internals#sockets
就能夠看到當前的 sockets 的狀態:
你能夠看到每個 socket 的時間軸:鏈接和代理的時間,每一個封包到達的時間,以及其它一些信息。
你也能夠把這些數據導出,以方便進一步分析或者報告問題。
有好的測試數據是優化的基礎, chrome://net-internals
就是 Chrome 網絡的信息中心。
Chrome 支持在頁面的 HTML 標籤中加入的兩個線索來優化資源加載:
<link rel="subresource" href="/javascript/myapp.js"> <link rel="prefetch" href="/images/big.jpeg">
在 rel
中指定的 subresource(子資源)和 prefetch(預加載)很是類似。
不一樣的是,若是一個 link 指定 rel(relation) 爲 prefetch 後,就是告訴瀏覽器這個資源是稍後的頁面中要用到的。
而指定爲 subresource 則表示在本頁中就會用到,指望能在使用前加載。
二者不一樣的語義讓 resource loader 有不一樣的行爲。
prefetch 的優先級較低,通常只會在頁面加載完成後纔會開始。
而 subresource 則會在解析出來時就被嘗試優先執行。
還要注意,prefetch 是 HTML5 的一部分,Firefox 和 Chrome 均可以支持。
但 subresource 還只能用在 Chrome 中。
不過,並非全部的 Web 開發者會願意加入上面所述的 subresource relation,就算加了,也要等收到主文檔並解析出這些內容才行,這段時間開銷依賴於服務器的響應時間和客戶端與服務器間的延遲時間,甚至要耗去上千毫秒。
和前面的預解析,預鏈接同樣,這裏還有一個 prefreshing::
直到在 2013 年初, prefreshing 仍是處於早期的討論階段。
若是經過數據結果分析,這個功能最終上線了,咱們就能夠稍晚些時候使用到它了。
前面討論的每一個優化都用來幫助減小用戶發起請求到看到頁面內容的延遲時間。
多快才能帶來即時呈現的體驗呢?
基於用戶體驗數據,這個時間是 100 毫秒,根本沒給網絡延遲留什麼空間。
而在 100 毫秒內,又怎樣渲染頁面呢?
你們可能都有這樣的體驗: 同時開多個頁籤時會明顯快於在一個頁籤中等待。
瀏覽器爲此提供了一個實現方式:
<link rel="prerender" href="http://example.org/index.html">
這就是 Chrome 的預渲染(prerendering in Chrome)! 相對於只下載一個資源的「prefetch」, 「prerender」會讓 Chrome 在一個不可見的頁籤中渲染一個頁面,包含了它全部的子資源。
當用戶要瀏覽它時,這個頁籤被切到前臺,作到了即時的體驗。
能夠瀏覽 prerender-test.appspot.com 來體驗一下效果,再經過 chrome://net-internals/#prerender
查看下歷史記錄和預鏈接頁面的狀態。
由於預渲染會同時消耗 CPU 和網絡資源,因些必定要在確信預渲染頁面會被使用到狀況下才用。
Google Search 就在它的搜索結果里加入 prerender, 由於第一個搜索結果極可能就是下一個頁面(也叫做Google Instant Pages)
你可使用預渲染特性,但如下限制項必定要牢記:
預渲染只能應用於確信安全的頁面。
另外 JavaScript 也最好在運行時使用Page Visibility API來判斷一下當前頁是否可見(參考 you should be doing anyway) !
最後,總之,Chrome 正逐步優化網絡延遲和用戶體驗,讓它隨着用戶的使用愈來愈快!
Ilya Grigorik is a web performance engineer and developer advocate on the Make The Web Fast team at Google, where he spends his days and nights on making the web fast and driving adoption of performance best practices.
Follow @igrigorik
文章來源:UC技術博客