資源緩存
資源緩存的目的是為了提高資源使用的效率,其基本思想是建立一個資源的緩存池,當需要請求資源的時候先去資源池查找是否有相應的資源,如果沒有則向服務器發送請求,webkit收到資源后將其設置到該資源類的對象中去,以便于緩存后下次使用,webkit從資源池中查找資源的關鍵字是URL,URL標記了資源的唯一性。
以上說明一個問題,如果ulr不同,就算內容相同也會被重新請求。
資源加載
鑒于從網絡獲取資源時一個非常耗時的過程,通常一些資源的加載是異步執行的,也就是說資源的獲取和加載不會阻礙 當前 WebKit的渲染過程,例如圖片、CSS文件。當然,網頁也存在某些特別的資源會阻礙主線程的渲染過程,例如 JavaScript 代碼文件。這會嚴重影響 WebKit 下載資源的效率,因為后面可能還有許多需要下載的資源,WebKit會怎么做?因為主線程被阻礙了,后面的解析工作沒辦法繼續往下進行,所以對于 HTML 網頁中后面使用的資源也沒有辦法知道并發送下載請求。當遇到這種情況的時候,WebKit的做法是這樣的:當前的主線程被阻礙時。WebKit會啟動另外一個線程去遍歷后面的HTML頁面,收集需要的資源URL,然后發送請求,這樣就可以避免被阻礙。與此同時,WebKit 能夠并發下載這些資源,甚至并發下載 JavaScript 代碼資源。
為了驗證以上是否正確,在本地搭了個服務器測試,其中有一個index.html頁面,和一個服務器腳本,服務器腳本用來延遲加載js。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h2>h2</h2>
<script src="http://localhost:8081/index.js"></script>
<h3>h3</h3>
<img src="test/images/0.png" alt="">
<img src="test/images/1.png" alt="">
<img src="test/images/2.png" alt="">
<script src="index.js"></script>
<script>
console.log("ok");
</script>
</body>
</html>
nodejs腳本 index.js
var http = require("http");
http.createServer(function(req,res){
}).listen(8081);
index.html請求了8081端口的一個js腳本,但是我始終不給它響應。
可以看到加載js的那一塊始終是一個pending狀態,但是在js后面的資源還是被下載了,也就是說webkit確實是開了多個線程去下載資源的,但你也可以看到,它雖然是開了多個線程去下載資源,但是并沒有去執行后面的代碼,如下圖
從另外一個方面也可以看出它沒有去執行后面的代碼,我在index.html中寫了這么一段代碼<script src="index.js"></script>
假如說它執行了,那么應該報錯才對,因為這段代碼的內容就是那個nodejs中的,如果我返回內容,效果就是下面這樣
也就是說,雖然資源webkit可能開多個線程去下載,但是執行代碼,始終用的是一個線程,對于執行javascript代碼使用一個線程執行,還算好理解,因為某些js文件可能依賴于其他的js文件,如果說使用多線程,這必然是一個比較麻煩的問題,另外如果某些js文件必須早于其他文件執行,那么多線程依然還是個問題,對于圖片和dom大概也是同樣的道理吧,如果說下載下來的圖片,馬上就去顯示,那如果說我在前面的js中給body加了一個display:none;那么你說是這個圖片是顯示呢還是不顯示呢?不能因為前面的js堵塞了后面的代碼執行,你就亂顯示吧,這也是多線程了一些煩惱吧。
資源的生命周期
同CachedResourceLoader對象一樣,資源池也屬于HTML文檔對象,由于資源池不能無限大,因此要用相應的機制來替換其中的資源,從而加入新的資源,這種機制采用的算法為LRU(最近最少使用)算法。對于打開網頁刷新當前頁面的場景,對于某些資源,webkit需要直接重新發送請求,要求服務器端將內容重新發送過來,但對于大多數資源,Webkit則可以利用HTTP協議減少網絡負載,在HTTP協議中對此有規定,游覽器可以發送消息確認是否需要更新,如果有則游覽器重新獲取該資源,否則就需要利用該資源。
webkit的做法如下,首先判斷資源是否在資源池中,如果在則發送一個HTTP請求給服務器,說明該資源在本地的一些信息,例如修改時間等,服務器根據該信息進行判斷,如果沒有更新則返回狀態碼304表示不需要更新,直接利用資源池中的資源,否則webkit申請下載最新的資源內容。
DNS預取和TCP預連接
一次DNS查詢的平均時間大概在60~120ms之間或者更長,而TCP的三次握手時間大概也是幾十毫秒或者更長,看似是一個很短的時間,但是相對于網頁的渲染來說,這是一個相當長的時間,如何有效的縮短這段時間,Chromium給出了自己的解決方案—DNS預取和TCP預連接。
首先講一下DNS預取,他的主要思想就是利用現有的DNS機制,提前解析網頁中可能的網絡連接。具體來說,就是當用戶正在瀏覽當前網頁的時候,Chromium提取網頁中的超鏈接,將域名抽取出來,利用比較少的CPU和網絡帶寬來解析這部分域名或IP地址,這樣一來,用戶根本感覺不到這一過程。但是當用戶單擊這些鏈接的時候,由于已經提前做好了DNS解析,可以節省不少時間,特別是對于域名解析比較慢的時候,效果尤其明顯。(DNS預取技術針對多個域名采取并行處理的方式,每個域名的解析均是由新開啟的一個線程來處理的,結束后此線程即退出)
當然,我們也可以顯式指定預取哪些域名來讓Chromium解析,這非常直接了當。特別是對于那些需要重定向的域名,具體做法如下:
<link rel="dns-prefetch" href="http://this-is-a-dns-prefetch-example.com">。
DNS預取技術不僅應用于網頁中的超鏈接,當我們在地址欄中輸入地址后,候選項同輸入的地址很匹配的時候,在我們敲下回車鍵獲取該網頁之前,Chromium已經開始使用DNS預取技術解析該域名了。
接下來是TCP預連接,Chromium使用追蹤技術來獲取用戶從什么網頁跳轉到另外一個網頁,可以利用這些數據,一些啟發式的規則和其他暗示來預測用戶下面回單擊什么超鏈接,當有足夠的把握的時候,他便會DNS預取,更進一步,還可以預先簡歷TCP連接。聽起來是不是特別6?不錯,Chromium就是這么6!同DNS預取技術一樣,追蹤技術不僅應用于網頁中的超鏈接,當用戶在地址欄中輸入地址的時候,也同樣適用!
文章列表