愛伊米

肝完《瀏覽器工作原理與實踐》,我總結了這些

作者:wuwhs

簡介:深圳市某科技公司 前端工程師

前言

作為一名前端er,日常工作打交道最多(之一)的莫過於熟悉而又陌生的瀏覽器了,熟悉是每天都會基於瀏覽器的應用層面之上碼業務,陌生是很多人可能跟我一樣不熟悉其內部執行原理。

js是怎樣執行的呢?

精美樣式頁面是怎樣渲染到電腦螢幕的呢?

在開放的網際網路它又是怎樣保證我們個人資訊保安的呢?

帶著種種疑雲開始肝李兵老師的《瀏覽器基本原理與實踐》,不得不說,大家之作,通俗易懂,層層撥開雲霧見青天,下面就(非常非常)簡單總結一下。

Chrome 架構:僅僅打開了 1 個頁面,為什麼有 4 個程序

執行緒和程序區別

:多執行緒可以並行處理任務,執行緒不能單獨存在,它是由程序來啟動和管理的。一個程序是一個程式的執行例項。

執行緒和程序的關係

1、程序中任意一執行緒執行出錯,都會導致整個程序的崩潰。

2、執行緒之間共享程序中的資料。

3、當一個程序關閉後,作業系統會回收程序所佔用的記憶體。

4、程序之間的內容相互隔離。

單程序瀏覽器

1、不穩定。單程序中的外掛、渲染執行緒崩潰導致整個瀏覽器崩潰。

2、不流暢。指令碼(死迴圈)或外掛會使瀏覽器卡頓。

3、不安全。外掛和指令碼可以獲取到作業系統任意資源。

多程序瀏覽器

1、解決不穩定。程序相互隔離,一個頁面或者外掛崩潰時,影響僅僅時當前外掛或者頁面,不會影響到其他頁面。

2、解決不流暢。指令碼阻塞當前頁面渲染程序,不會影響到其他頁面。

3、解決不安全。採用多程序架構使用沙箱。沙箱看成時作業系統給程序上來一把鎖,沙箱的程式可以執行,但是不能在硬碟上寫入任何資料,也不能在敏感位置讀取任何資料。

多程序架構

:分為 瀏覽器程序、渲染程序、GPU 程序、網路程序、外掛程序。

缺點

1、資源佔用高。

2、體系架構複雜。

面向服務架構

:把原來的各種模組重構成獨立的服務,每個服務都可以在獨立的程序中執行,訪問服務必須使用定義好的介面,透過 IPC 通訊,使得系統更內聚、松耦合、易維護和拓展。

TCP 協議:如何保證頁面檔案能被完整送達瀏覽器

IP 頭是 IP 資料包開頭的資訊,包含 IP 版本、源 IP 地址、目標 IP 地址、生存時間等資訊;

UDP 頭中除了目的埠,還有源埠號等資訊;

IP 負責把資料包送達目的主機;

UDP 負責把資料包送達具體應用;

對於錯誤的資料包,UDP 不提供重發機制,只是丟棄當前的包,不能保證資料的可靠性,但是傳輸速度非常塊;

TCP 頭除了包含了目標埠和本機埠號外,還提供了用於排序的序列號,保證了資料完整地傳輸,它的連線可分為三個階段:建立連線、傳輸資料和斷開連線;

HTTP 請求流程:為什麼很多站點第二次開啟速度會很快

瀏覽器中的 HTTP 請求從發起到結束一共經歷如下八個階段:構建請求、查詢快取、準備 IP 和埠、等待 TCP 佇列、建立 TCP 連線、發起 HTTP 請求、伺服器處理請求、伺服器返回請求和斷開連線;

構建請求。瀏覽器構建請求行,構建好後,準備發起網路請求;

查詢快取。在真正發起請求前瀏覽器會查詢快取中是否有請求資源副本,有則攔截請求,返回資源副本,否則進入網路請求;

準備 IP 地址和埠。HTTP 網路請求需要和伺服器建立 TCP 連線,而建立 TCP 連線需要準備 IP 地址和埠號,瀏覽器需要請求 DNS 返回域名對應的 IP,同時會快取域名解析結果,供下次查詢使用;

等待 TCP 佇列。Chrome 機制,同一個域名同時最多隻能建立 6 個 TCP 連線;

建立 TCP 連線。TCP 透過“三次握手”建立連線,傳輸資料,“四次揮手”斷開連線;

傳送 HTTP 請求。建立 TCP 連線後,瀏覽器就可以和伺服器進行 HTTP 資料傳輸了,首先會向伺服器傳送請求行,然後以請求頭形式傳送一些其他資訊,如果是 POST 請求還會發送請求體;

伺服器處理請求。首先伺服器會返回響應行,隨後,伺服器向瀏覽器傳送響應頭和響應體。通常伺服器返回資料,就要關閉 TCP 連線,如果請求頭或者響應頭有 Connection:keep-alive TCP 保持開啟狀態;

導航流程:從輸入 URL 到頁面展示這中間發生了什麼

使用者輸入 URL 並回車;

瀏覽器程序檢查 URL,組裝協議,構成完整 URL;

瀏覽器程序透過程序通訊(IPC)把 URL 請求傳送給網路程序;

網路程序接收到 URL 請求後檢查本地快取是否快取了該請求資源,如果有則將該資源返回給瀏覽器程序;

如果沒有,網路程序向 web 伺服器發起 http 請求(網路請求),請求流程如下:

進行 DNS 解析,獲取伺服器 IP 地址,埠

利用 IP 地址和伺服器建立 tcp 連線構建請求頭資訊

傳送請求頭資訊

伺服器響應後,網路程序接收響應頭和響應資訊,並解析響應內容

網路程序解析響應流程:

檢查狀態碼,如果是 301/302,則需要重定向,從 Location 自動讀取地址,重新進行第 4 步,如果是 200,則繼續處理請求

200 響應處理:檢查響應型別 Content-Type,如果是位元組流型別,則將該請求提交給下載管理器,該導航流程結束,不再進行後續渲染。如果是 html 則通知瀏覽器程序準備渲染程序進行渲染

準備渲染程序:

瀏覽器程序檢查當前 URL 是否和之前開啟的渲染程序根域名是否相同,如果相同,則複用原來的程序,如果不同,則開啟新的渲染程序

傳輸資料、更新狀態:

渲染程序準備好後,瀏覽器向渲染程序發起“提交文件”的訊息,渲染程序接收到訊息和網路程序建立傳輸資料的“管道”

渲染程序接收完資料後,向瀏覽器傳送“確認提交”

瀏覽器程序接收到確認訊息後 engine 瀏覽器介面狀態:安全、地址 URL、前進後退的歷史狀態、更新 web 頁面

渲染流程(上):HTML、CSS 和 JavaScript 是如何變成頁面的

瀏覽器不能直接理解 HTML 資料,需要將其轉化為 DOM 樹結構;

生成 DOM 樹後,根據 CSS 樣式表,計算出 DOM 樹所有節點樣式;

建立佈局樹:遍歷 DOM 樹所有可見節點,把這些節點加到佈局中,不可見節點忽略,如 head 標籤下所有內容,display: none 元素;

渲染流程(下):HTML、CSS 和 JavaScript 是如何變成頁面的

分層:層疊上下文屬性的元素(比如定位屬性元素、透明屬性元素、CSS 濾鏡屬性元素)提升為單獨的一層,需要裁剪的地方(比如出現捲軸)也會被建立為圖層;

圖層繪製:完成圖層樹構建後,渲染引擎會對圖層樹每一層進行繪製,把一個圖層拆分成小的繪製指令,再把指令按照順序組成一個帶繪製列表;

有些情況圖層很大,一次繪製所有圖層內容,開銷太大,合成執行緒會將圖層劃分為圖塊(256x256 或者 512x512);

合成執行緒將圖塊提交給柵格執行緒進行柵格化,將圖塊轉換為點陣圖。柵格化過程都會使用 GPU 加速,生成的點陣圖儲存週期 GPU 記憶體中;

一旦所有圖塊都被柵格化,合成執行緒會生成一個繪製圖塊命令(DrawQuad),然會將命令提交給瀏覽器程序,viz 元件接收到該指令,將頁面內容繪製到記憶體中,顯示在螢幕上;

重排:透過 JavaScript 或者 CSS 修改元素幾何位置屬性,會觸發重新佈局,解析後面一系列子階段;重繪:不引起佈局變換,直接進入繪製及其以後子階段;合成:跳過佈局和繪製階段,執行的後續操作,發生在合成執行緒,非主執行緒;

變數提升:javascript 程式碼是按順序執行的嗎

JavaScript 程式碼在執行之前需要先編譯,在編譯階段,變數和函式會被存放到變數環境中,變數預設值會被設定為 undefined;

在程式碼執行階段,JavaScript 引擎會從變數環境中查詢自定義的變數和函式;

如果在編譯階段,竄愛兩個相同的函式,那麼最終放在變數環境中的是最後定義的那個,後定義的覆蓋先定義的;

呼叫棧:為什麼 JavaScript 程式碼會出現棧溢位

每呼叫一個函式,JavaScript 引擎會為其建立執行上下文壓入呼叫棧,然後,JavaScript 引擎開始執行函式程式碼;

如果一個函式 A 呼叫另外一個函式 B,那麼 JavaScript 引擎會為 B 函式建立執行上下文,並將 B 函式的執行上下文壓入棧頂;

當前函式執行完畢後,JavaScript 引擎會將該函式的執行上下文彈出棧;

當分配的呼叫棧空間被佔滿時,會引發“堆疊溢位”問題;

塊級作用域:var 缺陷以及為什麼要引入 let 和 const

let、const 申明的變數不會被提升。在 javascript 引擎編譯後,會儲存在詞法環境中;

塊級作用域在程式碼執行時,將 let、const 變數存放在詞法環境的一個單獨的區域。詞法環境內部維護一個小型的棧結構,作用域內部變數壓入棧頂。作用域執行完,從棧頂彈出;

作用域鏈和閉包:程式碼中出現相同的變數,JavaScript 引擎如何選擇

使用一個變數,JavaScript 引擎會在當前的執行上下文中查詢變數,如果沒有找到,會繼續在 outer(執行環境指向外部執行上下文的引用)所指向的執行上下文中查詢;

JavaScript 執行過程,作用域鏈是由詞法作用域決定,而詞法作用域是由程式碼中函式宣告的位置決定;

根據詞法作用域的規則,內部函式總是可以訪問其外部函式中宣告的變數,當透過呼叫一個外部函式返回一個內部函式後,即使外部函式已經執行結束了,但是內部函式引用外部函式的變數依舊儲存在記憶體中,把這些變數的集合稱為閉包;

this:從 JavaScript 執行上下文視角講 this

當執行 new CreateObj 的時候,JavaScript 引擎做了四件事:

首先建立一個控物件 tempObj;

接著呼叫 CreateObj。call 方法,並將 tempObj 作為 call 方法的引數,這樣當 createObj 的執行上下文建立時,它的 this 就指向 tempObj 物件;

然後執行 CreateObj 函式,此時的 CreateObj 函式執行上下文中的 this 指向 tempObj 物件;

最後返回 tempObj 物件。

this 的使用分為:

當函式最為物件的方法呼叫時,函式中的 this 就是該物件;

當函式被正常呼叫時,在嚴格模式下,this 值是 undefined,非嚴格模式下 this 指向的是全域性物件 window;

巢狀函式中的 this 不會繼承外層函式的 this 值;

箭頭函式沒有自己的執行上下文,this 是外層函式的 this。

棧空間和堆空間:資料是如何儲存的

動態語言

:在使用時需要檢查資料型別的語言。

弱型別語言

:支援隱式轉換的語言。

JavaScript 中的 8 種資料型別,它們可以分為兩大類——

原始型別和引用型別。

原始型別資料存放在棧中,引用型別資料存放在堆中。堆中的資料是透過引用與變數關係聯絡起來的。

從記憶體視角瞭解閉包

:詞法掃描內部函式,引用了外部函式變數,堆空間建立一個“closure”物件,儲存變數。

垃圾回收:垃圾資料如何自動回收

棧中資料回收:執行狀態指標 ESP 在執行棧中移動,移過某執行上下文,就會被銷燬;

堆中資料回收:V8 引擎採用標記-清除演算法;

V8 把堆分為兩個區域——新生代和老生代,分別使用副、主垃圾回收器;

副垃圾回收器負責新生代垃圾回收,小物件(1 ~ 8M)會被分配到該區域處理;

新生代採用 scavenge 演算法處理:將新生代空間分為兩半,一半空閒,一半存物件,對物件區域做標記,存活物件複製排列到空閒區域,沒有記憶體碎片,完成後,清理物件區域,角色反轉;

新生代區域兩次垃圾回收還存活的物件晉升至老生代區域;

主垃圾回收器負責老生區垃圾回收,大物件,存活時間長;

新生代區域採用標記-清除演算法回收垃圾:從根元素開始,遞迴,可到達的元素活動元素,否則是垃圾資料;

為了不造成卡頓,標記過程被切分為一個個子標記,交替進行。

編譯器和解析器:V8 如何執行一段 JavaScript 程式碼的

計算機語言可以分為兩種:編譯型和解釋型語言。編譯型語言經過編譯器編譯後保留機器能讀懂的二進位制檔案,比如 C/C++,go 語言。解釋型語言是在程式執行時透過直譯器對程式進行動態解釋和執行,比如 Python,JavaScript 語言。

編譯型語言的編譯過程:編譯器首先將程式碼進行詞法分析、語法分析,生成抽象語法樹(AST),然後最佳化程式碼,最後生成處理器能夠理解的機器碼;

解釋型語言解釋過程:直譯器會對程式碼進行詞法分析、語法分析,並生產抽象語法樹(AST),不過它會再基於抽象語法樹生成位元組碼,最後根據位元組碼執行程式;

AST 的生成:第一階段是分詞(詞法分析),將一行行原始碼拆解成一個個 token(語法上不可再分、最小單個字元)。第二階段是解析(語法分析),將上一步生成的 token 資料,根據語法規則轉為 AST,這一階段會檢查語法錯誤;

位元組碼存在的意義:直接將 AST 轉化為機器碼,執行效率是非常高,但是消耗大量記憶體,從而先轉化為位元組碼解決記憶體問題;

直譯器 ignition 在解釋執行位元組碼,同時會手機程式碼資訊,發現某一部分程式碼是熱點程式碼(HotSpot),編譯器把熱點的位元組碼轉化為機器碼,並儲存起來,下次使用;

位元組碼配合直譯器和編譯器的計數實現稱為即時編譯(JIT)。

訊息佇列和事件迴圈:頁面是怎麼活起來的

每個渲染程序都有一個主執行緒,主執行緒會處理 DOM,計算樣式,處理佈局,JavaScript 任務以及各種輸入事件;

維護一個訊息佇列,新任務(比如 IO 執行緒)新增到訊息佇列尾部,主執行緒迴圈地從訊息佇列頭部讀取任務,執行任務;

解決處理優先順序高的任務:訊息佇列的中的任務稱為宏任務,每個宏任務中都會包含一個微任務佇列,在執行宏任務的過程中,如果 DOM 有變化,將該變化新增到微任務佇列中;

解決單個任務執行時長過久:JavaScript 透過回撥功能來規避。

webapi:setTimeout 是怎麼實現的

JavaScript 呼叫 setTimeout 設定回撥函式的時候,渲染程序會建立一個回撥任務,延時執行佇列存放定時器任務;

當定時器任務到期,就會從延時佇列中取出並執行;

如果當前任務執行時間過久,會影響延時到期定時器任務的執行;

如果 setTimeout 存在巢狀呼叫(5 次以上),判斷該函式方法被阻塞,那麼系統會設定最短時間間隔為 4 秒;

未啟用的頁面,setTimeout 執行最小間隔是 1000 毫秒,目的是為了降低載入損耗;

延時執行時間最大值是 24。8 天,因為延時值是以 32 個 bit 儲存的;

setTimeout 設定的回撥函式中的 this 指向全域性 window。

webpai:XMLHttpRequest 是怎麼實現的

XMLHttpRequest onreadystatechange 處理流程:未初始化 -> OPENED -> HEADERS_RECEIVED -> LOADING -> DONE;

渲染程序會將請求傳送給網路程序,然後網路程序負責資源下載,等網路程序接收到資料後,利用 IPC 通知渲染程序;

渲染程序接收到訊息之後,會將 xhr 回撥函式封裝成任務並新增到訊息佇列中,等主執行緒迴圈系統執行到該任務的時候,會根據相關狀態來呼叫回撥函式。

宏任務和微任務:不是所有的任務都是一個待遇

訊息佇列中的任務為宏任務。渲染程序內部會維護多個訊息佇列,比如延時執行佇列和普通訊息佇列,主執行緒採用 for 迴圈,不斷地從這些任務佇列中取出任務並執行;

微任務是一個需要非同步執行的函式,執行時機是在主函式執行結束之後、當前宏任務結束之前;

V8 在執行 javascript 指令碼時,會為其建立一個全域性執行上下文,同時會建立一個微任務佇列;

執行微任務過程中產生的微任務不會推遲到下個宏任務中執行,而是在當前宏任務中繼續執行;

使用 Promise 告別回撥函式

使用 Promise 解決了回撥地獄問題,消滅巢狀和多次處理;

模擬實現 Promise

async await 使用同步方式寫非同步程式碼

生成器函式是一個帶星號函式,而且是可以暫停執行和回覆執行的;

生成器函式內部執行一段程式碼,遇到 yield 關鍵字,javascript 引擎返回關鍵字後面的內容給外部,並且暫停該函式的執行;

外部函式可以同步 next 方法恢復函式的執行;

協程是一種比執行緒更加輕量級的存在,協程可以看成是跑線上程上的任務,一個執行緒可以存在多個協程,但是同時只能執行一個協程,如果 A 協程啟動 B 協程,A 為 B 的父協程;

協程不被操作協同核心所管理,而完全由程式所控制,這樣效能提升;

await xxx 會建立一個 Promise 物件,將 xxx 任務提交給微任務佇列;

暫停當前協程的執行,將主執行緒的控制權力轉交給父協程執行,同時將 Promise 物件返回給父協程,繼續執行父協程;

父協程執行結束之前會檢查微任務佇列,微任務佇列中有 resolve(xxx) 等待執行,觸發 then 的回撥函式;

回撥函式被啟用後,會將主執行緒的控制權交給協程,繼續執行後續語句,完成後將控制權還給父協程。

頁面效能分析:利用 chrome 做 web 效能分析

Chrome 開發者工具(簡稱 DevTools)是一組網頁製作和除錯的工具,內嵌於 Google Chrome 瀏覽器中。它一共包含了 10 個功能面板,包括了 Elements、Console、Sources、NetWork、Performance、Memory、Application、Security、Audits 和 Layers。

DOM 樹:JavaScript 是如何影響 DOM 樹構建的

HTML 解析器(HTMLParse)負責將 HTML 位元組流轉換為 DOM 結構;

HTML 解析器並不是等整個文件載入完成之後再解析,而是網路程序載入流多少資料,便解析多少資料;

位元組流轉換成 DOM 三個階段:1、位元組流轉換為 Token;2、維護一個 Token 棧,遇到 StartTag Token 入棧,遇到 EndTag Token 出棧;3、為每個 Token 建立一個 DOM 節點;

JavaScript 檔案和 CSS 樣式表文件都會阻塞 DOM 解析;

渲染流水線:CSS 如何影響首次載入時的白屏時間?

DOM 構建結束之後,css 檔案還未下載完成,渲染流水線空閒,因為下一步是合成佈局樹,合成佈局樹需要 CSSOM 和 DOM,這裡需要等待 CSS 載入結束並解析成 CSSOM;

CSSOM 兩個作用:提供給 JavaScript 操作樣式表能力,為佈局樹的合成提供基礎樣式資訊;

在執行 JavaScript 指令碼之前,如果頁面中包含了外部 CSS 檔案的引用,或者透過 style 標籤內建了 CSS 內容,那麼渲染引擎還需要將這些內容轉化為 CSSOM,因為 JavaScript 有修改 CSSOM 的能力,所以在執行 JavaScript 之前,還需要依賴 CSSOM。也就是說 CSS 在部分情況下也會阻塞 DOM 的生成。

分層和合成機制:為什麼 CSS 動畫比 JavaScript 高效

顯示器固定重新整理頻率是 60HZ,即每秒更新 60 張圖片,圖片來自顯示卡的前緩衝區;

顯示卡的職責是合成新的影象,儲存在後緩衝區,然後後緩衝區和前緩衝區互換,顯示卡更新頻率和顯示前重新整理頻率不一致,就會造成視覺上的卡頓;

渲染流水線生成的每一副圖片稱為一幀,生成一幀的方式有重排、重繪和合成三種;

重排會根據 CSSOM 和 DOM 計算佈局樹,重繪沒有重新佈局階段;

生成佈局樹之後,渲染引擎根據佈局樹特點轉化為層樹,每一層解析出繪製列表;

柵格執行緒根據繪製列表中的指令生成圖片,每一層對應一張圖片,合成執行緒將這些圖片合成一張圖片,傳送到後快取區;

合成執行緒會將每個圖層分割成大小固定的圖塊,優先繪製靠近視口的圖塊;

頁面效能:如何系統最佳化頁面

載入階段:減少關鍵資源個數,降低關鍵資源大小,降低關鍵資源的 RTT 次數;

互動階段:減少 JavaScript 指令碼執行時間,避免強制同步佈局:操作 DOM 的同時獲取佈局樣式會引發,避免佈局抖動:多次執行強制佈局和抖動,合理利用 CSS 合成動畫:標記 will-change,避免頻繁的垃圾回收;

CSS 實現一些變形、漸變、動畫等特效,這是由 CSS 觸發的,並且是在合成執行緒中執行,這個過程稱為合成,它不會觸發重排或者重繪;

虛擬 DOM:虛擬 DOM 和真實 DOM 有何不同

當有資料更新時, React 會生產一個新的虛擬 DOM,然會拿新的虛擬 DOM 和之前的虛擬 DOM 進行比較,這個過程找出變化的節點,然後將變化的節點應用到 DOM 上;

最開始的時候,比較兩個 DOM 的過程是在一個遞迴函數里執行的,其核心演算法是 reconciliation。通常情況,這個比較過程執行很快,不過虛擬 DOM 比較複雜時,執行比較函式可能佔據主執行緒比較久的時間,這樣會導致其他任務的等待,造成頁面卡頓。React 團隊重寫了 reconciliation 演算法,稱為 Fiber reconciler,之前老的演算法稱為 Stack reconciler;

PWA:解決 web 應用哪些問題

PWA(Progressive Web App),漸進式 Web 應用。一個漸進式過渡方案,讓普通站點過渡到 Web 應用,降低站點改造代價,逐漸支援新技術,而不是一步到位;

PWA 引入 ServiceWorker 來試著解決離線儲存和訊息推送問題,引入 mainfest。json 來解決一級入口問題;

暗轉了 ServiceWorker 模組之後,WebApp 請求資源時,會先透過 ServiceWorker,讓它判斷是返回 Serviceworker 快取的資源還是重新去網路請求資源,一切的控制權交給 ServiceWorker 來處理;

在目前的 Chrome 架構中,Service Worker 是執行在瀏覽器程序中的,因為瀏覽器程序生命週期是最長的,所以在瀏覽器的生命週期內,能夠為所有的頁面提供服務;

WebComponent:像搭積木一樣構建 web 應用

CSS 的全域性屬性會阻礙元件化,DOM 也是阻礙元件化的一個因素,因為頁面中只有一個 DOM,任何地方都可以直接讀取和修改 DOM;

WebComponent 提供了對區域性試圖封裝能力,可以讓 DOM、CSSOM 和 JavaScript 執行在區域性環境中;

template 建立模版,查詢模版內容,建立影子 DOM,模版新增到影子 DOM 上;

影子 DOM 可以隔離全域性 CSS 和 DOM,但是 JavaScript 是不會被隔離的;

HTTP1:HTTP1 效能最佳化

HTTP/0。9 基於 TCP 協議,三次握手建立連線,傳送一個 GET 請求行(沒有請求頭和請求體),伺服器接收請求之後,讀取對應 HTML 檔案,資料以 ASCII 字元流返回,傳輸完成斷開連線;

HTTP/1。0 增加請求頭和響應頭來進行協商,在發起請求時透過請求頭告訴伺服器它期待返回什麼型別問題、什麼形式壓縮、什麼語言以及檔案編碼。引入來狀態嗎,Cache 機制等;

HTTP/1。1 改進持久化連線,解決建立 TCP 連線、傳輸資料和斷開連線帶來的大量開銷,支援在一個 TCP 連線上可以傳輸多個 HTTP 請求,目前瀏覽器對於一個域名同時允許建立 6 個 TCP 持久連線;

HTTP/1。1 引入 Chunk transfer 支援動態生成內容:伺服器將資料分割成若干任意大小的資料塊,每個資料塊傳送時附上上個數據塊的長度,最後使用一個零長度的塊作為傳送資料完成的標誌。在 HTTP/1。1 需要在響應頭中設定完整的資料大小,如 Content-Length。

HTTP2:如何提升網路速度

HTTP/1。1 主要問題:TCP 慢啟動;同時開啟多條 TCP 連線,會競爭固定寬頻;對頭阻塞問題;

HTTP/2 在一個域名下只使用一個 TCP 長連線和消除對頭阻塞問題;

多路複用的實現:HTTP/2 添加了二進位制分幀層,將傳送或響應資料經過二進位制分幀處理,轉化為一個個帶有請求 ID 編號的幀,伺服器或者瀏覽器接收到響應幀後,根據相同 ID 幀合併為一條完整資訊;

設定請求優先順序:傳送請求可以設定請求優先順序,伺服器可以優先處理;

伺服器推送:請求一個 HTML 頁面,伺服器可以知道引用了哪些 JavaScript 和 CSS 檔案,附帶一起傳送給瀏覽器;

頭部壓縮:對請求頭和響應頭進行壓縮;

HTTP3:甩掉 TCP、TCL 包袱,構建高效網路

雖然 HTTP/2 解決了應用層面的對頭阻塞問題,不過和 HTTP/1。1 一樣,HTTP/2 依然是基於 TCP 協議,而 TCP 最初是為了單連線而設計;

TCP 可以看成是計算機之間的一個虛擬管道,資料從一端傳送到另一端會被拆分為一個個按照順序排列的資料包,如果在傳輸過程中,有一個數據因為網路故障或者其他原因丟失,那麼整個連線會處於暫停狀態,只有等到該資料重新傳輸;

由於 TCP 協議僵化,也不可能使用新的協議,HTTP/3 選擇了一個折衷的方法,基於現有的 UDP 協議,實現類似 TC 片多路複用,傳輸可靠等功能,稱為 QULC 協議;

QULC 實現類似 TCP 流量控制,傳輸可靠功能;整合 TLS 加密功能;實現多路複用功能;

同源策略:為什麼 XMLHttpRequst 不能跨域請求

協議、域名和埠號相同的 URL 是同源的;

同源策略會隔離不同源的 DOM、頁面資料和網路通訊;

頁面可以引用第三方資源,不過暴露出諸如 XSS 問題,引入內容安全策略 CSP 限制;

預設 XMLHttpRequest 和 Fetch 不能跨站請求資源,引入跨域資源共享(CORS)進行跨域訪問控制;

跨站指令碼攻擊 XSS:為什麼 cookie 中有 httpOnly 屬性

XSS 跨站指令碼,往 HTML 檔案中注入惡意程式碼,對使用者實施攻擊;

XSS 攻擊主要有儲存型 XSS 攻擊、反射型 XSS 攻擊和 DOM 的 XSS 攻擊;

阻止 XSS 攻擊:伺服器對指令碼進行過濾或轉碼,利用 CSP 策略,使用 HttpOnly;

CSRF 攻擊:陌生連線不要隨便點

CSRF 跨站請求偽造,利用使用者的登入狀態,透過第三方站點攻擊;

避免 CSRF 攻擊:利用 SameSite(三種模式:Strict、Lax、None) 讓瀏覽器禁止第三方站點發起請求攜帶關鍵 Cookie;驗證請求的來源站點,請求頭中的 Referer 和 Origin 屬性;利用 CSRF Token;

沙盒:頁面和系統之間的隔離牆

瀏覽器被劃分為瀏覽器核心和渲染核心兩個核心模組,其中瀏覽器核心石油網路程序、瀏覽器主程序和 GPU 程序組成的,渲染核心就是渲染程序;

瀏覽器中的安全沙箱是利用作業系統提供的安全技術,讓渲染程序在執行過程中無法訪問或者修改作業系統中的資料,在渲染程序需要訪問系統資源的時候,需要透過瀏覽器核心來實現,然後將訪問的結果透過 IPC 轉發給渲染程序;

站點隔離(Site Isolation)將同一站點(包含相同根域名和相同協議的地址)中相互關聯的頁面放到同一個渲染程序中執行;

實現站點隔離,就可以將惡意的 iframe 隔離在惡意程序內部,使得它無法繼續訪問其他 iframe 程序的內容,因此無法攻擊其他站點;

HTTPS:讓資料傳輸更安全

在 TCP 和 HTTP 之間插入一個安全層,所有經過安全層的資料都會被加密或者解密;

對稱加密:瀏覽器傳送加密套件列表和一個隨機數 client-random,伺服器會從加密套件中選取一個加密套件,然後生成一個隨機數 service-random,返回給瀏覽器。這樣瀏覽器和伺服器都有相同 client-random 和 service-random,再用相同的方法將兩者混合生成一個金鑰 master secret,雙方就可以進行資料加密傳輸了;

對稱加密缺點:client-random 和 service-random 的過程都是明文,駭客可以拿到協商的加密套件和雙方隨機數,生成金鑰,資料可以被破解;

非對稱加密:瀏覽器傳送加密套件列表給伺服器,伺服器選擇一個加密套件,返回加密套件和公鑰,瀏覽器用公鑰加密資料,伺服器用私鑰解密;

非對稱加密缺點:加密效率太低,不能保證伺服器傳送給瀏覽器的資料安全,駭客可以獲取公鑰;

對稱加密結合非對稱加密:瀏覽器傳送對稱加密套件列表、非對稱加密列表和隨機數 client-random 給伺服器,伺服器生成隨機數 service-random,選擇加密套件和公鑰返回給瀏覽器,瀏覽器利用 client-random 和 service-random 計算出 pre-master,然後利用公鑰給 pre-master 加密,向伺服器傳送加密後的資料,伺服器用私鑰解密出 pre-master 資料,結合 client-random 和 service-random 生成對稱金鑰,使用對稱金鑰傳輸加密資料;

引入數字證書是為了證明“我就是我”,防止 DNS 被劫持,偽造伺服器;

證書的作用:一個是向瀏覽器證明伺服器的身份,另一個是包含伺服器公鑰;

數字簽名過程:CA 使用 Hash 函式技術明文資訊,得出資訊摘要,然後 CA 使用私鑰對資訊摘要進行加密,加密後的秘文就是數字簽名;

驗證數字簽名:讀取證書明文資訊,使用相同 Hash 函式計算得到資訊摘要 A,再利用 CA 的公鑰解密得到 B,對比 A 和 B,如果一致,則確認證書合法;