現代瀏覽器探祕(part2):導航

瘋狂的技術宅發表於2019-03-01

翻譯:瘋狂的技術宅

原文:developers.google.com/web/updates…

原文首發於京程一燈公眾號:jingchengyideng

導航時都發生了什麼

這是關於Chrome內部工作原理系列的第2部分。 在上一篇文章中,我們研究了不同的程式與執行緒是怎樣如何處理瀏覽器不同部分的。 在這一篇中,我們將會深入研究每個程式和執行緒是如何進行通訊以顯示網站內容的。

讓我們看一下Web瀏覽的簡單用例:你在瀏覽器中鍵入URL,然後瀏覽器從Internet獲取資料並顯示頁面。 在這篇文章中,我們將重點關注使用者請求網站的部分以及瀏覽器準備呈現頁面的部分 – 也稱為導航。

從瀏覽器程式開始

正如我們在第1部分(CPU,GPU,記憶體和多程式架構 )中所描述的,選項卡外部的所有內容都由瀏覽器程式處理。 瀏覽器程式具有很多執行緒,比如於UI執行緒用於繪製瀏覽器的按鈕和輸入框,網路執行緒負責處理網路堆疊以從網際網路接收資料,儲存執行緒控制對檔案的訪問等。 當在位址列中鍵入URL時,你的輸入將由瀏覽器程式的UI執行緒處理。

Browser processes

圖1:頂部的瀏覽器UI,底部有UI,網路和儲存執行緒的瀏覽器程式圖

一個簡單的導航過程

第1步:處理輸入

當使用者開始輸入位址列時,UI執行緒首先要判斷的是“這是搜尋查詢還是URL?”。 因為在Chrome中,位址列也是搜尋輸入框,因此UI執行緒需要解析並判斷是將你的輸入傳送到搜尋引擎還是去請求對應的網站。

Handling user input

圖1:UI執行緒詢問輸入是搜尋查詢還是URL

第2步:開始導航

當使用者敲回車時,UI執行緒啟動網路呼叫以獲取站點內容。 載入指示圖示顯示在選項卡的一角,網路執行緒使用適當的協議,如DNS解析和為請求建立TLS連線。

Navigation start

圖2:UI執行緒與網路執行緒進行通訊以導航到mysite.com

此時,網路執行緒可以接收像HTTP 301那樣的伺服器重定向頭。在這種情況下,網路執行緒會通知UI執行緒伺服器正在請求重定向。之後會啟動另一個URL請求。

第3步:讀取響應

一旦響應主體(有效負載)開始進入,網路執行緒會在必要時檢視流的前幾個位元組。 響應中的Content-Type頭應該說明它是什麼型別的資料,但由於它可能丟失或發生錯誤,所以在這裡完成MIME型別嗅探(developer.mozilla.org/en-US/docs/… 這在原始碼的註釋中被稱為“棘手的事情”(cs.chromium.org/chromium/sr… 你可以閱讀這些註釋,來了解不同的瀏覽器是如何處理內容型別與有效載荷的。

HTTP response

圖3:包含Content-Type和有效負載的響應頭,它是實際資料

如果響應是HTML檔案,那麼下一步就是將資料傳遞給渲染器程式,但如果它是zip檔案或其他檔案,則表示它是一個下載請求,因此需要將資料傳遞給 下載管理器。

MIME type sniffing

圖4:網路執行緒詢問響應資料是否來自安全站點的HTML

這也是進行 SafeBrowsing(safebrowsing.google.com/)檢查的地方。 如果域和響應資料似乎與已知的惡意站點匹配,則網路執行緒會發出警告以顯示警告頁面。 此外,發生跨源讀取阻止(CORB)(www.chromium.org/Home/chromi…

第3步:查詢渲染器程式

完成所有檢查並且網路執行緒確信瀏覽器應該導航到所請求的站點後,網路執行緒會告知UI執行緒資料已準備就緒。 然後UI執行緒找到渲染器程式以進行網頁的渲染。

Find renderer process

圖5:網路執行緒告訴UI執行緒找到渲染程式

由於網路請求可能需要幾百毫秒才能得到響應,所以在這裡進行了加速此過程的優化。 當UI執行緒在第2步向網路執行緒傳送URL請求時,它已經知道他們正在導航到哪個站點。 UI執行緒嘗試與網路請求並行地主動查詢或啟動渲染器程式。 如果一切按預期進行,當網路執行緒接收資料時,渲染器程式已處於備用狀態。 如果導航重定向跨站點,則可能不會使用此備用程式,在這種情況下可能需要不同的程式。

第4步:提交導航

現在資料和渲染器程式已準備就緒,IPC將把導航從瀏覽器程式傳送到渲染器程式以進行提交。它同時還傳遞資料流,因此渲染器程式可以繼續接收HTML資料。 一旦瀏覽器進確認已經提交到了渲染器程式中,導航就完成了,文件載入階段就開始了。

此時,位址列會更新,安全指示器和站點設定UI會反映新頁面的站點資訊。 選項卡的會話歷史記錄將更新,因此後退/前進按鈕將可以逐步瀏覽剛導航到的站點。為了便於在關閉選項卡或視窗時能夠對選項卡/會話進行還原,會話的歷史記錄將被儲存在磁碟上。

Commit the navigation

圖6:瀏覽器和渲染器程式之間的IPC,請求呈現頁面

額外步驟:初始載入完成

提交導航後,渲染器程式繼續載入資源並呈現頁面。 我們將會在下一篇文章中詳細介紹這一階段的詳情。 一旦渲染器程式“完成”渲染,它就會將一個IPC傳送回瀏覽器程式(這發生在所有onload事件觸發了頁面中的所有幀並完成執行之後)。 此時,UI執行緒會停止選項卡上的載入指示器。

儘管已經“完成”,不過客戶端 JavaScript 仍然可以載入額外的資源並在此之後呈現新的檢視。

Page finish loading

圖7:渲染器程式通過IPC通知瀏覽器程式頁面已“載入完成”

導航到其他站點

簡單的導航完成了! 但是如果使用者再次將不同的URL放到位址列會發生什麼? 好吧,瀏覽器程式會通過相同的步驟導航到不同的站點。 但在它在做到這一點之前,還需要檢查當前正在渲染的站點,如果他們關心beforeunload事件的話。

當你嘗試重新導航或關閉選項卡時,beforeunload可以建立“要離開這個網站嗎?” 警告。 由於選項卡內包含JavaScript程式碼的所有內容都由渲染器程式處理,因此瀏覽器程式必須在進行新導航請求時檢查當前渲染器程式。

警告:不要新增無條件的beforeunload處理程式。 因為它會產生更多延遲,甚至在啟動導航之前需要執行一些處理。 應該僅在需要時新增此事件處理,例如,如果需要警告使用者他們可能會丟失在頁面上輸入的資料時。

beforeunload event handler

圖8:瀏覽器程式通過IPC通知渲染器程式它將要導航到另一個站點

如果導航是從渲染器程式啟動的(例如使用者單擊連結或客戶端JavaScript執行window.location =“https://newsite.com”),那麼渲染器程式會首先檢查beforeunload處理。 然後,它經歷與瀏覽器程式啟動導航相同的過程。 唯一的區別是導航請求從渲染器程式傳送到瀏覽器程式。

當新導航進入的站點與當前渲染的站點不同時,將會呼叫另一個單獨的渲染程式來處理新導航,同時保持當前渲染程式以處理unload等事件。 有關更多資訊,請參閱頁面生命週期狀態概述(developers.google.com/web/updates… 頁面生命週期 API (developers.google.com/web/updates…

new navigation and unload

圖9:從瀏覽器程式到新渲染器程式的2個IPC,通知新渲染器渲染頁面並通知舊渲染器程式解除安裝

如果是Service Worker

最近對該導航過程的一個改變是引入了service worker(developers.google.com/web/fundame… service worker是一種在應用程式碼中編寫網路代理的方法;它允許Web開發人員更好地控制本地快取內容以及何時從網路獲取新資料。 如果將service worker設定為從快取載入頁面,則無需從網路請求資料。

要記住的重要一點是Service Worker是在渲染器程式中執行的JavaScript程式碼。 但是當導航請求到來時,瀏覽器程式怎麼才能知道該站點有Service Worker?

註冊Service Worker時,將保留Service Worker的範圍作為參考(你可以在“Service Worker生命週期”一文中閱讀有關範圍的更多資訊)。 當導航發生時,網路執行緒根據註冊的Service Worker範圍檢查域,如果為該URL註冊了Service Worker,則UI執行緒找到渲染器程式來執行Service Worker程式碼。 Service Worker可以從快取載入資料,無需從網路請求資料,也可以從網路請求新資源。

Service worker scope lookup

圖10:瀏覽器程式中的網路執行緒查詢Service Worker範圍

serviceworker navigation

圖11:瀏覽器程式中的UI執行緒啟動渲染器程式以處理Service Worker; 然後,渲染器程式中的工作執行緒從網路請求資料

導航預載入

可以看到,如果Service Worker最終決定從網路請求資料,則瀏覽器程式和渲染器程式之間的往返通訊可能會導致延遲。 導航預載入是一種通過與Service Worker並行載入資源來加速此過程的機制。 它用header標記這些請求,允許伺服器為這些請求傳送不同的內容,例如:只更新部分資料而不是整個文件。

Navigation preload

圖12:瀏覽器程式中的UI執行緒啟動渲染器程式,在並行啟動網路請求的同時處理Service Worker

總結

在本文中,我們研究了導航過程中發生的事情,以及響應頭和客戶端JavaScript等Web應用程式碼是如何與瀏覽器互動的。 瞭解瀏覽器通過網路獲取資料的步驟,可以更容易地理解為什麼開發導航預載入等API。 在下一篇文章中,我們將深入探討瀏覽器如何處理HTML/ CSS/JavaScript來呈現頁面。

原文首發於京程一燈公眾號:jingchengyideng

相關文章