瀏覽器輸入 URL 后發(fā)生了什么茅主?

這是一道非常經典的題目舞痰,相信你被面試或者面試別人有非常大的概率接觸過,也可能只是其中某一部分進行提問诀姚。這道題涵蓋的知識點非常多响牛,考察得比較全面,網上一搜也有成百上千篇文章,不同的人有不同的見解呀打,然而大部分都是千篇一律矢赁。如果你沒有深入透徹系統(tǒng)性地研究過,光靠死記硬背贬丛,面試官稍稍針對某一點提問撩银,或者換成另外一種方式提問,就有可能露出破綻豺憔。仔細想想额获,學習積累到了一定階段,也該憑技術儲備對知識體系進行一遍全面的梳理總結恭应。

開放性的題目咪啡,沒有固定的答案,涉及計算機圖形學暮屡、操作系統(tǒng)、編譯原理毅桃、計算機網絡褒纲、通信原理、分布式系統(tǒng)钥飞、瀏覽器原理等多個不同的學科莺掠、領域。但無論從哪個領域入手读宙,軟件角度或硬件角度彻秆,鋪開來講都可以是長篇大論。如果你專精某個學科領域多年结闸,那你在這一方面肯定比我有更深厚的經驗唇兑、更獨特的見解,歡迎指點桦锄。

從我的角度來看扎附,在題意不夠明確、缺少情景和限定條件的情況下结耀,沒法直接作答留夜。在計算機越來越復雜的今天,任何一個條件的變化與組合图甜,都有可能產生千千萬萬種可能碍粥,打破常規(guī)。對題目本身而言黑毅,就會包括但不僅限于以下幾種條件:

  • 請求資源類型
  • 瀏覽器類型及版本
  • 服務器類型及版本
  • 網絡協(xié)議類型及版本
  • 網絡鏈路狀況
  • 經過哪些中間設備
  • 局域網類型及標準
  • 物理媒介類型
  • 運營商路線

如果請求的是靜態(tài)資源嚼摩,那么流量有可能到達 CDN 服務器;如果請求的是動態(tài)資源,那么情況更加復雜低斋,流量可能依次經過代理/網關蜂厅、Web 服務器、應用服務器膊畴、數據庫掘猿。圖 1 為阿里云 SLB(Server Load Balancer,負載均衡)高可用部署示意圖唇跨,它不同于傳統(tǒng)的主備切換模式過于依賴單機處理能力稠通,來自公網的請求通過上層交換機的 ECMP(Equal-cost multi-path routing,等價多路徑路由)將流量轉發(fā)給 LVS 集群(四層 SLB)买猖,對于 TCP/UDP 請求改橘,LVS 集群直接轉發(fā)給后端 ECS 集群,對于 HTTP 請求玉控,則轉發(fā)給 Tengine 集群(七層 SLB)飞主,由 Tengine 集群再轉發(fā)給后端 ECS 集群,集群之間通過實現會話同步高诺、健康檢查等機制來保證高可用碌识。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 1:阿里云負載均衡服務</figcaption>

隨著業(yè)務規(guī)模的不斷擴大,為了承載千萬級甚至億級流量及海量存儲虱而,對系統(tǒng)的多機房容災筏餐、自由擴容的要求越來越高,系統(tǒng)還可能演進為異地多活架構(圖 2)牡拇。與傳統(tǒng)的災備設計不同的是魁瞪,多個數據中心同時對外提供服務,同時保證異地單元間數據庫數據的一致性和完整性(CAP 理論)惠呼。整個系統(tǒng)架構分為流量層导俘、應用層、數據層剔蹋,利用 DNS 技術實現 GSLB(Global Server Load Balance趟畏,全局負載均衡),實現用戶就近訪問滩租。如果某個地域的系統(tǒng)發(fā)生整體故障赋秀,則把所有流量請求切換到另一個地域,來滿足異地容災律想,這也類似于餓了么現階段整體架構方案猎莲。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 2:阿里云異地多活解決方案</figcaption>

但是,不同于動態(tài)資源技即,考慮部署成本和流量成本著洼,靜態(tài)資源一般是通過 CDN,利用中間服務器作緩存,如果沒有命中緩存身笤,再回源到 OSS(Object Storage Service豹悬,阿里云對象存儲服務)或者私有服務器。

因此液荸,現實世界的情況是千變萬化的瞻佛,你也很難想象一個 GET 請求甚至觸發(fā)銀行的轉賬操作,一切取決于人為實現娇钱。重新回到本文的主題伤柄,我們排除一切特殊條件,把問題簡化一下文搂,如果僅僅考慮:

  • 一個 Chrome 瀏覽器
  • 一臺 Linux 服務器
  • 發(fā)起 HTML 請求
  • 不考慮任何緩存和優(yōu)化機制
  • 采用 HTTP/1.1 + TLS/1.2 + TCP 協(xié)議

這個過程如下:

DNS 解析過程

首先适刀,瀏覽器向本地 DNS 服務器發(fā)起請求,由于本地 DNS 服務器沒有緩存不能直接將域名轉換為 IP 地址煤蹭,需要采用遞歸或者迭代查詢的方式(圖 3)依次向根域名服務器笔喉、頂級域名服務器、權威域名服務器發(fā)起查詢請求硝皂,直至找到一個或一組 IP 地址然遏,返回給瀏覽器。一般本地 DNS 地址由 ISP(Internet Service Provider吧彪,互聯(lián)網服務提供商)通過 DHCP 協(xié)議動態(tài)分配,我們仍可以手動把它修改為公共 DNS丢早,比如 Google 提供的 8.8.8.8姨裸,國內的 114.114.114.114,它們分布在不同的地理位置上怨酝,借助 Anycast 技術傀缩,將請求路由到離用戶最近的 DNS 服務器上。為了讓 DNS 解析更加精確农猬,客戶端還需在請求包里帶上自己的源 IP 地址赡艰,否則類似 GSLB 的 DNS 服務器不能夠精準地匹配判斷離用戶最近的目標 IP 地址。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 3:DNS 查詢過程</figcaption>

HTTP 請求過程

由于 HTTP 是基于更易于閱讀的文本格式斤葱,除了在瀏覽器直接發(fā)起 HTTP 之外慷垮,你也可以用命令行 telnet 來與服務器指定端口建立 TCP 連接,按照協(xié)議規(guī)定的格式揍堕,來發(fā)送請求頭和請求實體料身。另外,如果想查看詳細具體的封包內容衩茸,可以使用網絡封包分析工具 Wireshark 或命令行 tcpdump芹血,來捕獲某一塊網卡上的數據包。在上一步我們通過 DNS 解析拿到服務器 IP 地址后,瀏覽器再通過系統(tǒng)調用 Socket 接口與服務器 443 端口進行通信幔烛,整個過程可以分解為建立連接啃擦、發(fā)送 HTTP 請求、返回 HTTP 響應饿悬、維持連接令蛉、釋放連接五個部分(圖 4)。圖中所示箭頭有可能代表一個 TCP 報文段乡恕,也有可能代表一個完整的應用層報文言询,在實際傳輸過程中,會被組合為一個或分片為多個 TCP 報文段傲宜。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 4:HTTP 請求過程</figcaption>

建立連接

  1. 在連接建立之前运杭,服務器必須做好接受連接的準備,通過調用 socket函卒、bind辆憔、listen 和 accept 四個函數來完成綁定公網 IP、監(jiān)聽 443 端口和接受請求的任務报嵌。
  2. 客戶端通過 socket 和 connect 兩個函數來主動打開連接虱咧,給服務器發(fā)送帶有 SYN 標志位的分組,隨機生成一個初始序列號 x锚国,以及附帶 MSS(Maximum Segment Size腕巡,最大段大小)等額外信息血筑。為了避免在網絡層被 IP 協(xié)議分片使得出現丟失錯誤的概率增加绘沉,及達到最佳的傳輸效果,MSS 的值一般為以太網 MTU(Maximum Transmission Unit豺总,最大傳輸單元)的值減去 IP 頭部和 TCP 頭部大小车伞,等于 1460 字節(jié)。
  3. 服務器必須確認收到客戶端的分組喻喳,發(fā)送帶有 SYN+ACK 標志位的分組另玖,隨機生成一個初始序列號 y,確認號為 x+1表伦,以及附帶 MSS 等額外信息谦去。當一端收到另外一端的 MSS 值時,會根據兩者的 MSS 取最小值來決定隨后的 TCP 最大報文段大小蹦哼。
  4. 客戶端確認收到服務器的分組哪轿,發(fā)送帶有 ACK 標志位的分組,確認號為 y+1翔怎,從而建立 TCP 連接窃诉。
  5. 如果客戶端此前未與服務器建立會話杨耙,那么雙方需要進行一次完整的 TLS 四次握手∑矗客戶端首先向服務器發(fā)送 Client Hello 報文珊膜,包含一個隨機數、TLS 協(xié)議版本宣脉、按優(yōu)先級排列的加密套件列表车柠。
  6. 服務器向客戶端發(fā)送 Server Hello 報文,包含一個新的隨機數塑猖、TLS 協(xié)議版本竹祷、經過選擇后的一個加密套件。
  7. 服務器向客戶端發(fā)送 Certificate 報文羊苟,包含服務器 X.509 證書鏈塑陵,其中,第一個為主證書蜡励,中間證書按照順序跟在主證書之后令花,而根 CA 證書通常內置在操作系統(tǒng)或瀏覽器中培愁,無需服務器發(fā)送霹粥。
  8. 如果密鑰交換選擇 DH 算法,服務器會向客戶端發(fā)送 Server Key Exchange 報文套媚,包含密鑰交換所需的 DH 參數稽寒;如果密鑰交換選擇 RSA 算法扮碧,則跳過這一步。
  9. 服務器向客戶端發(fā)送 Server Hello Done 報文杏糙,表明已經發(fā)送完所有握手消息慎王。
  10. 客戶端向服務器發(fā)送 Client Key Exchange 報文,如果密鑰交換選擇 RSA 算法搔啊,由客戶端生成預主密鑰,使用服務器證書中的公鑰對其加密北戏,包含在報文中负芋,服務器只需使用自己的私鑰解密就可以取出預主密鑰;如果密鑰交換選擇 DH 算法嗜愈,客戶端會在報文中包含自己的 DH 參數旧蛾,之后雙方都根據 DH 算法計算出相同的預主密鑰。需要注意的是蠕嫁,密鑰交換的只是預主密鑰锨天,這個值還需進一步加工,結合客戶端和服務器兩個隨機數種子剃毒,雙方使用 PRF(pseudorandom function病袄,偽隨機函數)生成相同的主密鑰搂赋。
  11. 客戶端向服務器發(fā)送 Change Cipher Spec 報文,表明已經生成主密鑰益缠,在隨后的傳輸過程都使用這個主密鑰對消息進行對稱加密脑奠。
  12. 客戶端向服務器發(fā)送 Finished 報文,這條消息是經過加密的幅慌,因此在 Wireshark 中顯示的是 Encrypted Handshake Message宋欺。如果服務器能解密出報文內容,則說明雙方生成的主密鑰是一致的胰伍。
  13. 服務器向客戶端發(fā)送 New Session Ticket 報文齿诞,而這個 Session Ticket 只有服務器才能解密,客戶端把它保存下來骂租,在以后的 TLS 重新握手過程中帶上它進行快速會話恢復祷杈,減少往返延遲。
  14. 服務器向客戶端發(fā)送 Change Cipher Spec 報文菩咨,同樣表明已經生成主密鑰吠式,在隨后的傳輸過程都使用這個主密鑰對消息進行對稱加密。
  15. 服務器向客戶端發(fā)送 Finished 報文抽米,如果客戶端能解密出報文內容特占,則說明雙方生成的主密鑰是一致的。至此云茸,完成所有握手協(xié)商是目。

發(fā)送 HTTP 請求

建立起安全的加密信道后,瀏覽器開始發(fā)送 HTTP 請求标捺,一個請求報文由請求行懊纳、請求頭、空行亡容、實體(Get 請求沒有)組成嗤疯。請求頭由通用首部、請求首部闺兢、實體首部茂缚、擴展首部組成。其中屋谭,通用首部表示無論是請求報文還是響應報文都可以使用脚囊,比如 Date;請求首部表示只有在請求報文中才有意義桐磁,分為 Accept 首部悔耘、條件請求首部、安全請求首部和代理請求首部這四類我擂;實體首部作用于實體內容衬以,分為內容首部和緩存首部這兩類缓艳;擴展首部表示用戶自定義的首部,通過 X-前綴來添加泄鹏。另外需要注意的是郎任,HTTP 請求頭是不區(qū)分大小寫的,它基于 ASCII 進行編碼备籽,而實體可以基于其它編碼方式舶治,由 Content-Type 決定。

返回 HTTP 響應

服務器接受并處理完請求车猬,返回 HTTP 響應霉猛,一個響應報文格式基本等同于請求報文,由響應行珠闰、響應頭惜浅、空行、實體組成伏嗜。區(qū)別于請求頭坛悉,響應頭有自己的響應首部集,比如 Vary承绸、Set-Cookie裸影,其它的通用首部、實體首部军熏、擴展首部則共用轩猩。此外,瀏覽器和服務器必須保證 HTTP 的傳輸順序荡澎,各自維護的隊列中請求/響應順序必須一一對應均践,否則會出現亂序而出錯的情況。

維持連接

完成一次 HTTP 請求后摩幔,服務器并不是馬上斷開與客戶端的連接彤委。在 HTTP/1.1 中,Connection: keep-alive 是默認啟用的或衡,表示持久連接焦影,以便處理不久后到來的新請求,無需重新建立連接而增加慢啟動開銷薇宠,提高網絡的吞吐能力偷办。在反向代理軟件 Nginx 中艰额,持久連接超時時間默認值為 75 秒澄港,如果 75 秒內沒有新到達的請求,則斷開與客戶端的連接柄沮。同時回梧,瀏覽器每隔 45 秒會向服務器發(fā)送 TCP keep-alive 探測包废岂,來判斷 TCP 連接狀況,如果沒有收到 ACK 應答狱意,則主動斷開與服務器的連接湖苞。注意,HTTP keep-alive 和 TCP keep-alive 雖然都是一種毕甓冢活機制财骨,但是它們完全不相同,一個作用于應用層藏姐,一個作用于傳輸層隆箩。

斷開連接

  1. 服務器向客戶端發(fā)送 Alert 報文,類型為 Close Notify羔杨,通知客戶端不再發(fā)送數據捌臊,即將關閉連接,同樣兜材,這條報文也是經過加密處理的理澎。
  2. 服務器通過調用 close 函數主動關閉連接,向客戶端發(fā)送帶有 FIN 標志位的分組曙寡,序列號為 m糠爬。
  3. 客戶端確認收到該分組,向服務器發(fā)送帶有 ACK 標志位的分組卵皂,確認號為 m+1秩铆。
  4. 客戶端發(fā)送完所有數據后,向服務器發(fā)送帶有 FIN 標志位的分組灯变,序列號為 n殴玛。
  5. 服務器確認收到該分組,向客戶端發(fā)送帶有 ACK 標志位的分組添祸,序列號為 n+1滚粟。客戶端收到確認分組后刃泌,立即進入 CLOSED 狀態(tài)凡壤;同時,服務器等待 2 個 MSL(Maximum Segment Lifetime耙替,最大報文生存時間) 的時間后亚侠,進入 CLOSED 狀態(tài)。

瀏覽器解析過程

現代瀏覽器是一個及其龐大的大型軟件俗扇,在某種程度上甚至不亞于一個操作系統(tǒng)硝烂,它由多媒體支持、圖形顯示铜幽、GPU 渲染滞谢、進程管理串稀、內存管理、沙箱機制狮杨、存儲系統(tǒng)母截、網絡管理等大大小小數百個組件組成。雖然開發(fā)者在開發(fā) Web 應用時橄教,無需關心底層實現細節(jié)清寇,只需將頁面代碼交付于瀏覽器計算,就可以展示出豐富的內容护蝶。但頁面性能不僅僅關乎瀏覽器的實現方式颗管,更取決于開發(fā)者的水平,對工具的熟悉程度滓走,代碼優(yōu)化是無止盡的垦江。顯然,了解瀏覽器的基本原理搅方,了解 W3C 技術標準比吭,了解網絡協(xié)議,對設計姨涡、開發(fā)一個高性能 Web 應用幫助非常大衩藤。

當我們在使用 Chrome 瀏覽器時,其背后的引擎是 Google 開源的 Chromium 項目涛漂,而 Chromium 的內核則是渲染引擎 Blink(基于 Webkit)和 JavaScript 引擎 V8赏表。在闡述瀏覽器解析 HTML 文件之前,先簡單介紹一下 Chromium 的多進程多線程架構(圖 5)匈仗,它包括多個進程:

  • 一個 Browser 進程
  • 多個 Renderer 進程
  • 一個 GPU 進程
  • 多個 NPAPI Render 進程
  • 多個 Pepper Plugin 進程

而每個進程包括若干個線程:

  • 一個主線程
  • 在 Browser 進程中:渲染更新界面
  • 在 Renderer 進程中:使用持有的內核 Blink 實例解析渲染更新界面
  • 一個 IO 線程
  • 在 Browser 進程中:處理 IPC 通信和網絡請求
  • 在 Renderer 進程中:處理與 Browser 進程之間的 IPC 通信
  • 一組專用線程
  • 一個通用線程池

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 5:Chromium 多進程多線程架構</figcaption>

Chromium 支持多種不同的方式管理 Renderer 進程瓢剿,不僅僅是每一個開啟的 Tab 頁面,iframe 頁面也包括在內悠轩,每個 Renderer 進程是一個獨立的沙箱间狂,相互之間隔離不受影響。

  • Process-per-site-instance:每個域名開啟一個進程火架,并且從一個頁面鏈接打開的新頁面共享一個進程(noopener 屬性除外)鉴象,這是默認模式
  • Process-per-site:每個域名開啟一個進程
  • Process-per-tab:每個 Tab 頁面開啟一個進程
  • Single process:所有頁面共享一個進程

當 Renderer 進程需要訪問網絡請求模塊(XHR、Fetch)何鸡,以及訪問存儲系統(tǒng)(同步 Local Storage纺弊、同步 Cookie、異步 Cookie Store)時骡男,則調用 RenderProcess 全局對象通過 IO 線程與 Browser 進程中的 RenderProcessHost 對象建立 IPC 信道淆游,底層通過 socketpair 來實現。正由于這種機制,Chromium 可以更好地統(tǒng)一管理資源稽犁、調度資源,有效地減少網絡骚亿、性能開銷已亥。

主流程

頁面的解析工作是在 Renderer 進程中進行的,Renderer 進程通過在主線程中持有的 Blink 實例邊接收邊解析 HTML 內容(圖 6)来屠,每次從網絡緩沖區(qū)中讀取 8KB 以內的數據虑椎。瀏覽器自上而下逐行解析 HTML 內容,經過詞法分析俱笛、語法分析捆姜,構建 DOM 樹。當遇到外部 CSS 鏈接時迎膜,主線程調用網絡請求模塊異步獲取資源泥技,不阻塞而繼續(xù)構建 DOM 樹。當 CSS 下載完畢后磕仅,主線程在合適的時機解析 CSS 內容珊豹,經過詞法分析、語法分析榕订,構建 CSSOM 樹店茶。瀏覽器結合 DOM 樹和 CSSOM 樹構建 Render 樹,并計算布局屬性劫恒,每個 Node 的幾何屬性和在坐標系中的位置贩幻,最后進行繪制展示在屏幕上。當遇到外部 JS 鏈接時两嘴,主線程調用網絡請求模塊異步獲取資源丛楚,由于 JS 可能會修改 DOM 樹和 CSSOM 樹而造成回流和重繪,此時 DOM 樹的構建是處于阻塞狀態(tài)的憔辫。但主線程并不會掛起鸯檬,瀏覽器會使用一個輕量級的掃描器去發(fā)現后續(xù)需要下載的外部資源,提前發(fā)起網絡請求螺垢,而腳本內部的資源不會識別喧务,比如 document.write。當 JS 下載完畢后枉圃,瀏覽器調用 V8 引擎在 Script Streamer 線程中解析功茴、編譯 JS 內容,并在主線程中執(zhí)行(圖 7)孽亲。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 6:Webkit 主流程</figcaption>

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 7:V8 解釋流程坎穿,Chrome 66 以前對比 Chrome 66</figcaption>

渲染流程

當 DOM 樹構建完畢后,還需經過好幾次轉換,它們有多種中間表示(圖 8)玲昧。首先計算布局栖茉、繪圖樣式,轉換為 RenderObject 樹(也叫 Render 樹)孵延。再轉換為 RenderLayer 樹吕漂,當 RenderObject 擁有同一個坐標系(比如 canvas、absolute)時尘应,它們會合并為一個 RenderLayer惶凝,這一步由 CPU 負責合成。接著轉換為 GraphicsLayer 樹犬钢,當 RenderLayer 滿足合成層條件(比如 transform苍鲜,熟知的硬件加速)時,會有自己的 GraphicsLayer玷犹,否則與父節(jié)點合并混滔,這一步同樣由 CPU 負責合成。最后歹颓,每個 GraphicsLayer 都有一個 GraphicsContext 對象遍坟,負責將層繪制成位圖作為紋理上傳給 GPU,由 GPU 負責合成多個紋理晴股,最終顯示在屏幕上愿伴。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 8:從 DOM 樹到 GraphicsLayer 樹的轉換</figcaption>

另外,為了提升渲染性能效率电湘,瀏覽器會有專用的 Compositor 線程來負責層合成(圖 9)隔节,同時負責處理部分交互事件(比如滾動、觸摸)寂呛,直接響應 UI 更新而不阻塞主線程怎诫。主線程把 RenderLayer 樹同步給 Compositor 線程,由它開啟多個 Rasterizer 線程贷痪,進行光柵化處理幻妓,在可視區(qū)域以瓦片為單位把頂點數據轉換為片元,最后交付給 GPU 進行最終合成渲染劫拢。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 9:Chromium 多線程渲染</figcaption>

頁面生命周期

頁面從發(fā)起請求開始肉津,結束于跳轉、刷新或關閉舱沧,會經過多次狀態(tài)變化和事件通知妹沙,因此了解整個過程的生命周期非常有必要。瀏覽器提供了 Navigation TimingResource Timing 兩種 API 來記錄每一個資源的事件發(fā)生時間點熟吏,你可以用它來收集 RUM(Real User Monitoring距糖,真實用戶監(jiān)控)數據玄窝,發(fā)送給后端監(jiān)控服務,綜合分析頁面性能來不斷改善用戶體驗悍引。圖 10 表示 HTML 資源加載的事件記錄全過程恩脂,而中間黃色部分表示其它資源(CSS、JS趣斤、IMG俩块、XHR)加載事件記錄過程,它們都可以通過調用 window.performance.getEntries() 來獲取具體指標數據唬渗。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 10:頁面加載事件記錄流程</figcaption>

衡量一個頁面性能的方式有很多,但能給用戶帶來直接感受的是頁面何時渲染完成奋渔、何時可交互镊逝、何時加載完成。其中嫉鲸,有兩個非常重要的生命周期事件撑蒜,DOMContentLoaded 事件表示 DOM 樹構建完畢,可以安全地訪問 DOM 樹所有 Node 節(jié)點玄渗、綁定事件等等座菠;load 事件表示所有資源都加載完畢,圖片藤树、背景浴滴、內容都已經完成渲染,頁面處于可交互狀態(tài)岁钓。但是迄今為止瀏覽器并不能像 Android 和 iOS app 一樣完全掌控應用的狀態(tài)升略,在前后臺切換的時候,重新分配資源屡限,合理地利用內存品嚣。實際上,現代瀏覽器都已經在做這方面的相關優(yōu)化钧大,并且自 Chrome 68 以后提供了Page Lifecycle API翰撑,定義了全新的瀏覽器生命周期(圖 11),讓開發(fā)者可以構建更出色的應用啊央。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 11:新版頁面生命周期</figcaption>

現在眶诈,你可以通過給 windowdocument 綁定上所有生命周期監(jiān)聽事件(圖 12),來監(jiān)測頁面切換瓜饥、用戶交互行為所觸發(fā)的狀態(tài)變化過程册养。不過,開發(fā)者只能感知事件在何時發(fā)生压固,不能直接獲取某一刻的頁面狀態(tài)(圖 11 中的 STATE)球拦。即使如此,利用這個 API,也可以讓腳本在合適的時機執(zhí)行某項任務或進行界面 UI 反饋坎炼。

<figcaption style="margin-top: 0.66667em; padding: 0px 1em; font-size: 0.9em; line-height: 1.5; text-align: center; color: rgb(153, 153, 153);">圖 12:生命周期監(jiān)聽事件</figcaption>

總結

篇幅有限愧膀,本文并不是一篇大而全的面試寶典,只涉及其中一些關鍵路徑和知識點谣光,開篇也提到問題本身沒有標準答案檩淋。文中也有很多沒有深入展開分析的地方,一是過于偏離主題萄金,二是個人能力有限蟀悦,比如編碼規(guī)則、加密算法氧敢、摘要算法日戈、路由算法、壓縮算法孙乖、協(xié)議標準浙炼、緩存機制、V8 原理唯袄、GPU 原理等等弯屈。如果想透徹理解每一個部分的技術細節(jié)和原理,可以結合參考其它書籍恋拷、資料和源碼资厉。

??最后,注意防脫蔬顾。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末酌住,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子阎抒,更是在濱河造成了極大的恐慌酪我,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件且叁,死亡現場離奇詭異都哭,居然都是意外死亡,警方通過查閱死者的電腦和手機逞带,發(fā)現死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門欺矫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人展氓,你說我怎么就攤上這事穆趴。” “怎么了遇汞?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵未妹,是天一觀的道長簿废。 經常有香客問我,道長络它,這世上最難降的妖魔是什么族檬? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮化戳,結果婚禮上单料,老公的妹妹穿的比我還像新娘。我一直安慰自己点楼,他們只是感情好扫尖,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掠廓,像睡著了一般换怖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上却盘,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天狰域,我揣著相機與錄音媳拴,去河邊找鬼黄橘。 笑死,一個胖子當著我的面吹牛屈溉,可吹牛的內容都是我干的塞关。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼子巾,長吁一口氣:“原來是場噩夢啊……” “哼帆赢!你這毒婦竟也來了?” 一聲冷哼從身側響起线梗,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤椰于,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后仪搔,有當地人在樹林里發(fā)現了一具尸體瘾婿,經...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年烤咧,在試婚紗的時候發(fā)現自己被綠了偏陪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡煮嫌,死狀恐怖笛谦,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情昌阿,我是刑警寧澤饥脑,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布恳邀,位于F島的核電站,受9級特大地震影響好啰,放射性物質發(fā)生泄漏轩娶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一框往、第九天 我趴在偏房一處隱蔽的房頂上張望鳄抒。 院中可真熱鬧,春花似錦椰弊、人聲如沸许溅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贤重。三九已至,卻和暖如春清焕,著一層夾襖步出監(jiān)牢的瞬間并蝗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工秸妥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滚停,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓粥惧,卻偏偏與公主長得像键畴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子突雪,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內容