從一道面試題,貫穿部分前端技能點

從輸入URL到頁面加載發生了什麼

基於網上的大量參考資料,梳理對這一加載流程的理解。深知本身的認知理解有限,請你們多多拍磚。javascript

這一過程的瀏覽器行爲表象( 步驟分解 ):css

  1. 輸入URL並回車 ( 經過DNS解析具體IP地址 )
  2. 瀏覽器發起請求 ( 瀏覽器經過TCP發送HTTP請求 )
  3. 服務器返回數據 ( 服務器處理請求並返回HTTP報文 )
  4. 瀏覽器渲染頁面

輸入URL並回車

瀏覽器主線程解析URL、經過服務器地址查找具體IP地址( DNS解析 )html

DNS的解析過程:前端

  • 第一步:瀏覽器將會檢查緩存中有沒有這個域名對應解析過的IP地址,若是有,該解析過程將會結束。瀏覽器緩存是有限制的(Chrome對每一個域名會默認緩存60s)
  • 第二步:若是用戶的瀏覽器中緩存中沒有,操做系統會先檢查本身本地的hosts文件是否有這個網址映射關係,若是有,就先調用這個IP地址映射,完成域名解析。(開發測試環境配置hosts的緣由)
  • 第三步:若是hosts裏沒有這個域名的映射,則查找本地DNS解析器緩存,是否有這個網址映射關係,若是有,直接返回,完成域名解析。
  • 第四步:若是hosts與本地DNS解析器緩存都沒有相應的映射關係,此時會訪問遞歸DNS服務器(區域 - 運營商 - 根服務),而後遞歸DNS服務器會遞歸查找域名記錄,返回結果

注:java

  1. 起初互聯網信息中心是總體管理一份hosts文件,因爲網絡規模不斷擴大不得不產生一個能夠有效管理主機名和ip地址之間對應關係的系統(NDS系統) - DNS就是互聯網中的分佈式數據庫
  2. 咱們根據dns解析原理去優化dns加載速度 預加載DNS<link rel="dns-prefetch" href="/img.alicdn.com">

瀏覽器怎麼發起請求?

瀏覽器是經過HTTP發起的請求,而HTTP屬於TCP/IP協議族的子級,TCP/IP協議族裏最重要的一點就是分層(五層)webpack

  1. 應用層:解析成IP地址併發送HTTP請求( NDS和HTTP屬於應用層 )
  2. 傳輸層:三次握手創建TCP鏈接( 爲應用層提供 - 靠譜的分片數據傳輸 )
  3. 網絡層:IP尋址( 爲傳輸層提供傳輸路線 )( 在與服務器之間經過多臺計算機和網絡設備進行傳輸時,網絡層起的做用就是在衆多的選項內選擇一條傳輸路線 )
  4. 鏈路層:網卡
  5. 物理層:物理傳輸 ( 硬件範疇比特流、雙絞線、電磁波 .. )

TCP/IP與OSI參考模型

什麼是TCP?

TCP負責創建鏈接、發送數據以及斷開鏈接。TCP提供將應用層發來的數據順利發送至服務端的可靠傳輸,爲了實現這一目的,須要在應用層數據前端附加一個頭TCP首部( 源端口號和目標端口號、序號、校驗 ),以後傳遞給IP層,IP層負責提供傳輸路線git

web開發者必須熟悉http。也就是先後端的http交互 ( http報文結構、緩存、跨域、cookie、安全...) 。 http是依託於TCP的,所以...github

TCP如何實現的可靠傳輸?( 三次握手 )

TCP協議採用了三次握手策略( 確保雙方均可以收到 )。TCP連接是全雙工的,雙方的數據讀寫能夠經過一個鏈接進行。web

三次握手

形象一點的描述:ajax

// 客戶端(毛蛋):二狗二狗,我是毛蛋,我是毛蛋,聽到請回答,聽到請回答!
// 服務端(二狗):二狗收到,二狗收到,毛蛋請講,毛蛋請講,完畢!
// 客戶端(毛蛋):毛蛋收到, ...
複製代碼

四次揮手

四次揮手:保證雙發數據都發送完畢,都認爲能夠斷開。若是客戶端發送關閉請求時,服務端沒有須要傳遞給客戶端的信息時,此時能夠合併爲一條數據發送會客戶端。

四次揮手

// 客戶端(第一次揮手):主動發起關閉請求(FIN報文段),客戶端此時進入FIN_WAIT_1狀態
// 服務端(第二次揮手):回覆客戶端(設置ACK報文段),服務端進入CLOSE_WAIT狀態,客戶端收到ACK報文後,進入FIN_WAIT_2狀態
// 服務端(第三次揮手):服務器此時會觀察本身是否還有數據沒有發送給客戶端,有,先發送數據再發送FIN報文,沒有,則直接發送FIN報文給客戶端,同時服務端進入LAST_ACK狀態
// 客戶端(第四次揮手):客戶端收到服務端的FIN報文段,向服務器發送ACK報文,而後客戶端進入TIME_WAIT狀態,服務端收到客戶端的ACK報文段後,就關閉鏈接;此時,客戶端等待2MSL後依然沒有收到服務端的回覆,則證實服務端已正常關閉,客戶端也能夠關閉這個鏈接了
複製代碼

注:

  1. 客戶端最後爲何須要等待態( TIME_WAIT )?傳遞的 FIN 報文段有可能會丟失
  2. 何時進行四次揮手:根據 Connection 請求頭,若是是 Keep-alive ,服務器就保持 TCP 連接,若是沒有 Keep-alive 或者主動 close ,則服務器 Response 傳輸完成後主動關閉 TCP 連接 ( http1.1 默認開啓 Keep-alive 的,在瀏覽器tab關閉時,TCP連接才關閉 )

服務器返回數據

瀏覽器經過(HTTP-TCP-IP-鏈路-物理),到達服務器,服務器在經過協議進行一步步(層鏈路-IP-TCP-HTTP)反向解析,拿到HTTP信息後 - 服務端進行處理( 反向代理、安全攔截、跨域驗證、業務邏輯... ),等待程序執行完畢後,再通過層層封裝發送給前端,完成交互

https://user-gold-cdn.xitu.io/2018/6/19/1641600a66b70789?w=890&h=1134&f=png&s=476328

各類HTTP頭

通用頭部

https://user-gold-cdn.xitu.io/2018/6/19/1641591cf216b3ed?w=782&h=133&f=png&s=35254

具備表明性的14個狀態碼

  • 200:該請求的被成功處理,請求的資源送回客戶端
  • 204:該請求的被成功處理,但響應的報文信息不含主體內容( 客戶端想服務端發送消息,而不須要返回新信息內容時使用 )
  • 206:範圍請求(Content-Range)
  • 301:永久重定向(京東老域名 www.360buy.com)
  • 302:臨時重定向( 短連接 )
  • 304:緩存( 下面有詳細說明 )
  • 307:臨時重定向(相似302) - 更加嚴謹(不會從POST變成GET)
  • 400:客戶端請求有誤( 請求報文存在錯誤語法,修改後從新發送 )
  • 401:請求未經受權
  • 403:禁止訪問( 未得到訪問權限、訪問權限出現問題... )
  • 404:資源未找到
  • 500:服務器內部錯誤( 執行時發生錯誤、Bug、某些臨時故障... )
  • 503:服務器不可用( 服務器處於超負載或停機維護狀態 )

經常使用的請求頭和響應頭

https://user-gold-cdn.xitu.io/2018/6/19/1641591cf27385fd?w=782&h=133&f=png&s=34829

https://user-gold-cdn.xitu.io/2018/6/19/1641591cf2ebfb3c?w=900&h=469&f=png&s=132070

https://user-gold-cdn.xitu.io/2018/6/19/1641591cf306f19d?w=1274&h=662&f=png&s=185543

緩存

兩種類型:強緩存(200 from cache)與協商緩存(304)

  1. 強緩存http1.1(Cache-Control/Max-Age)、http1.0(Pragma/Expires)
  2. 協商緩存http1.1(If-None-Match/E-tag)、http1.0(If-Modified-Since/Last-Modified) http1.1的方法老是優與http1.0,主流的方案緩存這四種方案老是會都加上 更好的方案是消滅304,除了主頁以外都是強緩存,須要更新時修改靜態資源的MD5戳便可 http資源緩存

瀏覽器渲染頁面

瀏覽器拿到HTML文檔,

  1. DOM解析(標記化和樹構建) - DOM樹
  2. CSSS解析 - CSSOM樹
  3. 將DOM樹和CSSOM樹合併刪除渲染樹(Render Tree) - 其實能夠把根html看作是一個層提高(就像總體的javascript自己就是一個宏任務同樣) - Paint
  4. 層疊上下文處理 - 造成Layer Tree:把渲染樹中的一些節點生成單獨的一層( 層提高 ) - Paint
  5. 層合併處理 - 造成Graphics Lyaer(GPU硬件加速) - GPU直接繪製
  • 附1:HTML沒法用常規的自上而下或者自下而上的進行解析,由於瀏覽器從來對一些無效的HTML用法採用包容態度,解析過程須要不斷循環往復。源內容在解析過程當中一般不會改變,可是在結構中JS每每會添加額外的標記(重排 || 迴流)。
  • 附2:標記化:詞法分析(起始標記、結束標記、屬性名、屬性值)。構建樹:標記生成後,傳遞給構件樹,標記一個構建一個,如此反覆
  • 附3:CSS解析與DOM解析同步進行(DOM解析結構,CSS解析樣式,二者並不衝突)
  • 附4:默認DOM和CSS加載與JS互斥(JS中可能會訪問咱們CSS中的樣式 && 結構)
  • 附5:生成Graphics Lyaer的元素(video、canvas、flash、CSS3D、perspective(透視)、opacity,transfrome應用了animation、transition時...)

說到渲染過程( 再說一說基本的優化手動 )

請求相關的優化手段

  1. 資源引入位置( 解析CSS會阻塞JS執行、解析JS會阻塞CSS解析和頁面渲染,css放在頭部,js放在底部 )
  2. 異步script標籤

不可避免的在頭部或者body中間加載JS文件, defer異步加載JS - 在DOM解析完後當即執行( 效果等同在body底部,主入口文件有一個便可 ),async異步加載JS - 在JS加載完後當即執行(建議獨立的代碼使用,如:baidu統計).

  1. 避免使用css@import

個人CSS代碼中歷來沒有怎麼寫過,形成額外的請求,若是真的想用,使用sass,會自動合併(引入佔位符不會形成代碼重複)

  1. 注意空的scr

a標籤空href,會重定向到當前頁面地址 Form給空method,會提交表單到當前頁面地址

  1. 咱們根據dns解析原理去優化dns加載速度 頁面加載優化<link rel="dns-prefetch" href="/img.alicdn.com">

DOM的相關的優化

  1. 緩存DOM
let CacheEl = {
  container: document.querySelectorAll('.container'),// 只有querySelectorAll返回的NodeList爲靜態的
  divCollection: document.getElementsByTagName('div),
  // HTMLCollection爲動態
  $Box: $('#box')
};
複製代碼
  1. 批量操做DOM
  1. 拼接完成後,再innerHTML更新DOM
  2. DocumentFragment對象實際上理論中能作到優化,可是
  3. 第二中方案比第一種方案時間要長,由於現代瀏覽器能優化的最終都會被瀏覽器優化
  1. DOM讀寫分離( 對性能影響很大 )

修改和訪問DOM分開批量進行(讀取DOM會觸發瀏覽器一次渲染)

  1. 事件代理( 減小內存佔用 )

其實DOM操做時代,最熟悉就是事件代理了(能動態捕獲添加的節點)

  1. 最小化全局影響( 編程思惟,減小內存 )

生命週期的概念:不用時清除定時器、不用時清除事件監聽器、建立最小做用域(GC)、清除對象引用

資源加載優化

  1. 首屏加載以前,縮短白屏時間使用瀏覽器緩存( 圖片、JS、CSS、字體、swf、音頻、ajax GET請求 )
  2. ajax預加載場景( 短時間內固定不變的數據:如 - 選項列表 )。GET請求能夠被緩存。若是是POST請求能夠配合localStorage作本地存儲。先從本地去,若是取到了就直接用,由於頁面在渲染的時候就已經請求過了。這樣能達到節省時間的目的。
  3. 動態請求資源:動態建立Image對象,或者script對象,設置src,將節點append到頁面(Image對象不須要),只要設置了src屬性,瀏覽器就會發出請求。最簡單的上報也是這麼搞
  4. 空閒時間,爲SPA的下一屏作預加載(在空閒時,悄悄的加載下一屏的內容)
  5. 預測用戶操做,預先加載數據(如:亞馬遜的二級菜單) - 技術要求較高
  6. 資源懶加載、圖片懶加載、JS按需加載(業務邏輯比較複雜的系統,點擊以後才觸發對應的加載 webpack)、滾動性能提高:函數節流

總結:

  1. DNS解析
  2. TCP三次握手四次揮手
  3. 各類HTTP頭信息
  4. 表明性的14種狀態碼
  5. 緩存
  6. 頁面渲染流程
  7. 基本優化方案( webpack + 三大框架 默認優化 )

細碎點不少,有機會再繼續 (⊙﹏⊙)b (⊙﹏⊙)b (⊙﹏⊙)b

  • 跨域、Cookie、CSS(BFC)、class、JS預處理、執行上下文、做用域、this、ES6...
  • web安全、https、http2.0 ...
  • Event Loop(瀏覽器Node)、Promise

一次dns緩存引起的慘案

BFC 神奇背後的原理

附:這篇博客 也許 想表達 恩,也許就是一篇博客 (⊙﹏⊙)b

相關文章
相關標籤/搜索