從輸入URL到頁面載入的過程
對知識體系進行一次預評級
看到這道題目,不借助搜尋引擎,自己的心裡是否有一個答案?
這裡,以目前的經驗(瞭解過一些處於不同階段的相關前端人員的情況),大概有以下幾種情況:(以下都是以點見面,實際上不同階段人員一般都會有其它的隱藏知識點的)
level1:
完全沒什麼概念的,支支吾吾的回答,一般就是這種水平(大致形象點描述):
瀏覽器發起請求,服務端返回資料,然後前端解析成網頁,執行指令碼。。。
這類人員一般都是:
萌新(剛接觸前端的,包括0-6個月都有可能有這種回答)
沉澱人員(就是那種可能已經接觸了前端幾年,但是仍然處於初級階段的那種。。。)
當然了,後者一般還會偶爾提下 http
、 後臺
、 瀏覽器渲染
, js引擎
等等關鍵字,但基本都是一詳細的問就不知道了。。。
level2:
已經有初步概念,但是可能沒有完整梳理過,導致無法形成一個完整的體系,或者是很多細節都不會展開,大概是這樣子的(可能符合若干條):
知道瀏覽器輸入url後會有http請求這個概念
有後臺這個概念,大致知道前後端的互動,知道前後端只要靠http報文通訊
知道瀏覽器接收到資料後會進行解析,有一定概念,但是具體流程不熟悉(如render樹構建流程,layout、paint,複合層與簡單層,常用優化方案等不是很熟悉)
對於js引擎的解析流程有一定概念,但是細節不熟悉(如具體的形參,函式,變數提升,執行上下文以及VO、AO、作用域鏈,回收機制等概念不是很熟悉)
如可能知道一些http規範初步概念,但是不熟悉(如http報文結構,常用頭部,快取機制,http2.0,https等特性,跨域與web安全等不是很熟悉)
到這裡,看到這上面一大堆的概念後,心裡應該也會有點底了。。。
實際上,大部分的前端人員可能都處於level2,但是,跳出這個階段並不容易,一般需要積累,不斷學習,才能水到渠成。
這類人員一般都是:
工作1-3年左右的普通人員(佔大多數,而且大多數人員工作3年左右並沒有實質上的提升)
工作3年以上的老人(這部分人大多都業務十分嫻熟,一個當好幾個用,但是,基礎比較薄弱,可能沒有嘗試寫過框架、元件、腳手架等)
大部分的初中級都陷在這個階段,如果要突破,不斷學習,積累,自然能水到渠成,打通任督二脈。
level3:
基本能到這一步的,不是高階就是接近高階,因為很多概念並不是靠背就能理解的,而要理解這麼多,需形成體系,一般都需要積累,非一日之功。
一般包括什麼樣的回答呢?(這裡就以自己的簡略回答進行舉例),一般這個階段的人員都會符合若干條(不一定全部,當然可能還有些是這裡遺漏的):
首先略去那些鍵盤輸入、和作業系統互動、以及螢幕顯示原理、網絡卡等硬體互動之類的(前端向中,很多硬體原理暫時略去。。。)
對瀏覽器模型有整體概念,知道瀏覽器是多程序的,瀏覽器核心是多執行緒的,清楚程序與執行緒之間得區別,以及輸入url後會開一個新的網路執行緒
對從開啟網路執行緒到發出一個完整的http請求中間的過程有所瞭解(如dns查詢,tcp/ip連結,五層因特爾協議棧等等,以及一些優化方案,如
dns-prefetch
)對從伺服器接收到請求到對應後臺接收到請求有一定了解(如負載均衡,安全攔截以及後臺程式碼處理等)
對後臺和前臺的http互動熟悉(包括http報文結構,場景頭部,cookie,跨域,web安全,http快取,http2.0,https等)
對瀏覽器接收到http資料包後的解析流程熟悉(包括解析html,詞法分析然後解析成dom樹、解析css生成css規則樹、合併成render樹,然後layout、painting渲染、裡面可能還包括複合圖層的合成、GPU繪製、外鏈處理、載入順序等)
對JS引擎解析過程熟悉(包括JS的解釋,預處理,執行上下文,VO,作用域鏈,this,回收機制等)
可以看到,上述包括了一大堆的概念,僅僅是偏前端向,而且沒有詳細展開,就已經如此之多的概念了,所以,個人認為如果沒有自己的見解,沒有形成自己的知識體系,僅僅是看看,背背是沒用的,過一段時間就會忘光了。
再說下一般這個階段的都可能是什麼樣的人吧。(不一定準確,這裡主要是靠少部分現實以及大部分推測得出)
工作2年以上的前端(基本上如果按正常進度的話,至少接觸前端兩年左右才會開始走向高階,當然,現在很多都是上學時就開始學了的,還有部分是天賦異稟,不好預估。。。)
或者是已經十分熟悉其它某門語言,再轉前端的人(基本上是很快就可以將前端水準提升上去)
一般符合這個條件的都會有各種隱藏屬性(如看過各大框架、元件的原始碼,寫過自己的元件、框架、腳手架,做過大型專案,整理過若干精品博文等)。
level4:
由於本人層次尚未達到,所以大致說下自己的見解吧。
一般這個層次,很多大佬都並不僅僅是某個技術棧了,而是成為了技術專家,技術leader之類的角色。所以僅僅是回答某個技術問題已經無法看出水準了,可能更多的要看架構,整體把控,大型工程構建能力等等
不過,對於某些執著於技術的大佬,大概會有一些回答吧(猜的):
從鍵盤談起到系統互動,從瀏覽器到CPU,從排程機制到系統核心,從資料請求到二進位制、彙編,從GPU繪圖到LCD顯示,然後再分析系統底層的程序、記憶體等等。
總之,從軟體到硬體,到材料,到分子,原子,量子,薛定諤的貓,人類起源,宇宙大爆炸,平行宇宙?感覺都毫無違和感。。。
這點可以參考下本題的原始出處:
http://fex.baidu.com/blog/2014/05/what-happen/
為什麼說知識體系如此重要?
為什麼說知識體系如此重要呢?這裡舉幾個例子。假設有被問到這樣一道題目(隨意想到的一個):
如何理解
getComputedStyle
在尚未梳理知識體系前,大概會這樣回答:
普通版本:
getComputedStyle
會獲取當前元素所有最終使用的CSS屬性值(最終計算後的結果),通過window.getComputedStyle
等價於document.defaultView.getComputedStyle
呼叫詳細版本:
window.getComputedStyle(elem,null).getPropertyValue("height")
可能的值為100px
,而且,就算是css上寫的是inherit
,getComputedStyle
也會把它最終計算出來的。不過注意,如果元素的背景色透明,那麼getComputedStyle
獲取出來的就是透明的這個背景(因為透明本身也是有效的),而不會是父節點的背景。所以它不一定是最終顯示的顏色。
就這個API來說,上述的回答已經比較全面了。
但是,其實它是可以繼續延伸的。
譬如現在會這樣回答:
getComputedStyle
會獲取當前元素所有最終使用的CSS屬性值,window.
和document.defaultView.
等價...getComputedStyle
會引起迴流,因為它需要獲取祖先節點的一些資訊進行計算(譬如寬高等),所以用的時候慎用,迴流會引起效能問題。然後合適的話會將話題引導迴流,重繪,瀏覽器渲染原理等等。當然也可以列舉一些其它會引發迴流的操作,如offsetXXX
,scrollXXX
,clientXXX
,currentStyle
等等
再舉一個例子:
visibility:hidden
和display:none
的區別
可以如下回答:
普通回答,一個隱藏,但佔據位置,一個隱藏,不佔據位置
進一步,
display
由於隱藏後不佔據位置,所以造成了dom樹的改變,會引發迴流,代價較大再進一步,當一個頁面某個元素經常需要切換
display
時如何優化,一般會用複合層優化,或者要求低一點用absolute
讓其脫離普通文件流也行。然後可以將話題引到普通文件流,absolute
文件流,複合圖層的區別,再進一步可以描述下瀏覽器渲染原理以及複合圖層和普通圖層的繪製區別(複合圖層單獨分配資源,獨立繪製,效能提升,但是不能過多,還有隱式合成等等)
上面這些大概就是知識系統化後的回答,會更全面,容易由淺入深,而且一有機會就可以往更底層挖
前端向知識的重點
此部分的內容是站在個人視角分析的,並不是說就一定是正確答案。
首先明確,計算機方面的知識是可以無窮無盡的挖的,而本文的重點是梳理前端向的重點知識。
對於前端向(這裡可能沒有提到 node.js
之類的,更多的是指客戶端前端),這裡將知識點按重要程度劃分成以下幾大類:
核心知識,必須掌握的,也是最基礎的,譬如瀏覽器模型,渲染原理,JS解析過程,JS執行機制等,作為骨架來承載知識體系
重點知識,往往每一塊都是一個知識點,而且這些知識點都很重要,譬如http相關,web安全相關,跨域處理等
拓展知識,這一塊可能更多的是瞭解,稍微實踐過,但是認識上可能沒有上面那麼深刻,譬如五層因特爾協議棧,hybrid模式,移動原生開發,後臺相關等等(當然,在不同領域,可能有某些知識就上升到重點知識層次了,譬如hybrid開發時,懂原生開發是很重要的)
為什麼要按上面這種方式劃分?這大概與個人的技術成長有關。
記得最開始學前端知識時,是一點一點的積累,一個知識點一個知識點的攻克。
就這樣,雖然在很長一段時間內積累了不少的知識,但是,總是無法將它串聯到一起。每次梳理時都是很分散的,無法保持思路連貫性。
直到後來,在將瀏覽器渲染原理、JS執行機制、JS引擎解析流程梳理一遍後,感覺就跟打通了任督二脈一樣,有了一個整體的架構,以前的知識點都連貫起來了。
梳理出了一個知識體系,以後就算再學新的知識,也會盡量往這個體系上靠攏,環環相扣,更容易理解,也更不容易遺忘。
梳理主幹流程
回到這道題上,如何回答呢?先梳理一個骨架。
知識體系中,最重要的是骨架,脈絡。有了骨架後,才方便填充細節。所以,先梳理下主幹流程:
從瀏覽器接收url到開啟網路請求執行緒(這一部分可以展開瀏覽器的機制以及程序與執行緒之間的關係)
開啟網路執行緒到發出一個完整的http請求(這一部分涉及到dns查詢,tcp/ip請求,五層因特網協議棧等知識)
從伺服器接收到請求到對應後臺接收到請求(這一部分可能涉及到負載均衡,安全攔截以及後臺內部的處理等等)
後臺和前臺的http互動(這一部分包括http頭部、響應碼、報文結構、cookie等知識,可以提下靜態資源的cookie優化,以及編碼解碼,如gzip壓縮等)
單獨拎出來的快取問題,http的快取(這部分包括http快取頭部,etag,catch-control等)
瀏覽器接收到http資料包後的解析流程(解析html-詞法分析然後解析成dom樹、解析css生成css規則樹、合併成render樹,然後layout、painting渲染、複合圖層的合成、GPU繪製、外鏈資源的處理、loaded和domcontentloaded等)
CSS的視覺化格式模型(元素的渲染規則,如包含塊,控制框,BFC,IFC等概念)
JS引擎解析過程(JS的解釋階段,預處理階段,執行階段生成執行上下文,VO,作用域鏈、回收機制等等)
其它(可以拓展不同的知識模組,如跨域,web安全,hybrid模式等等內容)
梳理出主幹骨架,然後就需要往骨架上填充細節內容
從瀏覽器接收url到開啟網路請求執行緒
這一部分展開的內容是:瀏覽器程序/執行緒模型,JS的執行機制。
多程序的瀏覽器
瀏覽器是多程序的,有一個主控程序,以及每一個tab頁面都會新開一個程序(某些情況下多個tab會合並進程)。
程序可能包括主控程序,外掛程序,GPU,tab頁(瀏覽器核心)等等。
Browser程序:瀏覽器的主程序(負責協調、主控),只有一個
第三方外掛程序:每種型別的外掛對應一個程序,僅當使用該外掛時才建立
GPU程序:最多一個,用於3D繪製
瀏覽器渲染程序(核心):預設每個Tab頁面一個程序,互不影響,控制頁面渲染,指令碼執行,事件處理等(有時候會優化,如多個空白tab會合併成一個程序)
如下圖:
多執行緒的瀏覽器核心
每一個tab頁面可以看作是瀏覽器核心程序,然後這個程序是多執行緒的,它有幾大類子執行緒:
GUI執行緒
JS引擎執行緒
事件觸發執行緒
定時器執行緒
網路請求執行緒
可以看到,裡面的JS引擎是核心程序中的一個執行緒,這也是為什麼常說JS引擎是單執行緒的。
解析URL
輸入URL後,會進行解析(URL的本質就是統一資源定位符)
URL一般包括幾大部分:
protocol
,協議頭,譬如有http,ftp等host
,主機域名或IP地址port
,埠號path
,目錄路徑query
,即查詢引數fragment
,即#
後的hash值,一般用來定位到某個位置
網路請求都是單獨的執行緒
每次網路請求時都需要開闢單獨的執行緒進行,譬如如果URL解析到http協議,就會新建一個網路執行緒去處理資源下載。
因此瀏覽器會根據解析出得協議,開闢一個網路執行緒,前往請求資源(這裡,暫時理解為是瀏覽器核心開闢的,如有錯誤,後續修復)。
更多
由於篇幅關係,這裡就大概介紹一個主幹流程,關於瀏覽器的程序機制,更多可以參考以前總結的一篇文章(因為內容實在過多,裡面包括JS執行機制,程序執行緒的詳解)。
從瀏覽器多程序到JS單執行緒,JS執行機制最全面的一次梳理:https://segmentfault.com/a/1190000012925872。
開啟網路執行緒到發出一個完整的http請求
這一部分主要內容包括: dns
查詢, tcp/ip
請求構建, 五層因特網協議棧
等等。
仍然是先梳理主幹,有些詳細的過程不展開(因為展開的話內容過多)。
DNS查詢得到IP
如果輸入的是域名,需要進行dns解析成IP,大致流程:
如果瀏覽器有快取,直接使用瀏覽器快取,否則使用本機快取,再沒有的話就是用host
如果本地沒有,就向dns域名伺服器查詢(當然,中間可能還會經過路由,也有快取等),查詢到對應的IP
注意,域名查詢時有可能是經過了CDN排程器的(如果有cdn儲存功能的話)。
而且,需要知道dns解析是很耗時的,因此如果解析域名過多,會讓首屏載入變得過慢,可以考慮 dns-prefetch
優化。
這一塊可以深入展開,具體請去網上搜索,這裡就不佔篇幅了(網上可以看到很詳細的解答)。
tcp/ip請求
http的本質就是 tcp/ip
請求。
需要了解3次握手規則建立連線以及斷開連線時的四次揮手。
tcp將http長報文劃分為短報文,通過三次握手與服務端建立連線,進行可靠傳輸。
三次握手的步驟(抽象派)
客戶端:hello,你是server麼?
服務端:hello,我是server,你是client麼
客戶端:yes,我是client
建立連