講解一下瀏覽器的工作原理,以及進(jìn)程?
時間:2024-01-16 02:18:02 | 來源:網(wǎng)站運(yùn)營
時間:2024-01-16 02:18:02 來源:網(wǎng)站運(yùn)營
講解一下瀏覽器的工作原理,以及進(jìn)程?:
背景
為什么要了解瀏覽器原理?
當(dāng)面試官問你輸入url到渲染發(fā)生了什么這種問題你不知所措?
頁面中到底能承載多少個元素,取決于什么條件?如果一個頁面在2s內(nèi)打不開,你應(yīng)該如何優(yōu)化?
DOM是什么,javascript操作的是DOM還是html?
回流和重繪又是什么?瀏覽器架構(gòu)是什么樣的?
當(dāng)你能夠細(xì)化的了解整個了瀏覽器工作原理的時候,你就能很好的處理這些問題
到底什么是瀏覽器
瀏覽器我們常用的有谷歌 IE Safari 火狐等等,目前開發(fā)者心中的瀏覽器只有一個,那就是谷歌瀏覽器,它的市場份額穩(wěn)居第一,從未被超越
image.png以工程師的維度,或者開發(fā)者的維度怎么看瀏覽器,它是一套標(biāo)準(zhǔn),這套標(biāo)準(zhǔn)可以運(yùn)行html、css、javascript代碼,這些內(nèi)容可以通過超文本傳輸協(xié)議進(jìn)行傳輸,通過瀏覽器的標(biāo)準(zhǔn)進(jìn)行展現(xiàn),能夠讓世界上任何使用瀏覽器的人都能夠看到網(wǎng)頁內(nèi)容
頁面的標(biāo)準(zhǔn)有W3C,語言標(biāo)準(zhǔn)有ECMAScript,當(dāng)然還有網(wǎng)絡(luò)標(biāo)準(zhǔn),等各種標(biāo)準(zhǔn)由瀏覽器統(tǒng)一管理。這些標(biāo)準(zhǔn)共同作用,即便是不同的瀏覽器,它們的標(biāo)準(zhǔn)也是一致的。當(dāng)然并不是完全一致,一些瀏覽器還是有自己的想法的,就是這些想法給前端程序員帶來了巨大的工作量,比如工程師們聞之色變的兼容IE,主要原因就是IE瀏覽器有自己的想法,搞了很多和別人不太一樣的東西,導(dǎo)致同樣的代碼和邏輯,在IE瀏覽器上展示就有問題
image.png線程和進(jìn)程
在深入研究瀏覽器架構(gòu)之前要掌握的另一個概念是進(jìn)程和線程。通常一個好的應(yīng)用程序會把自己劃分為幾個獨(dú)立的模塊和幾個相互配合的模塊,瀏覽器本身也是這樣,谷歌瀏覽器為例,它由多個線程和多個進(jìn)程組成,進(jìn)程之間相互協(xié)作完成瀏覽器整體的大功能,進(jìn)程又包含很多線程,一個進(jìn)程內(nèi)的線程也會相互配合完成這個進(jìn)程的工作職責(zé)
對于前端同學(xué)來說,這些概念是模糊的,概念上講什么進(jìn)程是資源分配的最小單位,線程是CPU調(diào)度的最小單位,很多同學(xué)聽了可能還是有些懵懂,我們用圖來簡單講述一下
以工廠為例,我們可以把工廠理解成進(jìn)程,工人理解成線程,工人只能在工廠工作,一個工廠可以有很多工人,同一個工廠內(nèi)的工人很容易交流,不同的工廠內(nèi)的工人不容易交流,只是會比較費(fèi)勁,一個工廠不會影響另一個工廠,但是工廠內(nèi)的工人會影響這個工廠的運(yùn)行
當(dāng)啟動一個應(yīng)用程序時,會創(chuàng)建一個進(jìn)程或者多個進(jìn)程,該程序可能會創(chuàng)建線程來幫助它工作。操作系統(tǒng)為每個進(jìn)程提供了可用的內(nèi)存,所有應(yīng)用程序狀態(tài)都保存在內(nèi)存空間中。當(dāng)您關(guān)閉應(yīng)用程序時,程序創(chuàng)建的進(jìn)程也會消失,占有的內(nèi)存也會被釋放
chrome架構(gòu)
了解了進(jìn)程和線程的關(guān)系之后,我們可以看一下啟動chrome瀏覽器需要占用多少進(jìn)程
多進(jìn)程架構(gòu)
谷歌瀏覽器自帶了一個任務(wù)管理器,點(diǎn)擊瀏覽器【更多工具】→【任務(wù)管理器】
image.png可以看到對應(yīng)的瀏覽器進(jìn)程
image.png各進(jìn)程作用
這張圖其實(shí)就表明瀏覽器其實(shí)是多進(jìn)程的架構(gòu),當(dāng)然這是不斷演化的結(jié)果,并不是一蹴而就的,簡單來看一下這些進(jìn)程的作用
- 瀏覽器進(jìn)程:主要用來控制瀏覽器部分,比如地址欄、書簽欄、后退和前進(jìn)的按鈕,當(dāng)然還有一些不可見的部分,比如網(wǎng)絡(luò)請求和文件的處理,同時也負(fù)責(zé)其它進(jìn)程的調(diào)度
- GPU進(jìn)程:跟其它進(jìn)程關(guān)系不大,主要用來獨(dú)立處理GPU任務(wù),比如整個應(yīng)用程序的繪制,網(wǎng)頁的成像功能
- 渲染進(jìn)程:負(fù)責(zé)網(wǎng)站的渲染,代碼運(yùn)行,web worker的管理
- Network進(jìn)程、Storage進(jìn)程、Audio進(jìn)程等看名字就知道是用來干嘛的
- 百度標(biāo)簽頁進(jìn)程&谷歌標(biāo)簽頁進(jìn)程:表明一個標(biāo)簽頁就是一個進(jìn)程
- 擴(kuò)展程序進(jìn)程:我們安裝的一些插件,每個插件也占用一個進(jìn)程
image.png多進(jìn)程優(yōu)缺點(diǎn)
為什么每個標(biāo)簽或者每個插件都要一個進(jìn)程呢?其實(shí)思考一下不難理解,如果我們打開的某一個網(wǎng)頁無響應(yīng)了,或者奔潰了,這個時候每個進(jìn)程是隔離的,并不會影響我們其它的頁面,這種設(shè)計(jì)其一是保證了標(biāo)簽頁的穩(wěn)定性。同樣的如果某個頁面死循環(huán)不流暢,其它頁面也是感知不到的,內(nèi)存泄漏也一樣,當(dāng)前頁面由于內(nèi)存泄漏關(guān)閉這個標(biāo)簽頁之后,對應(yīng)的內(nèi)存資源就會被回收,變相解決了內(nèi)存泄漏導(dǎo)致整個瀏覽器奔潰的問題
多進(jìn)程還有一個好處就是安全性,因?yàn)椴僮飨到y(tǒng)有一種限制進(jìn)程權(quán)限的方法,所以瀏覽器可以通過某些功能對某些進(jìn)程采取沙箱處理,比如瀏覽器限制了渲染進(jìn)程的任意文件的訪問。沙箱處理隔離了渲染進(jìn)程,這樣即使在渲染進(jìn)程里面執(zhí)行惡意程序,惡意程序也無法突破沙箱獲取系統(tǒng)的權(quán)限
當(dāng)然并不是多進(jìn)程都是好的,也有其不足之處,比如我打開了多個百度頁面的選項(xiàng)卡,這個時候?yàn)g覽器分配了多個進(jìn)程,導(dǎo)致更多的資源占用,雖然這兩個內(nèi)存中的東西完全一致
image.png當(dāng)然谷歌瀏覽器針對這一點(diǎn)也做了對應(yīng)的優(yōu)化,它的內(nèi)部限制了可以啟動的進(jìn)程數(shù)量,這個限制取決于你的設(shè)備的內(nèi)存和cpu的功率,并不是固定死的,設(shè)備越好可啟動的進(jìn)程數(shù)量就越多。當(dāng)達(dá)到它所限制的數(shù)量時,它會優(yōu)化打開的標(biāo)簽頁,比如相同站點(diǎn)的標(biāo)簽頁合并為同一個進(jìn)程
當(dāng)然多個標(biāo)簽跟開啟多個瀏覽器類似,谷歌瀏覽器也在不斷優(yōu)化,將瀏覽器中的各個部分作為一項(xiàng)服務(wù),從多進(jìn)程模型到多服務(wù)模型,可以輕松的進(jìn)行進(jìn)程拆分或者合并。也就是說當(dāng)你的硬件性能足夠,它可以將每個服務(wù)拆分到不同的進(jìn)程,當(dāng)你的硬件資源有限,它會將這些服務(wù)合并到一個進(jìn)程
站點(diǎn)隔離
前面說我們每個標(biāo)簽頁一個進(jìn)程,但是這個標(biāo)簽頁當(dāng)中有可能通過iframe嵌入了另一個頁面,這個時候如果公用一個進(jìn)程的話可能就會帶來風(fēng)險(xiǎn)。所以谷歌瀏覽器為跨站點(diǎn)的iframe頁面也開啟了一個單獨(dú)的渲染進(jìn)程。要考慮瀏覽器同源策略的影響,一個站點(diǎn)無法在未經(jīng)允許的情況下訪問其它站點(diǎn)的數(shù)據(jù),進(jìn)程隔離是分割站點(diǎn)的最有效的方式
站點(diǎn)隔離并不是我們想象的這么簡單,它改變了iframe和頁面的交互方式,即便是多個渲染進(jìn)程,當(dāng)你打開devtools的時候,它們看起來還是那么完美,并且當(dāng)你用ctrl+f對頁面進(jìn)行搜索的時候,多個渲染進(jìn)程之間的搜索工程師們也優(yōu)化的你看不出絲毫破綻
了解了瀏覽器的架構(gòu),來講一個面試官最喜歡問的面試題,當(dāng)瀏覽器輸入url之后發(fā)生了什么?
輸入url之后發(fā)生了什么
我們使用瀏覽器的主要目的就是為了搜索或者訪問某些網(wǎng)站,就讓我們從瀏覽器的角度,來看看我們是如何進(jìn)行搜索或者網(wǎng)站的訪問的
image.png從瀏覽器的架構(gòu)中我們可以得知,我們輸入url或者搜索的這一欄是由瀏覽器進(jìn)程控制的,其中瀏覽器進(jìn)程下面有一些線程,比如控制搜索欄交互和展示的UI線程,當(dāng)你輸入網(wǎng)址或者文字之后,UI線程便開始了工作
輸入還是搜索
當(dāng)你在地址欄輸入了內(nèi)容之后,UI線程要做的操作就是需要進(jìn)行辨別,判斷你輸入的是url還是搜索的字段,如果是url,則相應(yīng)的轉(zhuǎn)到對應(yīng)的站點(diǎn)。如果是搜索的字段,則通過瀏覽器中設(shè)置的使用那種搜索引擎,進(jìn)行對應(yīng)的站點(diǎn)跳轉(zhuǎn)
image.png不論是搜索還是站點(diǎn)訪問,最終都會走站點(diǎn)訪問的邏輯,當(dāng)你在地址欄輸入【你好】之后,回車,它也會變成相應(yīng)的站點(diǎn)url
image.png如何判斷是否是URL
要判斷是否是URL就要知道什么是URL(
「U」niform
「R」esource
「L」ocator)翻譯過來為統(tǒng)一資源定位符,俗稱網(wǎng)址
它的標(biāo)準(zhǔn)格式為:
? [協(xié)議類型]://[服務(wù)器地址]:[端口號]/[資源層級UNIX文件路徑][文件名]?[查詢]#[片段ID]
?
一個簡單的URL組成如下所示
一個完整的URL如下所示
image.png只要輸入的內(nèi)容滿足這個規(guī)則,就認(rèn)為是一個有效的URL,直接跳轉(zhuǎn)到對應(yīng)的網(wǎng)站,否則就執(zhí)行搜索邏輯(本質(zhì)還是跳轉(zhuǎn)到對應(yīng)的URL)
獲取內(nèi)容
拿到URL之后,是不是立刻就會發(fā)送請求?其實(shí)不是,瀏覽器還會進(jìn)行額外的一些檢查。
比如安全性檢查,檢查要訪問的內(nèi)容在本地是不是有緩存,緩存是否過期?一個較為完整的前置流程判斷大致如下
這張圖其實(shí)畫的有點(diǎn)多,但是主要是想讓大家了解在獲取資源之前,其實(shí)是有一個緩存的判斷的,否則就不會發(fā)送對應(yīng)的請求
image.png在控制臺能夠看到一些資源雖然返回了200的狀態(tài)碼,但是實(shí)際是來自緩存,并沒有從服務(wù)器獲取數(shù)據(jù),抓包的話也是沒有對應(yīng)的請求的
強(qiáng)緩存和協(xié)商緩存
上面其實(shí)講的是強(qiáng)緩存,強(qiáng)緩存是有對應(yīng)的過期時間的,時間是響應(yīng)標(biāo)頭expires控制,當(dāng)然圖中還有標(biāo)注cache-control這個字段,這兩個字短有啥區(qū)別呢?
expires是http1.0的產(chǎn)物,cache-control是1.1的產(chǎn)物,兩個同時存在,cache-control的優(yōu)先級更高
image.png圖中還有一個字段,是last-modified,這個主要用于協(xié)商緩存,如果強(qiáng)緩存生效,則直接走強(qiáng)緩存,不生效則走協(xié)商緩存,協(xié)商緩存會在請求頭中添加if-Modified-Since的字段,這個字段的值就是第一次請求文件的時候返回頭中的last-modified,服務(wù)收到請求后,對比服務(wù)中文件修改的時間,如果沒變化,狀態(tài)碼返回304,瀏覽器直接從緩存中獲取文件,如果有更新,則服務(wù)端會返回200狀態(tài),并返回新的文件,最后瀏覽器再將新的響應(yīng)報(bào)文和對應(yīng)的緩存標(biāo)識緩存起來
協(xié)商緩存還有一個字段是etag,圖中也有,它是根據(jù)文件內(nèi)容是否有修改來決定是否使用緩存數(shù)據(jù)
上述的緩存有些內(nèi)容可能還未涉及,但是大家先了解一個大概,像請求報(bào)文之類的我們后續(xù)會詳細(xì)說明,因?yàn)檫@里涉及到緩存,大家有個概念即可,我們看一下正常的請求流程是什么樣的,不走緩存的這種
DNS解析
如果緩存都未命中,我們就需要瀏覽器去發(fā)送請求,請求網(wǎng)址對應(yīng)的資源,但是我們都知道,服務(wù)器的地址都是一段ip地址,但是我們明明輸入的是一個URL,URL怎么能夠知道我們訪問的是哪一個服務(wù)器呢?
這就引出了DNS的概念,DNS其實(shí)就是用于實(shí)現(xiàn)域名和IP相互映射的一個分布式數(shù)據(jù)庫,它可以將域名翻譯成計(jì)算機(jī)可識別的IP地址
借用網(wǎng)上的一張圖,來看看DNS的查詢流程是怎樣的
image.png- 看瀏覽器中是否有URL對應(yīng)的IP緩存
- 當(dāng)瀏覽器中沒有的時候,查看系統(tǒng)的hosts文件是否有域名對應(yīng)的IP
image.png- 查看路由器緩存(上述兩種都沒有的時候)這些都是客戶端的緩存
- 當(dāng)客戶端沒有對應(yīng)緩存的時候,開始詢問服務(wù)器
- 進(jìn)入ISP DNS緩存,也就是運(yùn)營商緩存,比如你用的移動或者聯(lián)通的寬帶,他們有自己的DNS緩存服務(wù)器
- 當(dāng)以上均沒有,則進(jìn)入根域名服務(wù)器進(jìn)行查詢,全球就13臺根域名服務(wù)器,1個主的,12個輔的。若無則將其管轄范圍內(nèi)頂級域名(如.com、.cn等)服務(wù)器 IP 告訴本地 DNS 服務(wù)器
- 詢問頂級域名服務(wù)器,若無記錄則將其管轄范圍內(nèi)權(quán)威域名服務(wù)器的 IP 地址告訴本地 DNS 服務(wù)器
- 詢問權(quán)威域名(主域名)服務(wù)器
- 本地域名服務(wù)器把返回的結(jié)果保存到緩存,以備下一次使用,同時將該結(jié)果反饋給客戶端,客戶端拿到了對應(yīng)的IP地址,就能訪問到對應(yīng)的服務(wù)器,DNS查詢結(jié)束
DNS是極其重要的一環(huán),這個環(huán)節(jié)出了問題就無法進(jìn)行后續(xù)的操作,它是客戶端訪問互聯(lián)網(wǎng)的關(guān)鍵所在
建立TCP連接
通過DNS解析我們已經(jīng)獲取到了文件所在的服務(wù)器的IP,有了這個IP之后,我們就需要發(fā)送請求獲取對應(yīng)的文件了,但是在獲取文件的第一步,首先要做的就是建立TCP連接
一個頁面的性能的影響因素首要的就是首屏渲染時間,而首屏渲染時間其中的一個影響因素就是網(wǎng)絡(luò)加載速度,決定性的內(nèi)容就是文件的返回時間
如果你對網(wǎng)絡(luò)有充分的了解,那肯定知道網(wǎng)絡(luò)的基礎(chǔ)是網(wǎng)絡(luò)協(xié)議,網(wǎng)站則是基于http協(xié)議,http又是基于TCP/IP的。計(jì)算機(jī)底層是101010這種二進(jìn)制數(shù)據(jù),文件傳輸也是二進(jìn)制數(shù)據(jù),那這些數(shù)據(jù)是如何到我們的瀏覽器的?第一步就是要建立服務(wù)器與客戶端之間的連接
image.png上面我們已經(jīng)獲取到了服務(wù)端的IP地址,每個計(jì)算機(jī)都有一個獨(dú)立的IP地址,客戶端和服務(wù)端有了各自的地址之后就能夠精準(zhǔn)傳輸了,整個過程如下圖所示
建立連接
建立連接的過程就是三次握手的過程,表示整個過程要發(fā)送三次包
image.png- 客戶端對服務(wù)端發(fā)起連接請求,攜帶syn=1、seq=x的報(bào)文
- 服務(wù)端接收到了客戶端的請求,如果確認(rèn)可以連接,則返回報(bào)文,攜帶syn=1、ack=x+1、seq=y
- 客戶端收到服務(wù)端發(fā)送的報(bào)文之后,檢查是否符合要求,通知應(yīng)用連接已經(jīng)建立,向服務(wù)端發(fā)送確認(rèn)信息seq=z,ack=y+1,服務(wù)端校驗(yàn)正確則建立成功
連接建立成功,就可以傳輸數(shù)據(jù)了
數(shù)據(jù)傳輸完成,有一個斷開的過程
連接斷開
斷開連接被稱為四次揮手過程,表示整個斷開過程要發(fā)送四次包,需要雙向連接和雙向關(guān)閉,不管是客戶端還是服務(wù)端,任何一方都可以發(fā)起斷開的過程
image.png- 首先主動方要求斷開連接,發(fā)送fin=1 ack=z seq=x的報(bào)文,請求斷開連接
- 被動方接收到報(bào)文,發(fā)送ack=x+1、seq=z表明我知道了
- 發(fā)送完數(shù)據(jù)后,再發(fā)送一個fin=1、ack=x、seq=y的報(bào)文,告訴主動方數(shù)據(jù)發(fā)送完畢
- 主動方收到之后,發(fā)送ack表明確認(rèn)收到,tcp連接斷開
獲取資源
上一步已經(jīng)建立好了連接,那就開始傳輸數(shù)據(jù)了。在該階段,客戶端需要對數(shù)據(jù)包進(jìn)行確認(rèn)操作,在接收到數(shù)據(jù)包之后,需要發(fā)送確認(rèn)數(shù)據(jù)包給發(fā)送端。所以發(fā)送了一個數(shù)據(jù)包之后,在規(guī)定時間內(nèi)沒有接收到客戶端端反饋的確認(rèn)消息,則判斷為數(shù)據(jù)包丟失,并觸發(fā)發(fā)送端的重發(fā)機(jī)制。同樣,一個大的文件在傳輸過程中會被拆分成很多小的數(shù)據(jù)包,這些數(shù)據(jù)包到達(dá)接收端后,接收端會按照TCP頭中的序號為其排序,從而保證組成完整的數(shù)據(jù)。
請求頭
通過TCP以及UDP共同作用,這個時候?yàn)g覽器的網(wǎng)絡(luò)線程是能夠收到服務(wù)器的完整數(shù)據(jù),在獲取數(shù)據(jù)的時候,我們會添加一系列的請求頭,比如我們必須指定請求方法到底是GET還是POST,或者是其它,之前我們也提到了If-Modified-Since,用于緩存,還有Cookie、User-Agent、Referer等頭信息
用這些請求頭數(shù)據(jù)去告訴服務(wù)器我們當(dāng)前需要什么內(nèi)容,以及告訴服務(wù)端客戶端的一些信息
image.png響應(yīng)頭
當(dāng)服務(wù)端同意或者拒絕給客戶端返回內(nèi)容之后,客戶端都會收到一些反饋,反饋的內(nèi)容除了正常我們想要的數(shù)據(jù)以外,還有response頭信息??梢钥吹綄?yīng)的狀態(tài)代碼為200,表示成功,說明獲取到了服務(wù)端返回的對應(yīng)信息。
這個域名對應(yīng)的IP+端口就是圖中的遠(yuǎn)程地址,除了狀態(tài)為200還有其它的一些狀態(tài),比如301告知服務(wù)器正在重定向,然后網(wǎng)絡(luò)線程發(fā)起另一個URL請求,針對http狀態(tài)碼各個階段的含義大家可以自行了解一下
- 1XX Informational(請求正在處理)
- 2XX Success(請求成功)
- 3XX Redirection(重定向) 需要進(jìn)行附加操作以完成請求
- 4XX Client Error(客戶端錯誤)
- 5XX Server Error(服務(wù)器錯誤)
上面的圖中可以看到響應(yīng)頭里面包含了一些信息或者執(zhí)行了一些操作,比如Set-Cookie響應(yīng)頭可以往瀏覽器里面設(shè)置一些cookie,Conetnt-Type、Cache-Control等相關(guān)
網(wǎng)絡(luò)線程在查看了頭部的這些字節(jié)之后,因?yàn)閭鬏斶^程有可能會出現(xiàn)異常,比如丟失或者錯誤,所以在這里會完成MIME類型嗅探(也就是檢查一個字節(jié)流的內(nèi)容,試圖推斷其中文件的數(shù)據(jù)格式)
image.png上面我們在訪問百度的時候,Conetnt-Type為html類型的文件,瀏覽器檢測到如果文件類型是html的話,之后就會把數(shù)據(jù)交給渲染進(jìn)程。當(dāng)然這里不僅僅只有html文件類型,如果是其它文件類型,比如zip或者其它的內(nèi)容,則瀏覽器會交給下載管理器進(jìn)行對應(yīng)資源的下載
這個時候通常也會進(jìn)行瀏覽器的安全檢查,兩方面檢查
- 如果這個站點(diǎn)和已知的惡意站點(diǎn)匹配,則網(wǎng)絡(luò)線程發(fā)出警告,表明這是一個惡意站點(diǎn)
image.png- 還有一個檢查的點(diǎn)大家都比較熟悉,那就是跨域問題的檢測,跨域本質(zhì)是瀏覽器的安全檢查機(jī)制,如果發(fā)現(xiàn)請求的URL的協(xié)議域名端口任意一個和當(dāng)前站點(diǎn)不同即為跨域,這個檢查也會在這個階段,確保敏感的跨站點(diǎn)的數(shù)據(jù)不會進(jìn)入渲染進(jìn)程
所以我們要明確的一點(diǎn)是,跨域是瀏覽器的安全策略,是瀏覽器攔截的,如果你用抓包工具的話,會發(fā)現(xiàn)數(shù)據(jù)其實(shí)已經(jīng)給到我們了,當(dāng)然post請求還會存在一個預(yù)檢的過程,防止抓到數(shù)據(jù),在發(fā)正式請求之前,預(yù)檢服務(wù)端是否做了跨域的處理
渲染
當(dāng)前已經(jīng)準(zhǔn)備好了對應(yīng)的數(shù)據(jù),也就是html文件。并且完成了前置的所有信息檢查,那么網(wǎng)絡(luò)線程就會告訴UI線程數(shù)據(jù)已經(jīng)準(zhǔn)備就緒,UI線程要做的就是找一個渲染進(jìn)程用于html的渲染
但是這個過程是有優(yōu)化的空間的,因?yàn)榫W(wǎng)絡(luò)線程請求數(shù)據(jù)的過程是需要時間的,所以在網(wǎng)絡(luò)線程發(fā)送URL的請求的時候,它已經(jīng)知道當(dāng)前是要訪問哪個站點(diǎn),UI線程將會并行查找并啟動渲染進(jìn)程,這個時候請求到數(shù)據(jù)的時候,渲染進(jìn)程已經(jīng)是待命的狀態(tài),可用于直接渲染
這個時候需要瀏覽器進(jìn)程跟渲染進(jìn)程通過IPC進(jìn)行通信,通信過程還需要傳遞數(shù)據(jù)流,方便渲染進(jìn)程可以持續(xù)接收html數(shù)據(jù),一旦渲染進(jìn)程渲染完畢,便會通知瀏覽器進(jìn)程當(dāng)前完畢,導(dǎo)航階段就完成了,就開始了加載文檔的過程
這個時候,地址欄更新。安全指示器和站點(diǎn)設(shè)置的UI反應(yīng)站點(diǎn)的信息,選項(xiàng)卡的歷史記錄會被更新,前進(jìn)后退等歷史記錄逐步被更新,歷史記錄同樣也會在磁盤上存儲一份,方便進(jìn)行整個歷史瀏覽的檢索
image.png我們知道,當(dāng)頁面進(jìn)行加載的時候,瀏覽器UI上tab標(biāo)簽頁上會有一個加載中的loading標(biāo)志,一旦渲染進(jìn)程完成渲染,渲染進(jìn)程會將回調(diào)通過IPC發(fā)送到瀏覽器進(jìn)程(onload事件完成的時候,包含所有子頁面(frame)),瀏覽器UI上loading標(biāo)志消失,顯示完成狀態(tài),但是這個結(jié)束并不代表頁面渲染就完成了,有可能還有JavaScript在加載額外的資源或者新的視圖
這個時候渲染進(jìn)程便開始渲染,具體是如何渲染的我們之后詳細(xì)講述,我們再看一下在這基礎(chǔ)如何訪問另一個頁面
訪問不同站點(diǎn)
在當(dāng)前標(biāo)簽頁,我們進(jìn)行另一個頁面訪問的時候,瀏覽器進(jìn)程會重復(fù)上面的過程。但是開始的時候,瀏覽器會確認(rèn)當(dāng)前的站點(diǎn)是否關(guān)心beforeunload這個事件,如果對這個事件做了監(jiān)聽,當(dāng)訪問另一個網(wǎng)站或者刷新的時候,就會彈出一下選項(xiàng)進(jìn)行確認(rèn)
window.addEventListener('beforeunload', (event) => { // 顯示確認(rèn)對話框 event.preventDefault(); // 為了兼容處理,Chrome需要設(shè)置returnValue event.returnValue = '';});
當(dāng)然,這只是眾多生命周期中的一個節(jié)點(diǎn),還有比如unload,pagehide,pageshow等等,感興趣的可以參照
https://developers.google.com/web/updates/2018/07/page-lifecycle-apiservice worker
這里我們再插入一個知識點(diǎn),service worker,它的作用是什么呢?其實(shí)就是服務(wù)器和瀏覽器之間的一個中間人。目的是為了攔截網(wǎng)站的所有請求,可以進(jìn)行相應(yīng)的判斷,如果一些接口可以直接使用緩存就直接返回緩存
service worker獨(dú)立于當(dāng)前網(wǎng)頁的線程,所以執(zhí)行大量的操作也不會阻塞主線程
渲染進(jìn)程如何工作
上面的過程把html文件已經(jīng)交給了渲染進(jìn)程,渲染進(jìn)程負(fù)責(zé)頁簽的顯示,在一個渲染進(jìn)程中,主線程負(fù)責(zé)解析,編譯代碼,運(yùn)行等工作,它的核心就是將HTML、CSS和JavaScript轉(zhuǎn)換成用戶可以與之交互的網(wǎng)頁
當(dāng)然渲染進(jìn)程是一個多線程架構(gòu),它主要有以下線程:GUI線程、JavaScript引擎線程、定時器觸發(fā)線程、事件觸發(fā)線程、http請求線程、合成線程和IO線程
GUI渲染線程
拿到數(shù)據(jù)之后,GUI渲染線程就開始解析HTML并將其轉(zhuǎn)換成DOM(文檔對象模型),DOM是瀏覽器對頁面的內(nèi)部表示,javascript獲取和操作的頁面元素本質(zhì)是瀏覽器提供的DOM數(shù)據(jù),同時當(dāng)頁面發(fā)生重繪和回流的時候,該線程也會執(zhí)行
在解析過程中,即便是你的html語法有一些異常,比如沒有關(guān)閉標(biāo)簽,匹配錯誤等,瀏覽器也不會拋出異常,比如如下代碼,在瀏覽器上會自動解析成功
<body> <div> </p></body>
image.png這里還需要注意一點(diǎn)的是,GUI渲染線程和JavaScript引擎線程是互斥的,當(dāng)JavaScript引擎線程執(zhí)行的時候,GUI線程是被掛起的,相當(dāng)于是凍結(jié)狀態(tài),GUI的更新會被保存在一個隊(duì)列中等JavaScript引擎空閑的時候立刻執(zhí)行。之所以這樣是因?yàn)镴S代碼可能會改變DOM結(jié)構(gòu),所以JavaScript引擎執(zhí)行時間過長是會阻塞頁面的渲染的,了解這一點(diǎn)也就知道為什么fiber架構(gòu)為什么能夠讓大型應(yīng)用看起來不卡頓
在解析html的過程中,其實(shí)還有一些其它的資源,比如img或者link,這個時候就會給瀏覽器進(jìn)程的網(wǎng)絡(luò)線程發(fā)送信息,GUI線程會根據(jù)這些額外的資源是否會阻塞轉(zhuǎn)換過程而決定是不是需要資源加載完畢。比如碰到script標(biāo)簽有可能就會阻塞,但是也有例外,script標(biāo)簽添加了async或者defer屬性
JavaScript引擎線程
負(fù)責(zé)解析JavaScript腳本,運(yùn)行代碼
事件觸發(fā)線程
比如我們的點(diǎn)擊事件,滾動事件,異步請求,或者執(zhí)行setTimeout等這些事件時,會將對應(yīng)的任務(wù)添加到事件觸發(fā)線程,當(dāng)這個事件被觸發(fā)的時候,則把觸發(fā)的事件回調(diào)添加到待處理隊(duì)列的隊(duì)尾。由于javascript是單線程的,所以處理這些事件都必須排隊(duì)
定時器線程
setInterval與setTimeout所在線程,計(jì)數(shù)通過上面的內(nèi)容,可以得到不可能通過javascript線程計(jì)數(shù),否則會阻塞,因此會有一個單獨(dú)的線程進(jìn)行計(jì)數(shù)的處理,等待時間達(dá)到后,將回調(diào)函數(shù)添加到事件隊(duì)列
HTTP請求線程
在XMLHttpRequest連接后是通過瀏覽器新開一個線程請求,監(jiān)測到獲取了對應(yīng)的內(nèi)容后,將回調(diào)函數(shù)添加到事件隊(duì)列,再由javascript引擎執(zhí)行
合成線程后面會講,IO線程主要用于和其它的線程通信
布局
當(dāng)前DOM已經(jīng)有了,但是精美的頁面光有DOM是不夠的,只有DOM是不會出現(xiàn)我們的五彩斑斕的頁面,需要CSS讓頁面變得更美觀,GUI線程會解析CSS并決定每個DOM元素的樣式
image.png如果你沒有設(shè)置對應(yīng)的樣式,瀏覽器也有自己的內(nèi)置的一些標(biāo)簽樣式,比如h1-h6
有了樣式,渲染進(jìn)程已經(jīng)知道了每個結(jié)點(diǎn)呈現(xiàn)的效果,但是節(jié)點(diǎn)的位置信息怎么來,這個時候需要布局樹,渲染進(jìn)程會遍歷DOM結(jié)構(gòu)(包含樣式),布局樹只包含在頁面中顯示的元素,當(dāng)一個元素被設(shè)置為display: none的時候布局樹中是沒有這個元素的。同理如果div::before { content: 'Hi!' },則布局樹中是存在這個Hi的,DOM樹javascript能夠獲取,但是布局樹獲取不到
布局樹的描述非常具有挑戰(zhàn)性,因?yàn)槟阈枰獙φ麄€頁面進(jìn)行精確的描繪。布局中存在浮動、定位、固定、文字換行,自動伸縮,各種元素的結(jié)合,可以想象這個任務(wù)多么繁重
繪制
我們有了布局樹的信息之后是不是就能繪制了,其實(shí)并不是,雖然你知道了每個元素的位置,但是它們繪制的順序是怎樣的其實(shí)還是不清楚,到底哪個元素先,哪個元素后,了解PS的同學(xué),肯定知道圖層的概念,哪個元素應(yīng)該在哪個元素的頂部?CSS有控制元素層級的一個屬性,叫做z-index,用過的同學(xué)應(yīng)該都了解
image.png這個階段會通過布局樹形成繪制記錄,繪制記錄本質(zhì)就是繪制的一系列步驟,比如我要先干什么,在干什么(先繪制背景,再繪制元素內(nèi)容,再繪制形狀等等)
渲染
渲染的過程開銷是很大的,任何一個小的變化都會引起一系列的變化,當(dāng)布局樹發(fā)生變化的時候,繪制需要重新構(gòu)建頁面變化,頁面有動畫的效果的時候,每一幀都需要更新動畫內(nèi)容,如果無法保證幀動畫,給用戶感官上就會出現(xiàn)卡頓
image.pngimage.pngimage.pngjavascript也會阻塞頁面的渲染,導(dǎo)致卡頓的發(fā)生,可以將 Javascript 操作優(yōu)化成小塊,然后使用requestAnimationFrame()
- 重排:表示布局樹發(fā)生變化的時候,整個布局樹要重新構(gòu)建,形成繪制記錄,重新回爐修煉
- 重繪:不影響元素位置信息的,比如元素的顏色發(fā)生變化,但是元素位置未發(fā)生改變,只需要重繪
合成
目前我們已經(jīng)有了所有的信息,文檔結(jié)構(gòu)-元素樣式-元素幾何-布局樹-繪制記錄,最終將繪制記錄轉(zhuǎn)換到屏幕上的像素稱之為光柵化
之前的方式是可視區(qū)域進(jìn)行光柵化,滾動的時候再次進(jìn)行光柵化,如下所示
AiIny83Lk4rTzsM8bxSn.gif但是現(xiàn)在瀏覽器有著更好的處理方式,這個方式被叫做合成
合成會將一個頁面拆成很多層,每個層在不同的的合成線程中進(jìn)行光柵化,然后組合成一個新的頁面。滾動過程中如果這個層已經(jīng)光柵化,則使用已經(jīng)光柵化的層進(jìn)行合成
Aggd8YLFPckZrBjEj74H.gif那這個時候問題就來了,一個層中要包含哪些元素呢?主線程需要遍歷布局樹,做為開發(fā)者,想要創(chuàng)建一個新的層,可以使用css 屬性will-change讓瀏覽器創(chuàng)建層
光柵化各個層之后,將其存儲在GPU的緩存中,合成線程也能夠決定相應(yīng)的優(yōu)先級,保證用戶看到的部分最先被光柵化,每當(dāng)有交互發(fā)生變化,合成線程就會創(chuàng)建更多的合成幀然后通過 GPU 將新的部分渲染出來
瀏覽器的事件體系
事件是什么?比如按鈕的點(diǎn)擊,input輸入框的內(nèi)容輸入,鼠標(biāo)滾輪和拖拽,都是事件
交互的時候?yàn)g覽器進(jìn)程最先接收到事件,瀏覽器關(guān)心的只有當(dāng)前事件發(fā)生在哪個頁簽,然后將事件位置信息和事件類型發(fā)送到當(dāng)前頁簽的渲染進(jìn)程,渲染進(jìn)程會找到事件發(fā)生的元素和對應(yīng)的事件
image.png但是前面也說到,頁面是被光柵化的,在合成線程處理頁面的時候,合成線程會標(biāo)記有事件監(jiān)聽的區(qū)域,有這些信息,合成線程就會將觸發(fā)的事件發(fā)送給主線程處理
總結(jié)
瀏覽器的多進(jìn)程架構(gòu),根據(jù)不同的功能劃分了不同的進(jìn)程,進(jìn)程內(nèi)不同的使命劃分了不同的線程,當(dāng)用戶開始瀏覽網(wǎng)頁時候,瀏覽器進(jìn)程進(jìn)行處理輸入、開始導(dǎo)航請求數(shù)據(jù)、請求響應(yīng)數(shù)據(jù),查找新建渲染進(jìn)程,提交導(dǎo)航,之后渲染又進(jìn)行了解析HTML構(gòu)建DOM、構(gòu)建過程加載子資源、下載并執(zhí)行JS代碼、樣式計(jì)算、布局、繪制、合成,一步一步的構(gòu)建出一個可交互的WEB頁面,瀏覽器進(jìn)程又接受頁面的交互事件信息,并將其交給渲染進(jìn)程,渲染進(jìn)程內(nèi)主進(jìn)程進(jìn)行命中測試,查找目標(biāo)元素并執(zhí)行綁定的事件,完成頁面的交互。
剛踏上開發(fā)之路時,我?guī)缀踔魂P(guān)注怎樣去寫代碼、怎樣提升自己的生產(chǎn)效率。誠然,這些事情很重要,但與此同時我們也應(yīng)當(dāng)思考瀏覽器會怎么去處理我們書寫的代碼?,F(xiàn)代瀏覽器一直致力探索如何提供更好的用戶體驗(yàn)。書寫對瀏覽器友好的代碼,反過來也能提供友好的用戶體驗(yàn)。希望能夠通過這節(jié)課讓大家了解瀏覽器的運(yùn)行機(jī)制和原理,構(gòu)建出對瀏覽器更為友好的代碼。同時也能夠不斷優(yōu)化我們的業(yè)務(wù),讓用戶體驗(yàn)更上一層樓,這就是本節(jié)課全部內(nèi)容
最后,感謝大家閱讀,碼字不易,一鍵三連
關(guān)鍵詞:原理,進(jìn)程,工作,瀏覽,講解