前端計網面試題學習總結(持續更新)
一.從輸入URL到頁面展示,這中間發生了什麼
首先來看一下“從輸入URL到頁面展示完整流程的示意圖”
1.1 瀏覽器展示頁面程序介紹
該圖是瀏覽器多程序配合從而完成瀏覽器的頁面跳轉、渲染,主要有三個瀏覽器程序相互配合。
- 瀏覽器程序:主要負責使用者互動、子程序管理和檔案儲存等功能,就相當於瀏覽器的大管家。
- 網路程序:為渲染程序和瀏覽器程序等提供網路下載功能。
- 渲染程序:主要是將網路上下載的HTML、JavaScript、CSS、圖片等資源解析為網頁顯示和互動的頁面。因為渲染程序是有讀寫硬碟的許可權,為了防止網上下載的惡意程式碼對系統進行破壞,Chrome會讓渲染程序執行在安全沙箱裡,保證系統的安全。
1.2 渲染程序渲染頁面流程
再來看一下其中“頁面渲染流程圖”
渲染流程:
- 構建DOM樹:
將瀏覽器無法直接理解和使用的HTML
轉換為瀏覽器理解的DOM樹
-
樣式計算(Recalculate Sytle)
- 瀏覽器會先將
css
文字轉換成瀏覽器可以理解的結構styleSheets - 將央視表中的屬性值轉換為標準值,例如:將2em轉為32px,將red轉為rgb(255,0,0)
- 通過
DOM樹
將繼承的樣式以及自身樣式合併
- 瀏覽器會先將
-
佈局(Layout)
建立一個只含可見元素的
DOM
佈局樹 -
分層(Layer)
為了得到一些複雜的效果,需要對佈局樹進一步得到圖層樹,以下是會被單獨提升為一層的情況
- 擁有層疊上下文屬性的元素會被提升為單獨一層
- 需要裁剪的地方會被提升為單獨一層
-
圖層繪製(Paint)
渲染引擎會將各圖層的繪製分成一個個小的繪製指令進行繪製
-
柵格化(raster)
此時渲染程序中的主執行緒會將繪製列表提交給合成執行緒,合成執行緒會按視口(viewport)附近圖塊來優先生成點陣圖,這個過程就是柵格化來執行的。所謂的柵格就是指:將圖塊轉換為點陣圖。柵格化的過程回撥用GPU,所以也會呼叫GPU程序
-
最後的合成與顯示
如果所有的圖塊都被柵格化後,合成執行緒會生成一個繪製圖塊的命令--DrawQuad,並且將該命令提交給瀏覽器中的一個viz元件進行繪製
1.3 輸入URL到頁面展示總結
接下來就大致描述下這個過程:
-
首先,使用者輸入URL
-
瀏覽器檢查的是否為URL,如果是URL則根據規則,在這段內容加上協議,合為完整的URL
-
瀏覽器程序通過程序間通訊(IPC)將URL傳送給網路程序。
-
網路程序請求後檢查本地快取是否快取了該請求的資源,如果有則返回該資源給瀏覽器程序。
-
如果沒有就向伺服器傳送http請求,請求流程如下:
- 進行DNS解析,獲得請求域名的IP地址
- 利用IP地址建立TCP連線,如果是
HTTPS
則還會建立TLS
連線 - 構建併發送請求頭、請求體
- 接收響應頭和響應報文並解析響應內容
-
網路程序解析響應內容的流程
- 檢查響應內容中的狀態碼
- 如果是301/302 會重定向至其他URL,這時網路程序會讀取
Location
欄位裡讀區重定向地址,然後重新再來。例如:在當你使用HTTP
請求使用HTTPS
的網站時,會給你重定向至HTTPS
的網址 - 如果是2xx 的狀態碼,表示瀏覽器可以繼續處理該請求,處理時會處理響應頭會有一些欄位標誌響應體的內容的一些狀態。不同的
Content-Type
響應頭型別處理的流程不同。
- 如果是301/302 會重定向至其他URL,這時網路程序會讀取
- 檢查響應內容中的狀態碼
-
準備渲染程序
- 渲染程序會判斷當前的頁面是否與之前的已渲染的程序頁面是否屬於同一站點
- 如果不屬於同一站點,則開啟新的渲染程序
- 如果屬於同一站點,則複用之前的渲染程序
- 此時渲染程序準備好等待網路程序傳輸資料
- 渲染程序會判斷當前的頁面是否與之前的已渲染的程序頁面是否屬於同一站點
-
提交文件
- 當瀏覽器程序接收到網路程序的響應頭資料後,就向渲染程序發起
提交文件
- 渲染程序接收到提交文件資訊後,會和網路程序建立傳送資料的“管道”
- 等文件資料傳輸完成後,渲染程序會返回“確認提交”的訊息給瀏覽器程序
- 瀏覽器程序在收到“確認提交”的訊息後,會更新瀏覽器的介面狀態,包括安全狀態,位址列的URL,前進後退的歷史狀態,並更新Web頁面
- 當瀏覽器程序接收到網路程序的響應頭資料後,就向渲染程序發起
-
開始渲染
- 渲染程序會將
HTML
轉化成瀏覽器能讀懂的DOM樹 - 渲染引擎將
CSS
樣式錶轉化成瀏覽器可以理解的styleSheets,計算DOM節點的樣式 - 建立佈局樹
- 對佈局樹進行分層,生成分層樹
- 對每個圖層進行繪製列表,並提交給合成執行緒
- 合成執行緒會將圖層分為圖塊,並在光柵化執行緒池中將圖塊轉化為點陣圖
- 合成執行緒傳送繪製圖塊命令DrawQuad給瀏覽器程序
- 瀏覽器程序根據DrawQuad訊息生成頁面,並顯示在顯示器上
- 渲染程序會將
1.4 搬運文章
二. 跨域問題
2.1 什麼是跨域
2.1.1 什麼是同源策略
同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSRF等攻擊。所謂同源是指"協議+域名+埠"三者相同,即便兩個不同的域名指向同一個ip地址,也非同源。
同源策略限制的內容
- Cookie、LocalStorage、IndexedDB等儲存性內容
- DOM節點
- AJAX請求傳送後,結果被瀏覽器攔截了
但是有三個標籤是允許跨域載入資源的
<img src=XXX>
<link href=XXX>
<script src=XXX>
2.1.2 常見的跨域場景
當協議、子域名、主域名、埠號任意一個不相同時,都算作不同域。不同域之間請求資源就算作“跨域”。
注:跨域請求並不是請求發不出去,請求是可以傳送的,服務端也可以收到請求並正常返回,但由於瀏覽器的同源策略,響應結果被攔截了
2.2 跨域解決方案
2.2.1 JSONP
-
JSONP的原理:
利用
<script>
標籤沒有跨域限制的漏洞,網頁可以得到從其他來源動態產生的 JSON 資料。JSONP請求一定需要對方的伺服器做支援才可以。 -
JSONP的實現流程:
比如,有個a.html頁面,它裡面的程式碼需要利用ajax獲取一個不同域上的json資料,假設這個json資料地址是http://damonare.cn/data.php,那麼a.html中的程式碼就可以這樣:
<script type="text/javascript"> function dosomething(jsondata){ //處理獲得的json資料 } </script> <script src="http://example.com/data.php?callback=dosomething"></script>
我們看到獲取資料的地址後面還有一個callback引數,按慣例是用這個引數名,但是你用其他的也一樣。當然如果獲取資料的jsonp地址頁面不是你自己能控制的,就得按照提供資料的那一方的規定格式來操作了。
因為是當做一個js檔案來引入的,所以http://damonare.cn/data.php返回的必須是一個能執行的js檔案,所以這個頁面的php程式碼可能是這樣的(一定要和後端約定好哦):
<?php $callback = $_GET['callback'];//得到回撥函式名 $data = array('a','b','c');//要返回的資料 echo $callback.'('.json_encode($data).')';//輸出 ?>
最終,輸出結果為:dosomething(['a','b','c']);
如果你的頁面使用jquery,那麼通過它封裝的方法就能很方便的來進行jsonp操作了。
<script type="text/javascript"> $.getJSON('http://example.com/data.php?callback=?,function(jsondata)'){ //處理獲得的json資料 }); </script>
jquery會自動生成一個全域性函式來替換callback=?中的問號,之後獲取到資料後又會自動銷燬,實際上就是起一個臨時代理函式的作用。$.getJSON方法會自動判斷是否跨域,不跨域的話,就呼叫普通的ajax方法;跨域的話,則會以非同步載入js檔案的形式來呼叫jsonp的回撥函式。
-
JSONP的優缺點
-
JSONP的優點是:它不像XMLHttpRequest物件實現的Ajax請求那樣受到同源策略的限制;它的相容性更好,在更加古老的瀏覽器中都可以執行,不需要XMLHttpRequest或ActiveX的支援;並且在請求完畢後可以通過呼叫callback的方式回傳結果。
-
JSONP的缺點則是:它只支援GET請求而不支援POST等其它型別的HTTP請求;它只支援跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript呼叫的問題。
-
2.2.2 CORS跨域
-
CORS原理
CORS(Cross-Origin Resource Sharing)跨域資源共享,定義了必須在訪問跨域資源時,瀏覽器與伺服器應該如何溝通。CORS背後的基本思想就是使用自定義的HTTP頭部讓瀏覽器與伺服器進行溝通,從而決定請求或響應是應該成功還是失敗。目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。
-
兩種請求
-
簡單請求
只要同時滿足以下兩大條件,就屬於簡單請求
條件1:使用下列方法之一:
- GET
- HEAD
- POST
條件2:Content-Type 的值僅限於下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
請求中的任意 XMLHttpRequestUpload 物件均沒有註冊任何事件監聽器; XMLHttpRequestUpload 物件可以使用 XMLHttpRequest.upload 屬性訪問。
-
複雜請求
複雜請求是對伺服器有特殊要求的請求,比如請求方法是
PUT
、DELETE
,或者Content-Type
的欄位型別是application/json
。複雜請求的CORS請求,會在正式通訊之前,增加一次HTTP查詢請求,稱為"預檢"請求,該請求是
OPTION
方法的,通過該請求來知道服務端是否允許跨域請求。預檢請求的HTTP頭資訊:
OPTIONS /cors HTTP/1.1 Origin: http://api.bob.com Access-Control-Request-Method: PUT /瀏覽器CORS請求用到的HTTP方法 Access-Control-Request-Headers: X-Custom-Header /CORS額外發送的透資訊欄位 Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
服務端收到預檢請求以後,檢查完資訊允許跨源請求,作出迴應。
TTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://api.bob.com //表示該介面可以請求 Access-Control-Allow-Methods: GET, POST, PUT //返回支援的跨域請求方法 Access-Control-Allow-Headers: X-Custom-Header //支援所有頭資訊欄位,不限於瀏覽器“預檢”中請求的欄位 Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain
-
2.2.3Nginx反向代理
使用nginx反向代理實現跨域,是最簡單的跨域方式。只需要修改nginx的配置即可解決跨域問題,支援所有瀏覽器,支援session,不需要修改任何程式碼,並且不會影響伺服器效能。
實現思路:通過nginx配置一個代理伺服器(域名與domain1相同,埠不同)做跳板機,反向代理訪問domain2介面,並且可以順便修改cookie中domain資訊,方便當前域cookie寫入,實現跨域登入。
先下載nginx,然後將nginx目錄下的nginx.conf修改如下:
// proxy伺服器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie裡域名
index index.html index.htm;
# 當用webpack-dev-server等中介軟體代理介面訪問nignx時,此時無瀏覽器參與,故沒有同源限制,下面的跨域配置可不啟用
add_header Access-Control-Allow-Origin http://www.domain1.com; #當前端只跨域不帶cookie時,可為*
add_header Access-Control-Allow-Credentials true;
}
}
複製程式碼
最後通過命令列nginx -s reload
啟動nginx
// index.html
var xhr = new XMLHttpRequest();
// 前端開關:瀏覽器是否讀寫cookie
xhr.withCredentials = true;
// 訪問nginx中的代理伺服器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
複製程式碼
// server.js
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
var params = qs.parse(req.url.substring(2));
// 向前臺寫cookie
res.writeHead(200, {
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:指令碼無法讀取
});
res.write(JSON.stringify(params));
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
2.3 搬運文章
三.瀏覽器快取
3.1 什麼是瀏覽器快取
MDN解釋:
A browser cache holds all documents downloaded via HTTP by the user ... without requiring an additional trip to the server.
瀏覽器快取者使用者通過HTTP獲取的所有資源,在下一次請求時可以避免重複向伺服器傳送出多餘請求
3.2 快取分類
一般瀏覽器快取可以分為兩類:
- 強快取:不會向伺服器傳送請求,直接從快取中讀取資源
- 協商快取:協商快取是強制快取失效後,瀏覽器攜帶快取標識向伺服器發出請求,由瀏覽器根據快取標識決定是否使用快取的過程
瀏覽器在載入資源時,會先判斷是否命中強快取再驗證是否命中協商快取。
3.3強快取
強制快取是向瀏覽器快取查詢快取,根據快取規則決定是否使用快取結果的過程。強制快取有三種情況
- 未找到快取結果和標識,強制快取失效。直接向服務端傳送請求。
- 存在快取標識和結果,但是已經失效,強制快取失效。攜帶資源標識,發起協商快取。
- 存在快取結果和標識,且結果未失效,強制快取生效,返回結果。
那麼快取規則是如何的?
當瀏覽器向伺服器發起請求時,伺服器將快取規則放入HTTP響應報文的HTTP和請求結果一起返回給瀏覽器,控制強制快取的欄位時Expires
、Cache-Control
-
Expires
快取過期時間,用來指定資源到期的時間,是伺服器端的具體的時間點。也就是說,
Expires=max-age + 請求時間
,需要和Last-modified
結合使用。Expires
是Web伺服器響應訊息頭欄位,在響應http請求時告訴瀏覽器在過期時間前瀏覽器可以直接從瀏覽器快取取資料,而無需再次請求。Expires 是 HTTP/1 的產物,受限於本地時間,如果修改了本地時間,可能會造成快取失效。
-
Cache-Control
在HTTP/1.1中,Cache-Control是最重要的規則,主要用於控制網頁快取,主要取值為:
- public:所有內容都將被快取(客戶端和代理伺服器都可快取)
- private:所有內容只有客戶端可以快取,
Cache-Control
的預設取值 - no-cache:客戶端快取內容,但是是否使用快取則需要經過協商快取來驗證決定
- no-store:所有內容都不會被快取,即不使用強制快取,也不使用協商快取
- max-age=xxx (xxx is numeric):快取內容將在xxx秒後失效
需要注意的是,
no-cache
這個名字有一點誤導。設定了no-cache
之後,並不是說瀏覽器就不再快取資料,只是瀏覽器在使用快取資料時,需要先確認一下資料是否還跟伺服器保持一致,也就是協商快取。而no-store
才表示不會被快取,即不使用強制快取,也不使用協商快取
瀏覽器的快取存放在哪裡,如何在瀏覽器中判斷強制快取是否生效?這就是下面我們要講到的from disk cache
和from memory cache
。
Chrome的網路請求的Size會出現三種情況from disk cache(磁碟快取)
、from memory cache(記憶體快取)
、以及資源大小數值。
狀態 | 型別 | 說明 |
---|---|---|
200 | form memory cache | 不請求網路資源,資源在記憶體當中,一般指令碼、字型、圖片會存在記憶體當中 |
200 | form disk ceche | 不請求網路資源,在磁碟當中,一般非指令碼會存在記憶體當中,如css等 |
200 | 資源大小數值 | 從伺服器下載最新資源 |
304 | 報文大小 | 請求服務端發現資源沒有更新,使用本地資源 |
簡單的對比一下
3.4協商快取
協商快取就是瀏覽器存在快取但快取已失效,於是瀏覽器攜帶快取標識向伺服器發起請求,由伺服器根據快取標識決定是否使用快取。主要有兩種情況:
- 服務端返回
304
或者Not Modified
,協商快取生效
- 服務端返回
200
以及請求結果
,協商快取失效
那麼服務端是如何驗證資源是否更新呢
Last-Modified
和If-Modified-Since
驗證流程:
- 瀏覽器第一次請求資源的時候,服務端返回Header中會帶有一個
Last-Modified
欄位,表示資源最後修改時間。 - 當瀏覽器再次請求該資源的時候,請求頭中會帶有
If-Modified-Since(儲存Last-Modified的值)
欄位。服務端收到這個請求後,將If-Modified-Since
與當前的最後修改時間進行對比。如果相等則可以協商快取。不相等則將資源重新響應。
由於last-modified依賴的是儲存的絕對時間,還是會出現誤差的情況:
- 儲存的時間是以秒為單位的,1秒內多次修改是無法捕捉到的;
- 各機器讀取到的時間不一致,就有出現誤差的可能性。為了改善這個問題,提出了使用
etag
。
ETag
和If-None-Match
etag
是http
協議提供的若干機制中的一種Web
快取驗證機制,並且允許客戶端進行快取協商。生成etag常用的方法包括對資源內容使用抗碰撞雜湊函式,使用最近修改的時間戳的雜湊值,甚至只是一個版本號。 和last-modified
一樣.
- 瀏覽器會先發送一個請求得到
etag
的值,然後再下一次請求在request header
中帶上if-none-match
:[儲存的etag的值]
。 - 通過傳送的
etag
的值和服務端重新生成的etag
的值進行比對,如果一致代表資源沒有改變,服務端返回正文為空的響應,告訴瀏覽器從快取中讀取資源。
etag能夠解決last-modified的一些缺點,但是etag每次服務端生成都需要進行讀寫操作,而last-modified只需要讀取操作,從這方面來看,etag的消耗是更大的。
二者對比
- 精確度上:
Etag
要優於Last-Modified
。 - 優先順序上:伺服器校驗優先考慮
Etag
。 - 效能上:
Etag
要遜於Last-Modified
3.5 總結
當瀏覽器再次訪問一個已經訪問過的資源時,它會這樣做:
- 看看是否命中強快取,如果命中,就直接使用快取了;
- 如果沒有命中強快取,就發請求到伺服器檢查是否命中協商快取;
- 如果命中協商快取,伺服器會返回
304
告訴瀏覽器使用本地快取; - 否則,返回最新的資源。