原文,Mariko Kosakajavascript
[譯]從內部瞭解現代瀏覽器(1)java
這是本系列博客的第2部分。在上一篇文章中,咱們研究了不一樣的進程和線程如何處理瀏覽器的不一樣部分。在這篇文章中,咱們將深刻研究每一個進程和線程如何進行通訊以顯示網站。瀏覽器
讓咱們看一下Web瀏覽的簡單用例:您在瀏覽器中鍵入URL,而後瀏覽器從Internet獲取數據並顯示頁面。在這篇文章中,咱們將重點關注用戶請求網站的部分以及瀏覽器準備呈現頁面的部分 - 也稱爲導航。緩存
正如咱們在 第1部分中所述:CPU,GPU,內存和多進程架構,選項卡外部的全部內容都由瀏覽器進程處理。瀏覽器進程具備諸如UI線程之類的線程,其繪製瀏覽器的按鈕和輸入字段,處理網絡堆棧從互聯網接收數據的網絡線程,控制對文件的訪問的存儲線程等。當您在地址欄中鍵入URL時,您的輸入將先由瀏覽器進程的UI線程處理。安全
圖1:瀏覽器UI、網絡和存儲線程服務器
當用戶開始在地址欄輸入時,UI線程首先要知道的是「這是搜索查詢仍是URL?」。在Chrome中,地址欄也能夠輸入搜索字段,所以UI線程須要解析並肯定是將您的輸入發送到搜索引擎仍是發送到您請求的網站。網絡
圖2:UI線程詢問輸入是搜索查詢仍是URL架構
當用戶點擊回車時,UI線程啓動網絡調用以獲取站點內容。加載標誌顯示在選項卡的一側,網絡線程經過適當的協議,如DNS 爲請求來查找和創建TLS鏈接。
圖3:UI線程與網絡線程通訊以導航到mysite
此時,網絡線程能夠接收像HTTP 301這樣的服務器重定向頭。好比,當服務器請求重定向時,網絡線程會與UI線程通訊,而後啓動另外一個URL請求。
一旦響應主體(有效載荷)開始進入,網絡線程會在必要時查看流的前幾個字節。響應的Content-Type字段應該說明它是什麼類型的數據,但因爲它可能丟失或錯誤,因此在這裏會完成MIME類型嗅探。這是源代碼中註釋的「棘手的業務」源代碼。您能夠閱讀註釋,以瞭解不一樣的瀏覽器如何處理內容類型/有效載荷對。
圖3:包含Content-Type和有效載荷的響應頭
若是響應是HTML文件,那麼下一步就是將數據傳遞給渲染器進程,但若是它是zip文件或其餘文件,則表示它是下載請求,所以須要將數據傳遞給下載管理器。
圖4:網絡線程詢問響應數據是不是來自安全站點的HTML
這也是SafeBrowsing檢查發生的地方。若是域和響應數據彷佛與已知的惡意站點匹配,則網絡線程會發出警告以顯示警告頁面。此外, 還會發生Cross Origin Read Blocking(CORB)檢查,以確保敏感的跨站點數據沒法進入渲染器進程。
完成全部檢查而且網絡線程確信瀏覽器應導航到所請求的站點後,網絡線程會告知UI線程數據已準備就緒。而後,UI線程找到渲染器進程以進行網頁的渲染。
圖5:網絡線程與ui線程
因爲網絡請求可能須要幾百毫秒才能獲得響應,所以作了加速此過程的優化。當UI線程在步驟2向網絡線程發送URL請求時,它已經知道他們正在導航到哪一個站點。UI線程嘗試與網絡請求並行地主動查找或啓動渲染器進程。這樣,若是一切按預期進行,則當網絡線程接收數據時,渲染器進程已處於備用位置。若是導航重定向跨站點,則可能不會使用此備用進程,在這種狀況下可能須要其餘不一樣的進程;
如今數據和渲染器進程已準備就緒,瀏覽器進程將經過IPC發送到渲染器進程以提交導航。它還會傳遞數據流,所以渲染器進程能夠繼續接收HTML數據。一旦瀏覽器進程監聽到確認提交已在渲染器進程中發生,導航就完成了,文檔加載階段就開始了。
此時,地址欄會更新,安全指示器和站點設置UI會反映新頁面的站點信息。選項卡的會話歷史記錄將被更新,所以後退/前進按鈕也會被應用新的記錄。爲了在關閉選項卡或窗口時簡化選項卡/會話還原,會話歷史記錄會存儲在磁盤上。
圖6:瀏覽器進程和渲染器進程間的ipc,請求渲染頁面
提交導航後,渲染器進程繼續加載資源並呈現頁面。 咱們將在下一篇文章中詳細介紹現階段的狀況。 一旦渲染器進程「完成」渲染,它就會將一個IPC發送回瀏覽器進程(這是在全部onload事件觸發了包含頁面中的全部框架,而且完成執行event handler
以後)。 此時,UI線程會中止選項卡上的加載標誌。 我說的「完成」,由於客戶端JavaScript仍然能夠加載額外的資源並在此以後呈現新的視圖。
圖7:渲染器進程告知瀏覽器進程渲染完成
簡單的導航到此已經完成,可是當用戶在地址欄再次鍵入了其餘的地址將會發生什麼呢?是的,瀏覽器進程會進行相同的步驟去導航到不一樣的網址;可是,在這以前,瀏覽器進程會檢查當前被渲染的網站是否具備beforeupload
。
beforeupload
會建立一個「你肯定要離開此站點」的警告當你想導航到其餘的站點或者關閉這個標籤;你的全部javascript代碼將被渲染器進程處理,因此瀏覽器進程必須在新導航進入的時候檢查當前的渲染器進程;
警告:不要添加無條件的beforeunload
事件處理程序。 它會產生更多延遲,由於在啓動導航以前須要執行處理程序。 應僅在須要時添加此事件處理程序,例如,警告用戶他們可能會丟失他們在頁面上輸入的數據。
圖8:瀏覽器進程告知渲染器進程導航將要變動
若是導航是從渲染器進程啓動的(如用戶單擊連接或客戶端JavaScript已運行window.location =「newsite.com」),則渲染器進程首先檢查beforeunload處理程序。 而後,它進行與瀏覽器進程啓動導航相同的步驟。 惟一的區別是導航請求從渲染器進程到瀏覽器進程觸發的。
當新導航進入與當前渲染不一樣的站點時,將調用單獨的渲染過程來處理新導航,同時保持當前渲染過程以處理卸載等事件。 有關更多信息,請參閱頁面生命週期狀態概述以及如何使用Page Lifecycle API鉤子事件。
圖9:瀏覽器告知渲染器進程須要渲染頁面和卸載頁面
最近有關導航的一個改變是引入了service worker
(連接)。service worker
是一種在應用程序代碼中編寫網絡代理的方法; 容許Web開發人員更好地控制本地緩存內容以及什麼時候從網絡獲取新數據。若是將service worker設置爲從緩存加載頁面,則無需從網絡請求數據。
重要的是service worker
是在渲染器進程中運行的JavaScript代碼。可是當導航請求進入時,瀏覽器進程如何知道該站點有service worker
?
圖10:瀏覽器進程中的網絡線程檢查service worker
註冊域
註冊service worker
時,將保留service worker
的範圍做爲參考(您能夠在此「service worker
生命週期」一文中閱讀有關範圍的更多信息)。當導航發生時,網絡線程根據註冊的service worker
範圍檢查域,若是爲該URL註冊了service worker
,則UI線程找到渲染器進程以執行service worker
代碼。service worker
能夠從緩存加載數據,無需從網絡請求數據,或者能夠從網絡請求新資源。
圖11:瀏覽器進程中的UI線程啓動渲染器進程以處理service worker
; 而後,渲染器進程中的工做線程從網絡請求數據
您能夠看到,若是service worker
最終決定從網絡請求數據,則瀏覽器進程渲染器進程之間的往返可能會致使延遲。 導航預加載( Navigation Preload)是一種經過與service worker
並行加載資源來加速此過程的機制。 它會標記這些請求,容許服務器決定爲這些請求發送不一樣的內容; 例如,只更新數據而不是完整文檔。
圖12:瀏覽器進程中的UI線程啓動渲染器進程以在並行的啓動網絡請求的同時處理service work
在這篇文章中,咱們研究了導航過程當中發生的狀況以及響應頭和客戶端JavaScript等Web應用程序代碼如何與瀏覽器交互。 瞭解瀏覽器經過網絡獲取數據的步驟,能夠更容易地理解爲何開發導航預加載等API。 在下一篇文章中,咱們將深刻探討瀏覽器如何處理HTML / CSS / JavaScript以呈現頁面。