從輸入URL到頁面加載的過程?如何由一道題完善自己的前端知識體系缕溉![轉(zhuǎn)]

對知識體系進(jìn)行一次預(yù)評級

看到這道題目考传,不借助搜索引擎,自己的心里是否有一個答案证鸥?

這里僚楞,以目前的經(jīng)驗(了解過一些處于不同階段的相關(guān)前端人員的情況),大概有以下幾種情況:(以下都是以點見面枉层,實際上不同階段人員一般都會有其它的隱藏知識點的)

level1:

完全沒什么概念的泉褐,支支吾吾的回答,一般就是這種水平(大致形象點描述):

  • 瀏覽器發(fā)起請求鸟蜡,服務(wù)端返回數(shù)據(jù)膜赃,然后前端解析成網(wǎng)頁,執(zhí)行腳本揉忘。跳座。端铛。

這類人員一般都是:

  • 萌新(剛接觸前端的,包括0-6個月都有可能有這種回答)
  • 沉淀人員(就是那種可能已經(jīng)接觸了前端幾年躺坟,但是仍然處于初級階段的那種沦补。乳蓄。咪橙。)

當(dāng)然了,后者一般還會偶爾提下http虚倒、后臺美侦、瀏覽器渲染js引擎等等關(guān)鍵字魂奥,但基本都是一詳細(xì)的問就不知道了菠剩。。耻煤。

level2:

已經(jīng)有初步概念具壮,但是可能沒有完整梳理過,導(dǎo)致無法形成一個完整的體系哈蝇,或者是很多細(xì)節(jié)都不會展開棺妓,大概是這樣子的:(可能符合若干條)

  • 知道瀏覽器輸入url后會有http請求這個概念
  • 有后臺這個概念,大致知道前后端的交互炮赦,知道前后端只要靠http報文通信
  • 知道瀏覽器接收到數(shù)據(jù)后會進(jìn)行解析怜跑,有一定概念,但是具體流程不熟悉(如render樹構(gòu)建流程吠勘,layout性芬、paint,復(fù)合層與簡單層剧防,常用優(yōu)化方案等不是很熟悉)
  • 對于js引擎的解析流程有一定概念植锉,但是細(xì)節(jié)不熟悉(如具體的形參,函數(shù)峭拘,變量提升俊庇,執(zhí)行上下文以及VO、AO棚唆、作用域鏈暇赤,回收機(jī)制等概念不是很熟悉)
  • 如可能知道一些http規(guī)范初步概念,但是不熟悉(如http報文結(jié)構(gòu)宵凌,常用頭部鞋囊,緩存機(jī)制,http2.0瞎惫,https等特性溜腐,跨域與web安全等不是很熟悉)

到這里译株,看到這上面一大堆的概念后,心里應(yīng)該也會有點底了挺益。歉糜。。

實際上望众,大部分的前端人員可能都處于level2匪补,但是,跳出這個階段并不容易烂翰,一般需要積累夯缺,不斷學(xué)習(xí),才能水到渠成

這類人員一般都是:

  • 工作1-3年左右的普通人員(占大多數(shù)甘耿,而且大多數(shù)人員工作3年左右并沒有實質(zhì)上的提升)
  • 工作3年以上的老人(這部分人大多都業(yè)務(wù)十分嫻熟踊兜,一個當(dāng)好幾個用,但是佳恬,基礎(chǔ)比較薄弱捏境,可能沒有嘗試寫過框架、組件毁葱、腳手架等)

大部分的初中級都陷在這個階段垫言,如果要突破,不斷學(xué)習(xí)头谜,積累骏掀,自然能水到渠成,打通任督二脈

level3:

基本能到這一步的,不是高階就是接近高階,因為很多概念并不是靠背就能理解的啄糙,而要理解這么多,需形成體系葵袭,一般都需要積累,非一日之功乖菱。

一般包括什么樣的回答呢坡锡?(這里就以自己的簡略回答進(jìn)行舉例),一般這個階段的人員都會符合若干條(不一定全部窒所,當(dāng)然可能還有些是這里遺漏的):

  • 首先略去那些鍵盤輸入鹉勒、和操作系統(tǒng)交互、以及屏幕顯示原理吵取、網(wǎng)卡等硬件交互之類的(前端向中禽额,很多硬件原理暫時略去。。脯倒。)
  • 對瀏覽器模型有整體概念实辑,知道瀏覽器是多進(jìn)程的,瀏覽器內(nèi)核是多線程的藻丢,清楚進(jìn)程與線程之間得區(qū)別剪撬,以及輸入url后會開一個新的網(wǎng)絡(luò)線程
  • 對從開啟網(wǎng)絡(luò)線程到發(fā)出一個完整的http請求中間的過程有所了解(如dns查詢,tcp/ip鏈接悠反,五層因特網(wǎng)協(xié)議棧等等残黑,以及一些優(yōu)化方案,如dns-prefetch
  • 對從服務(wù)器接收到請求到對應(yīng)后臺接收到請求有一定了解(如負(fù)載均衡问慎,安全攔截以及后臺代碼處理等)
  • 對后臺和前臺的http交互熟悉(包括http報文結(jié)構(gòu)萍摊,場景頭部,cookie如叼,跨域,web安全穷劈,http緩存笼恰,http2.0,https等)
  • 對瀏覽器接收到http數(shù)據(jù)包后的解析流程熟悉(包括解析html歇终,詞法分析然后解析成dom樹社证、解析css生成css規(guī)則樹、合并成render樹评凝,然后layout追葡、painting渲染、里面可能還包括復(fù)合圖層的合成奕短、GPU繪制宜肉、外鏈處理、加載順序等)
  • 對JS引擎解析過程熟悉(包括JS的解釋翎碑,預(yù)處理谬返,執(zhí)行上下文,VO日杈,作用域鏈遣铝,this,回收機(jī)制等)

可以看到莉擒,上述包括了一大堆的概念酿炸,僅僅是偏前端向,而且沒有詳細(xì)展開涨冀,就已經(jīng)如此之多的概念了填硕,所以,個人認(rèn)為如果沒有自己的見解蝇裤,沒有形成自己的知識體系廷支,僅僅是看看频鉴,背背是沒用的,過一段時間就會忘光了恋拍。

再說下一般這個階段的都可能是什么樣的人吧垛孔。(不一定準(zhǔn)確,這里主要是靠少部分現(xiàn)實以及大部分推測得出)

  • 工作2年以上的前端(基本上如果按正常進(jìn)度的話施敢,至少接觸前端兩年左右才會開始走向高階周荐,當(dāng)然,現(xiàn)在很多都是上學(xué)時就開始學(xué)了的僵娃,還有部分是天賦異稟概作,不好預(yù)估。默怨。讯榕。)
  • 或者是已經(jīng)十分熟悉其它某門語言,再轉(zhuǎn)前端的人(基本上是很快就可以將前端水準(zhǔn)提升上去)

一般符合這個條件的都會有各種隱藏屬性(如看過各大框架匙睹、組件的源碼愚屁,寫過自己的組件、框架痕檬、腳手架霎槐,做過大型項目,整理過若干精品博文等)

level4:

由于本人層次尚未達(dá)到梦谜,所以大致說下自己的見解吧丘跌。

一般這個層次,很多大佬都并不僅僅是某個技術(shù)棧了唁桩,而是成為了技術(shù)專家闭树,技術(shù)leader之類的角色。所以僅僅是回答某個技術(shù)問題已經(jīng)無法看出水準(zhǔn)了朵夏,
可能更多的要看架構(gòu)蔼啦,整體把控,大型工程構(gòu)建能力等等

不過仰猖,對于某些執(zhí)著于技術(shù)的大佬捏肢,大概會有一些回答吧:(猜的)

  • 從鍵盤談起到系統(tǒng)交互,從瀏覽器到CPU饥侵,從調(diào)度機(jī)制到系統(tǒng)內(nèi)核鸵赫,從數(shù)據(jù)請求到二進(jìn)制、匯編躏升,從GPU繪圖到LCD顯示辩棒,然后再分析系統(tǒng)底層的進(jìn)程、內(nèi)存等等

總之,從軟件到硬件一睁,到材料钻弄,到分子,原子者吁,量子窘俺,薛定諤的貓,人類起源复凳,宇宙大爆炸瘤泪,平行宇宙?感覺都毫無違和感育八。对途。。

這點可以參考下本題的原始出處:

http://fex.baidu.com/blog/2014/05/what-happen/

為什么說知識體系如此重要髓棋?

為什么說知識體系如此重要呢实檀?這里舉幾個例子

假設(shè)有被問到這樣一道題目(隨意想到的一個):

  • 如何理解getComputedStyle

在尚未梳理知識體系前,大概會這樣回答:

  • 普通版本:getComputedStyle會獲取當(dāng)前元素所有最終使用的CSS屬性值(最終計算后的結(jié)果)仲锄,通過window.getComputedStyle等價于document.defaultView.getComputedStyle調(diào)用
  • 詳細(xì)版本:window.getComputedStyle(elem, null).getPropertyValue("height")可能的值為100px劲妙,而且,就算是css上寫的是inherit儒喊,getComputedStyle也會把它最終計算出來的。不過注意币呵,如果元素的背景色透明怀愧,那么getComputedStyle獲取出來的就是透明的這個背景(因為透明本身也是有效的),而不會是父節(jié)點的背景余赢。所以它不一定是最終顯示的顏色芯义。

就這個API來說,上述的回答已經(jīng)比較全面了妻柒。

但是扛拨,其實它是可以繼續(xù)延伸的。

譬如現(xiàn)在會這樣回答:

  • getComputedStyle會獲取當(dāng)前元素所有最終使用的CSS屬性值举塔,window.document.defaultView.等價...
  • getComputedStyle會引起回流绑警,因為它需要獲取祖先節(jié)點的一些信息進(jìn)行計算(譬如寬高等),所以用的時候慎用央渣,回流會引起性能問題计盒。然后合適的話會將話題引導(dǎo)回流,重繪芽丹,瀏覽器渲染原理等等北启。當(dāng)然也可以列舉一些其它會引發(fā)回流的操作,如offsetXXXscrollXXX咕村,clientXXX场钉,currentStyle等等

再舉一個例子:

  • visibility: hiddendisplay: none的區(qū)別

可以如下回答:

  • 普通回答,一個隱藏懈涛,但占據(jù)位置逛万,一個隱藏,不占據(jù)位置
  • 進(jìn)一步肩钠,display由于隱藏后不占據(jù)位置泣港,所以造成了dom樹的改變,會引發(fā)回流价匠,代價較大
  • 再進(jìn)一步当纱,當(dāng)一個頁面某個元素經(jīng)常需要切換display時如何優(yōu)化,一般會用復(fù)合層優(yōu)化踩窖,或者要求低一點用absolute讓其脫離普通文檔流也行坡氯。然后可以將話題引到普通文檔流,absolute文檔流洋腮,復(fù)合圖層的區(qū)別箫柳,
  • 再進(jìn)一步可以描述下瀏覽器渲染原理以及復(fù)合圖層和普通圖層的繪制區(qū)別(復(fù)合圖層單獨分配資源,獨立繪制啥供,性能提升悯恍,但是不能過多,還有隱式合成等等)

上面這些大概就是知識系統(tǒng)化后的回答伙狐,會更全面涮毫,容易由淺入深,而且一有機(jī)會就可以往更底層挖

前端向知識的重點

此部分的內(nèi)容是站在個人視角分析的贷屎,并不是說就一定是正確答案

首先明確罢防,計算機(jī)方面的知識是可以無窮無盡的挖的,而本文的重點是梳理前端向的重點知識

對于前端向(這里可能沒有提到node.js之類的唉侄,更多的是指客戶端前端)咒吐,這里將知識點按重要程度劃分成以下幾大類:

  • 核心知識,必須掌握的属划,也是最基礎(chǔ)的恬叹,譬如瀏覽器模型,渲染原理榴嗅,JS解析過程妄呕,JS運行機(jī)制等,作為骨架來承載知識體系
  • 重點知識嗽测,往往每一塊都是一個知識點绪励,而且這些知識點都很重要肿孵,譬如http相關(guān),web安全相關(guān)疏魏,跨域處理等
  • 拓展知識停做,這一塊可能更多的是了解,稍微實踐過大莫,但是認(rèn)識上可能沒有上面那么深刻蛉腌,譬如五層因特網(wǎng)協(xié)議棧,hybrid模式只厘,移動原生開發(fā)烙丛,后臺相關(guān)等等(當(dāng)然,在不同領(lǐng)域羔味,可能有某些知識就上升到重點知識層次了河咽,譬如hybrid開發(fā)時,懂原生開發(fā)是很重要的)

為什么要按上面這種方式劃分赋元?

這大概與個人的技術(shù)成長有關(guān)忘蟹。

記得最開始學(xué)前端知識時,是一點一點的積累搁凸,一個知識點一個知識點的攻克媚值。

就這樣,雖然在很長一段時間內(nèi)積累了不少的知識护糖,但是褥芒,總是無法將它串聯(lián)到一起。每次梳理時都是很分散的嫡良,無法保持思路連貫性喂很。

直到后來,在將瀏覽器渲染原理皆刺、JS運行機(jī)制、JS引擎解析流程梳理一遍后凌摄,感覺就跟打通了任督二脈一樣羡蛾,有了一個整體的架構(gòu),以前的知識點都連貫起來了锨亏。

梳理出了一個知識體系痴怨,以后就算再學(xué)新的知識,也會盡量往這個體系上靠攏器予,環(huán)環(huán)相扣浪藻,更容易理解,也更不容易遺忘

梳理主干流程

回到這道題上乾翔,如何回答呢爱葵?先梳理一個骨架

知識體系中施戴,最重要的是骨架,脈絡(luò)萌丈。有了骨架后赞哗,才方便填充細(xì)節(jié)。所以辆雾,先梳理下主干流程:

1. 從瀏覽器接收url到開啟網(wǎng)絡(luò)請求線程(這一部分可以展開瀏覽器的機(jī)制以及進(jìn)程與線程之間的關(guān)系)

2. 開啟網(wǎng)絡(luò)線程到發(fā)出一個完整的http請求(這一部分涉及到dns查詢肪笋,tcp/ip請求,五層因特網(wǎng)協(xié)議棧等知識)

3. 從服務(wù)器接收到請求到對應(yīng)后臺接收到請求(這一部分可能涉及到負(fù)載均衡度迂,安全攔截以及后臺內(nèi)部的處理等等)

4. 后臺和前臺的http交互(這一部分包括http頭部藤乙、響應(yīng)碼、報文結(jié)構(gòu)惭墓、cookie等知識坛梁,可以提下靜態(tài)資源的cookie優(yōu)化,以及編碼解碼诅妹,如gzip壓縮等)

5. 單獨拎出來的緩存問題罚勾,http的緩存(這部分包括http緩存頭部,etag吭狡,catch-control等)

6. 瀏覽器接收到http數(shù)據(jù)包后的解析流程(解析html-詞法分析然后解析成dom樹尖殃、解析css生成css規(guī)則樹、合并成render樹划煮,然后layout送丰、painting渲染、復(fù)合圖層的合成弛秋、GPU繪制器躏、外鏈資源的處理、loaded和domcontentloaded等)

7. CSS的可視化格式模型(元素的渲染規(guī)則蟹略,如包含塊登失,控制框,BFC挖炬,IFC等概念)

8. JS引擎解析過程(JS的解釋階段揽浙,預(yù)處理階段,執(zhí)行階段生成執(zhí)行上下文意敛,VO馅巷,作用域鏈、回收機(jī)制等等)

9. 其它(可以拓展不同的知識模塊草姻,如跨域钓猬,web安全,hybrid模式等等內(nèi)容)

梳理出主干骨架撩独,然后就需要往骨架上填充細(xì)節(jié)內(nèi)容

從瀏覽器接收url到開啟網(wǎng)絡(luò)請求線程

這一部分展開的內(nèi)容是:瀏覽器進(jìn)程/線程模型敞曹,JS的運行機(jī)制

多進(jìn)程的瀏覽器

瀏覽器是多進(jìn)程的账月,有一個主控進(jìn)程,以及每一個tab頁面都會新開一個進(jìn)程(某些情況下多個tab會合并進(jìn)程)

進(jìn)程可能包括主控進(jìn)程异雁,插件進(jìn)程捶障,GPU,tab頁(瀏覽器內(nèi)核)等等

  • Browser進(jìn)程:瀏覽器的主進(jìn)程(負(fù)責(zé)協(xié)調(diào)纲刀、主控)项炼,只有一個
  • 第三方插件進(jìn)程:每種類型的插件對應(yīng)一個進(jìn)程,僅當(dāng)使用該插件時才創(chuàng)建
  • GPU進(jìn)程:最多一個锭部,用于3D繪制
  • 瀏覽器渲染進(jìn)程(內(nèi)核):默認(rèn)每個Tab頁面一個進(jìn)程拌禾,互不影響役衡,控制頁面渲染茵休,腳本執(zhí)行,事件處理等(有時候會優(yōu)化手蝎,如多個空白tab會合并成一個進(jìn)程)

如下圖:

[圖片上傳失敗...(image-b16d5f-1611625004013)]

多線程的瀏覽器內(nèi)核

每一個tab頁面可以看作是瀏覽器內(nèi)核進(jìn)程榕莺,然后這個進(jìn)程是多線程的,它有幾大類子線程

  • GUI線程
  • JS引擎線程
  • 事件觸發(fā)線程
  • 定時器線程
  • 網(wǎng)絡(luò)請求線程

[圖片上傳失敗...(image-299582-1611625004013)]

可以看到棵介,里面的JS引擎是內(nèi)核進(jìn)程中的一個線程钉鸯,這也是為什么常說JS引擎是單線程的

解析URL

輸入URL后,會進(jìn)行解析(URL的本質(zhì)就是統(tǒng)一資源定位符)

URL一般包括幾大部分:

  • protocol邮辽,協(xié)議頭亏拉,譬如有http,ftp等
  • host逆巍,主機(jī)域名或IP地址
  • port,端口號
  • path莽使,目錄路徑
  • query锐极,即查詢參數(shù)
  • fragment,即#后的hash值芳肌,一般用來定位到某個位置

網(wǎng)絡(luò)請求都是單獨的線程

每次網(wǎng)絡(luò)請求時都需要開辟單獨的線程進(jìn)行,譬如如果URL解析到http協(xié)議窝爪,就會新建一個網(wǎng)絡(luò)線程去處理資源下載

因此瀏覽器會根據(jù)解析出得協(xié)議橄妆,開辟一個網(wǎng)絡(luò)線程,前往請求資源(這里栋猖,暫時理解為是瀏覽器內(nèi)核開辟的,如有錯誤汪榔,后續(xù)修復(fù))

更多

由于篇幅關(guān)系蒲拉,這里就大概介紹一個主干流程,關(guān)于瀏覽器的進(jìn)程機(jī)制痴腌,更多可以參考以前總結(jié)的一篇文章(因為內(nèi)容實在過多雌团,里面包括JS運行機(jī)制,進(jìn)程線程的詳解)

從瀏覽器多進(jìn)程到JS單線程士聪,JS運行機(jī)制最全面的一次梳理

開啟網(wǎng)絡(luò)線程到發(fā)出一個完整的http請求

這一部分主要內(nèi)容包括:dns查詢锦援,tcp/ip請求構(gòu)建,五層因特網(wǎng)協(xié)議棧等等

仍然是先梳理主干剥悟,有些詳細(xì)的過程不展開(因為展開的話內(nèi)容過多)

DNS查詢得到IP

如果輸入的是域名灵寺,需要進(jìn)行dns解析成IP,大致流程:

  • 如果瀏覽器有緩存区岗,直接使用瀏覽器緩存略板,否則使用本機(jī)緩存,再沒有的話就是用host
  • 如果本地沒有躏尉,就向dns域名服務(wù)器查詢(當(dāng)然蚯根,中間可能還會經(jīng)過路由,也有緩存等)胀糜,查詢到對應(yīng)的IP

注意颅拦,域名查詢時有可能是經(jīng)過了CDN調(diào)度器的(如果有cdn存儲功能的話)

而且,需要知道dns解析是很耗時的教藻,因此如果解析域名過多距帅,會讓首屏加載變得過慢,可以考慮dns-prefetch優(yōu)化

這一塊可以深入展開括堤,具體請去網(wǎng)上搜索碌秸,這里就不占篇幅了(網(wǎng)上可以看到很詳細(xì)的解答)

tcp/ip請求

http的本質(zhì)就是tcp/ip請求

需要了解3次握手規(guī)則建立連接以及斷開連接時的四次揮手

tcp將http長報文劃分為短報文,通過三次握手與服務(wù)端建立連接悄窃,進(jìn)行可靠傳輸

三次握手的步驟:(抽象派)

客戶端:hello讥电,你是server么?
服務(wù)端:hello轧抗,我是server恩敌,你是client么
客戶端:yes,我是client

建立連接成功后横媚,接下來就正式傳輸數(shù)據(jù)

然后纠炮,待到斷開連接時月趟,需要進(jìn)行四次揮手(因為是全雙工的,所以需要四次揮手)

四次揮手的步驟:(抽象派)

主動方:我已經(jīng)關(guān)閉了向你那邊的主動通道了恢口,只能被動接收了
被動方:收到通道關(guān)閉的信息
被動方:那我也告訴你孝宗,我這邊向你的主動通道也關(guān)閉了
主動方:最后收到數(shù)據(jù),之后雙方無法通信

tcp/ip的并發(fā)限制

瀏覽器對同一域名下并發(fā)的tcp連接是有限制的(2-10個不等)

而且在http1.0中往往一個資源下載就需要對應(yīng)一個tcp/ip請求

所以針對這個瓶頸耕肩,又出現(xiàn)了很多的資源優(yōu)化方案

get和post的區(qū)別

get和post雖然本質(zhì)都是tcp/ip因妇,但兩者除了在http層面外,在tcp/ip層面也有區(qū)別看疗。

get會產(chǎn)生一個tcp數(shù)據(jù)包沙峻,post兩個

具體就是:

  • get請求時,瀏覽器會把headersdata一起發(fā)送出去两芳,服務(wù)器響應(yīng)200(返回數(shù)據(jù))摔寨,
  • post請求時,瀏覽器先發(fā)送headers怖辆,服務(wù)器響應(yīng)100 continue是复,

瀏覽器再發(fā)送data,服務(wù)器響應(yīng)200(返回數(shù)據(jù))竖螃。

再說一點淑廊,這里的區(qū)別是specification(規(guī)范)層面,而不是implementation(對規(guī)范的實現(xiàn))

五層因特網(wǎng)協(xié)議棧

其實這個概念挺難記全的特咆,記不全沒關(guān)系季惩,但是要有一個整體概念

其實就是一個概念: 從客戶端發(fā)出http請求到服務(wù)器接收,中間會經(jīng)過一系列的流程腻格。

簡括就是:

從應(yīng)用層的發(fā)送http請求画拾,到傳輸層通過三次握手建立tcp/ip連接,再到網(wǎng)絡(luò)層的ip尋址菜职,再到數(shù)據(jù)鏈路層的封裝成幀青抛,最后到物理層的利用物理介質(zhì)傳輸。

當(dāng)然酬核,服務(wù)端的接收就是反過來的步驟

五層因特網(wǎng)協(xié)議棧其實就是:

1.應(yīng)用層(dns,http) DNS解析成IP并發(fā)送http請求

2.傳輸層(tcp,udp) 建立tcp連接(三次握手)

3.網(wǎng)絡(luò)層(IP,ARP) IP尋址

4.數(shù)據(jù)鏈路層(PPP) 封裝成幀

5.物理層(利用物理介質(zhì)傳輸比特流) 物理傳輸(然后傳輸?shù)臅r候通過雙絞線蜜另,電磁波等各種介質(zhì))

當(dāng)然,其實也有一個完整的OSI七層框架嫡意,與之相比举瑰,多了會話層、表示層蔬螟。

OSI七層框架:物理層嘶居、數(shù)據(jù)鏈路層網(wǎng)絡(luò)層傳輸層邮屁、會話層表示層菠齿、應(yīng)用層

表示層:主要處理兩個通信系統(tǒng)中交換信息的表示方式佑吝,包括數(shù)據(jù)格式交換,數(shù)據(jù)加密與解密绳匀,數(shù)據(jù)壓縮與終端類型轉(zhuǎn)換等

會話層:它具體管理不同用戶和進(jìn)程之間的對話芋忿,如控制登陸和注銷過程

從服務(wù)器接收到請求到對應(yīng)后臺接收到請求

服務(wù)端在接收到請求時,內(nèi)部會進(jìn)行很多的處理

這里由于不是專業(yè)的后端分析疾棵,所以只是簡單的介紹下戈钢,不深入

負(fù)載均衡

對于大型的項目,由于并發(fā)訪問量很大是尔,所以往往一臺服務(wù)器是吃不消的殉了,所以一般會有若干臺服務(wù)器組成一個集群,然后配合反向代理實現(xiàn)負(fù)載均衡

當(dāng)然了拟枚,負(fù)載均衡不止這一種實現(xiàn)方式薪铜,這里不深入...

簡單的說:

用戶發(fā)起的請求都指向調(diào)度服務(wù)器(反向代理服務(wù)器,譬如安裝了nginx控制負(fù)載均衡)恩溅,然后調(diào)度服務(wù)器根據(jù)實際的調(diào)度算法隔箍,分配不同的請求給對應(yīng)集群中的服務(wù)器執(zhí)行,然后調(diào)度器等待實際服務(wù)器的HTTP響應(yīng)脚乡,并將它反饋給用戶

后臺的處理

一般后臺都是部署到容器中的蜒滩,所以一般為:

  • 先是容器接受到請求(如tomcat容器)
  • 然后對應(yīng)容器中的后臺程序接收到請求(如java程序)
  • 然后就是后臺會有自己的統(tǒng)一處理,處理完后響應(yīng)響應(yīng)結(jié)果

概括下:

  • 一般有的后端是有統(tǒng)一的驗證的奶稠,如安全攔截俯艰,跨域驗證
  • 如果這一步不符合規(guī)則,就直接返回了相應(yīng)的http報文(如拒絕請求等)
  • 然后當(dāng)驗證通過后窒典,才會進(jìn)入實際的后臺代碼蟆炊,此時是程序接收到請求,然后執(zhí)行(譬如查詢數(shù)據(jù)庫瀑志,大量計算等等)
  • 等程序執(zhí)行完畢后涩搓,就會返回一個http響應(yīng)包(一般這一步也會經(jīng)過多層封裝)
  • 然后就是將這個包從后端發(fā)送到前端,完成交互

后臺和前臺的http交互

前后端交互時劈猪,http報文作為信息的載體

所以http是一塊很重要的內(nèi)容昧甘,這一部分重點介紹它

http報文結(jié)構(gòu)

報文一般包括了:通用頭部請求/響應(yīng)頭部战得,請求/響應(yīng)體

通用頭部

這也是開發(fā)人員見過的最多的信息充边,包括如下:

Request Url: 請求的web服務(wù)器地址

Request Method: 請求方式
(Get、POST、OPTIONS浇冰、PUT贬媒、HEAD、DELETE肘习、CONNECT际乘、TRACE)

Status Code: 請求的返回狀態(tài)碼,如200代表成功

Remote Address: 請求的遠(yuǎn)程服務(wù)器地址(會轉(zhuǎn)為IP)

譬如漂佩,在跨域拒絕時脖含,可能是method為options,狀態(tài)碼為404/405等(當(dāng)然投蝉,實際上可能的組合有很多)

其中养葵,Method的話一般分為兩批次:

HTTP1.0定義了三種請求方法: GET, POST 和 HEAD方法。
以及幾種Additional Request Methods:PUT瘩缆、DELETE关拒、LINK、UNLINK

HTTP1.1定義了八種請求方法:GET咳榜、POST夏醉、HEAD、OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法涌韩。

HTTP 1.0定義參考:https://tools.ietf.org/html/rfc1945

HTTP 1.1定義參考:https://tools.ietf.org/html/rfc2616

這里面最常用到的就是狀態(tài)碼畔柔,很多時候都是通過狀態(tài)碼來判斷,如(列舉幾個最常見的):

200——表明該請求被成功地完成臣樱,所請求的資源發(fā)送回客戶端
304——自從上次請求后靶擦,請求的網(wǎng)頁未修改過,請客戶端使用本地緩存
400——客戶端請求有錯(譬如可以是安全模塊攔截)
401——請求未經(jīng)授權(quán)
403——禁止訪問(譬如可以是未登錄時禁止)
404——資源未找到
500——服務(wù)器內(nèi)部錯誤
503——服務(wù)不可用
...

再列舉下大致不同范圍狀態(tài)的意義

1xx——指示信息雇毫,表示請求已接收玄捕,繼續(xù)處理
2xx——成功,表示請求已被成功接收棚放、理解枚粘、接受
3xx——重定向,要完成請求必須進(jìn)行更進(jìn)一步的操作
4xx——客戶端錯誤飘蚯,請求有語法錯誤或請求無法實現(xiàn)
5xx——服務(wù)器端錯誤馍迄,服務(wù)器未能實現(xiàn)合法的請求

[圖片上傳失敗...(image-79b3e6-1611625004013)]

總之,當(dāng)請求出錯時局骤,狀態(tài)碼能幫助快速定位問題攀圈,完整版本的狀態(tài)可以自行去互聯(lián)網(wǎng)搜索

請求/響應(yīng)頭部

請求和響應(yīng)頭部也是分析時常用到的

常用的請求頭部(部分):

Accept: 接收類型,表示瀏覽器支持的MIME類型
(對標(biāo)服務(wù)端返回的Content-Type)
Accept-Encoding:瀏覽器支持的壓縮類型,如gzip等,超出類型不能接收
Content-Type:客戶端發(fā)送出去實體內(nèi)容的類型
Cache-Control: 指定請求和響應(yīng)遵循的緩存機(jī)制峦甩,如no-cache
If-Modified-Since:對應(yīng)服務(wù)端的Last-Modified赘来,用來匹配看文件是否變動,只能精確到1s之內(nèi),http1.0中
Expires:緩存控制犬辰,在這個時間內(nèi)不會請求嗦篱,直接使用緩存,http1.0幌缝,而且是服務(wù)端時間
Max-age:代表資源在本地緩存多少秒默色,有效時間內(nèi)不會請求,而是使用緩存狮腿,http1.1中
If-None-Match:對應(yīng)服務(wù)端的ETag,用來匹配文件內(nèi)容是否改變(非常精確)呕诉,http1.1中
Cookie: 有cookie并且同域訪問時會自動帶上
Connection: 當(dāng)瀏覽器與服務(wù)器通信時對于長連接如何進(jìn)行處理,如keep-alive
Host:請求的服務(wù)器URL
Origin:最初的請求是從哪里發(fā)起的(只會精確到端口),Origin比Referer更尊重隱私
Referer:該頁面的來源URL(適用于所有類型的請求缘厢,會精確到詳細(xì)頁面地址,csrf攔截常用到這個字段)
User-Agent:用戶客戶端的一些必要信息甩挫,如UA頭部等

常用的響應(yīng)頭部(部分):

Access-Control-Allow-Headers: 服務(wù)器端允許的請求Headers
Access-Control-Allow-Methods: 服務(wù)器端允許的請求方法
Access-Control-Allow-Origin: 服務(wù)器端允許的請求Origin頭部(譬如為*)
Content-Type:服務(wù)端返回的實體內(nèi)容的類型
Date:數(shù)據(jù)從服務(wù)器發(fā)送的時間
Cache-Control:告訴瀏覽器或其他客戶贴硫,什么環(huán)境可以安全的緩存文檔
Last-Modified:請求資源的最后修改時間
Expires:應(yīng)該在什么時候認(rèn)為文檔已經(jīng)過期,從而不再緩存它
Max-age:客戶端的本地資源應(yīng)該緩存多少秒,開啟了Cache-Control后有效
ETag:請求變量的實體標(biāo)簽的當(dāng)前值
Set-Cookie:設(shè)置和頁面關(guān)聯(lián)的cookie伊者,服務(wù)器通過這個頭部把cookie傳給客戶端
Keep-Alive:如果客戶端有keep-alive英遭,服務(wù)端也會有響應(yīng)(如timeout=38)
Server:服務(wù)器的一些相關(guān)信息

一般來說,請求頭部和響應(yīng)頭部是匹配分析的亦渗。

譬如挖诸,請求頭部的Accept要和響應(yīng)頭部的Content-Type匹配,否則會報錯

譬如法精,跨域請求時多律,請求頭部的Origin要匹配響應(yīng)頭部的Access-Control-Allow-Origin,否則會報跨域錯誤

譬如搂蜓,在使用緩存時狼荞,請求頭部的If-Modified-SinceIf-None-Match分別和響應(yīng)頭部的Last-Modified帮碰、ETag對應(yīng)

還有很多的分析方法相味,這里不一一贅述

請求/響應(yīng)實體

http請求時,除了頭部殉挽,還有消息實體丰涉,一般來說

請求實體中會將一些需要的參數(shù)都放入進(jìn)入(用于post請求)。

譬如實體中可以放參數(shù)的序列化形式(a=1&b=2這種)此再,或者直接放表單對象(Form Data對象昔搂,上傳時可以夾雜參數(shù)以及文件),等等

而一般響應(yīng)實體中输拇,就是放服務(wù)端需要傳給客戶端的內(nèi)容

一般現(xiàn)在的接口請求時摘符,實體中就是對于的信息的json格式,而像頁面請求這種,里面就是直接放了一個html字符串逛裤,然后瀏覽器自己解析并渲染瘩绒。

CRLF

CRLF(Carriage-Return Line-Feed),意思是回車換行带族,一般作為分隔符存在

請求頭和實體消息之間有一個CRLF分隔锁荔,響應(yīng)頭部和響應(yīng)實體之間用一個CRLF分隔

一般來說(分隔符類別):

CRLF->Windows-style
LF->Unix Style
CR->Mac Style

如下圖是對某請求的http報文結(jié)構(gòu)的簡要分析

[圖片上傳失敗...(image-91d2f-1611625004013)]

cookie以及優(yōu)化

cookie是瀏覽器的一種本地存儲方式,一般用來幫助客戶端和服務(wù)端通信的蝙砌,常用來進(jìn)行身份校驗阳堕,結(jié)合服務(wù)端的session使用。

場景如下(簡述):

在登陸頁面择克,用戶登陸了

此時恬总,服務(wù)端會生成一個session,session中有對于用戶的信息(如用戶名肚邢、密碼等)

然后會有一個sessionid(相當(dāng)于是服務(wù)端的這個session對應(yīng)的key)

然后服務(wù)端在登錄頁面中寫入cookie壹堰,值就是:jsessionid=xxx

然后瀏覽器本地就有這個cookie了,以后訪問同域名下的頁面時骡湖,自動帶上cookie贱纠,自動檢驗,在有效時間內(nèi)無需二次登陸响蕴。

上述就是cookie的常用場景簡述(當(dāng)然了谆焊,實際情況下得考慮更多因素)

一般來說,cookie是不允許存放敏感信息的(千萬不要明文存儲用戶名换途、密碼)懊渡,因為非常不安全,如果一定要強(qiáng)行存儲军拟,首先剃执,一定要在cookie中設(shè)置httponly(這樣就無法通過js操作了),另外可以考慮rsa等非對稱加密(因為實際上懈息,瀏覽器本地也是容易被攻克的肾档,并不安全)

另外,由于在同域名的資源請求時辫继,瀏覽器會默認(rèn)帶上本地的cookie怒见,針對這種情況,在某些場景下是需要優(yōu)化的姑宽。

譬如以下場景:

客戶端在域名A下有cookie(這個可以是登陸時由服務(wù)端寫入的)

然后在域名A下有一個頁面遣耍,頁面中有很多依賴的靜態(tài)資源(都是域名A的,譬如有20個靜態(tài)資源)

此時就有一個問題炮车,頁面加載舵变,請求這些靜態(tài)資源時酣溃,瀏覽器會默認(rèn)帶上cookie

也就是說,這20個靜態(tài)資源的http請求纪隙,每一個都得帶上cookie赊豌,而實際上靜態(tài)資源并不需要cookie驗證

此時就造成了較為嚴(yán)重的浪費,而且也降低了訪問速度(因為內(nèi)容更多了)

當(dāng)然了绵咱,針對這種場景碘饼,是有優(yōu)化方案的(多域名拆分)。具體做法就是:

  • 將靜態(tài)資源分組悲伶,分別放到不同的域名下(如static.base.com
  • page.base.com(頁面所在域名)下請求時艾恼,是不會帶上static.base.com域名的cookie的,所以就避免了浪費

說到了多域名拆分麸锉,這里再提一個問題蒂萎,那就是:

  • 在移動端,如果請求的域名數(shù)過多淮椰,會降低請求速度(因為域名整套解析流程是很耗費時間的,而且移動端一般帶寬都比不上pc)
  • 此時就需要用到一種優(yōu)化方案:dns-prefetch(讓瀏覽器空閑時提前解析dns域名纳寂,不過也請合理使用主穗,勿濫用)

關(guān)于cookie的交互,可以看下圖總結(jié)

[圖片上傳失敗...(image-5d620f-1611625004013)]

gzip壓縮

首先毙芜,明確gzip是一種壓縮格式忽媒,需要瀏覽器支持才有效(不過一般現(xiàn)在瀏覽器都支持),
而且gzip壓縮效率很好(高達(dá)70%左右)

然后gzip一般是由apache腋粥、tomcat等web服務(wù)器開啟

當(dāng)然服務(wù)器除了gzip外晦雨,也還會有其它壓縮格式(如deflate,沒有g(shù)zip高效隘冲,且不流行)

所以一般只需要在服務(wù)器上開啟了gzip壓縮闹瞧,然后之后的請求就都是基于gzip壓縮格式的,
非常方便展辞。

長連接與短連接

首先看tcp/ip層面的定義:

  • 長連接:一個tcp/ip連接上可以連續(xù)發(fā)送多個數(shù)據(jù)包奥邮,在tcp連接保持期間,如果沒有數(shù)據(jù)包發(fā)送罗珍,需要雙方發(fā)檢測包以維持此連接洽腺,一般需要自己做在線維持(類似于心跳包)
  • 短連接:通信雙方有數(shù)據(jù)交互時,就建立一個tcp連接覆旱,數(shù)據(jù)發(fā)送完成后蘸朋,則斷開此tcp連接

然后在http層面:

  • http1.0中,默認(rèn)使用的是短連接扣唱,也就是說藕坯,瀏覽器沒進(jìn)行一次http操作团南,就建立一次連接,任務(wù)結(jié)束就中斷連接堕担,譬如每一個靜態(tài)資源請求時都是一個單獨的連接
  • http1.1起已慢,默認(rèn)使用長連接,使用長連接會有這一行Connection: keep-alive霹购,在長連接的情況下佑惠,當(dāng)一個網(wǎng)頁打開完成后,客戶端和服務(wù)端之間用于傳輸http的tcp連接不會關(guān)閉齐疙,如果客戶端再次訪問這個服務(wù)器的頁面膜楷,會繼續(xù)使用這一條已經(jīng)建立的連接

注意: keep-alive不會永遠(yuǎn)保持,它有一個持續(xù)時間贞奋,一般在服務(wù)器中配置(如apache)赌厅,另外長連接需要客戶端和服務(wù)器都支持時才有效

http 2.0

http2.0不是https,它相當(dāng)于是http的下一代規(guī)范(譬如https的請求可以是http2.0規(guī)范的)

然后簡述下http2.0與http1.1的顯著不同點:

  • http1.1中轿塔,每請求一個資源特愿,都是需要開啟一個tcp/ip連接的,所以對應(yīng)的結(jié)果是勾缭,每一個資源對應(yīng)一個tcp/ip請求揍障,由于tcp/ip本身有并發(fā)數(shù)限制,所以當(dāng)資源一多俩由,速度就顯著慢下來
  • http2.0中毒嫡,一個tcp/ip請求可以請求多個資源,也就是說幻梯,只要一次tcp/ip請求兜畸,就可以請求若干個資源避凝,分割成更小的幀請求鲁僚,速度明顯提升舵抹。

所以峦朗,如果http2.0全面應(yīng)用饲做,很多http1.1中的優(yōu)化方案就無需用到了(譬如打包成精靈圖主胧,靜態(tài)資源多域名拆分等)

然后簡述下http2.0的一些特性:

  • 多路復(fù)用(即一個tcp/ip連接可以請求多個資源)
  • 首部壓縮(http頭部壓縮迄委,減少體積)
  • 二進(jìn)制分幀(在應(yīng)用層跟傳送層之間增加了一個二進(jìn)制分幀層蚌吸,改進(jìn)傳輸性能汰翠,實現(xiàn)低延遲和高吞吐量)
  • 服務(wù)器端推送(服務(wù)端可以對客戶端的一個請求發(fā)出多個響應(yīng)龄坪,可以主動通知客戶端)
  • 請求優(yōu)先級(如果流被賦予了優(yōu)先級,它就會基于這個優(yōu)先級來處理复唤,由服務(wù)器決定需要多少資源來處理該請求健田。)

https

https就是安全版本的http,譬如一些支付等操作基本都是基于https的佛纫,因為http請求的安全系數(shù)太低了妓局。

簡單來看总放,https與http的區(qū)別就是: 在請求前,會建立ssl鏈接好爬,確保接下來的通信都是加密的局雄,無法被輕易截取分析

一般來說,如果要將網(wǎng)站升級成https存炮,需要后端支持(后端需要申請證書等)炬搭,然后https的開銷也比http要大(因為需要額外建立安全鏈接以及加密等),所以一般來說http2.0配合https的體驗更佳(因為http2.0更快了)

一般來說穆桂,主要關(guān)注的就是SSL/TLS的握手流程宫盔,如下(簡述):

1. 瀏覽器請求建立SSL鏈接,并向服務(wù)端發(fā)送一個隨機(jī)數(shù)–Client random和客戶端支持的加密方法享完,比如RSA加密灼芭,此時是明文傳輸。 

2. 服務(wù)端從中選出一組加密算法與Hash算法般又,回復(fù)一個隨機(jī)數(shù)–Server random彼绷,并將自己的身份信息以證書的形式發(fā)回給瀏覽器
(證書里包含了網(wǎng)站地址,非對稱加密的公鑰茴迁,以及證書頒發(fā)機(jī)構(gòu)等信息)

3. 瀏覽器收到服務(wù)端的證書后

    - 驗證證書的合法性(頒發(fā)機(jī)構(gòu)是否合法苛预,證書中包含的網(wǎng)址是否和正在訪問的一樣),如果證書信任笋熬,則瀏覽器會顯示一個小鎖頭,否則會有提示

    - 用戶接收證書后(不管信不信任)腻菇,瀏覽會生產(chǎn)新的隨機(jī)數(shù)–Premaster secret胳螟,然后證書中的公鑰以及指定的加密方法加密`Premaster secret`,發(fā)送給服務(wù)器筹吐。

    - 利用Client random糖耸、Server random和Premaster secret通過一定的算法生成HTTP鏈接數(shù)據(jù)傳輸?shù)膶ΨQ加密key-`session key`

    - 使用約定好的HASH算法計算握手消息,并使用生成的`session key`對消息進(jìn)行加密丘薛,最后將之前生成的所有信息發(fā)送給服務(wù)端嘉竟。 

4. 服務(wù)端收到瀏覽器的回復(fù)

    - 利用已知的加解密方式與自己的私鑰進(jìn)行解密,獲取`Premaster secret`

    - 和瀏覽器相同規(guī)則生成`session key`

    - 使用`session key`解密瀏覽器發(fā)來的握手消息洋侨,并驗證Hash是否與瀏覽器發(fā)來的一致

    - 使用`session key`加密一段握手消息舍扰,發(fā)送給瀏覽器

5. 瀏覽器解密并計算握手消息的HASH,如果與服務(wù)端發(fā)來的HASH一致希坚,此時握手過程結(jié)束边苹,

之后所有的https通信數(shù)據(jù)將由之前瀏覽器生成的session key并利用對稱加密算法進(jìn)行加密

這里放一張圖(來源:阮一峰-圖解SSL/TLS協(xié)議

[圖片上傳失敗...(image-cd4443-1611625004013)]

單獨拎出來的緩存問題,http的緩存

前后端的http交互中裁僧,使用緩存能很大程度上的提升效率个束,而且基本上對性能有要求的前端項目都是必用緩存的

強(qiáng)緩存與弱緩存

緩存可以簡單的劃分成兩種類型:強(qiáng)緩存200 from cache)與協(xié)商緩存304

區(qū)別簡述如下:

  • 強(qiáng)緩存(200 from cache)時慕购,瀏覽器如果判斷本地緩存未過期,就直接使用茬底,無需發(fā)起http請求
  • 協(xié)商緩存(304)時沪悲,瀏覽器會向服務(wù)端發(fā)起http請求,然后服務(wù)端告訴瀏覽器文件未改變阱表,讓瀏覽器使用本地緩存

對于協(xié)商緩存殿如,使用Ctrl + F5強(qiáng)制刷新可以使得緩存無效

但是對于強(qiáng)緩存,在未過期時捶枢,必須更新資源路徑才能發(fā)起新的請求(更改了路徑相當(dāng)于是另一個資源了握截,這也是前端工程化中常用到的技巧)

緩存頭部簡述

上述提到了強(qiáng)緩存和協(xié)商緩存,那它們是怎么區(qū)分的呢烂叔?

答案是通過不同的http頭部控制

先看下這幾個頭部:

If-None-Match/E-tag谨胞、If-Modified-Since/Last-Modified、Cache-Control/Max-Age蒜鸡、Pragma/Expires

這些就是緩存中常用到的頭部胯努,這里不展開。僅列舉下大致使用逢防。

屬于強(qiáng)緩存控制的:

(http1.1)Cache-Control/Max-Age
(http1.0)Pragma/Expires

注意:Max-Age不是一個頭部叶沛,它是Cache-Control頭部的值

屬于協(xié)商緩存控制的:

(http1.1)If-None-Match/E-tag
(http1.0)If-Modified-Since/Last-Modified

可以看到,上述有提到http1.1http1.0忘朝,這些不同的頭部是屬于不同http時期的

再提一點灰署,其實HTML頁面中也有一個meta標(biāo)簽可以控制緩存方案-Pragma

<META HTTP-EQUIV="Pragma" CONTENT="no-cache">

不過,這種方案還是比較少用到局嘁,因為支持情況不佳溉箕,譬如緩存代理服務(wù)器肯定不支持,所以不推薦

頭部的區(qū)別

首先明確悦昵,http的發(fā)展是從http1.0到http1.1

而在http1.1中肴茄,出了一些新內(nèi)容,彌補(bǔ)了http1.0的不足但指。

http1.0中的緩存控制:

  • Pragma:嚴(yán)格來說寡痰,它不屬于專門的緩存控制頭部,但是它設(shè)置no-cache時可以讓本地強(qiáng)緩存失效(屬于編譯控制棋凳,來實現(xiàn)特定的指令拦坠,主要是因為兼容http1.0,所以以前又被大量應(yīng)用)
  • Expires:服務(wù)端配置的剩岳,屬于強(qiáng)緩存贪婉,用來控制在規(guī)定的時間之前,瀏覽器不會發(fā)出請求卢肃,而是直接使用本地緩存疲迂,注意才顿,Expires一般對應(yīng)服務(wù)器端時間,如Expires:Fri, 30 Oct 1998 14:19:41
  • If-Modified-Since/Last-Modified:這兩個是成對出現(xiàn)的尤蒿,屬于協(xié)商緩存的內(nèi)容郑气,其中瀏覽器的頭部是If-Modified-Since,而服務(wù)端的是Last-Modified腰池,它的作用是尾组,在發(fā)起請求時,如果If-Modified-SinceLast-Modified匹配示弓,那么代表服務(wù)器資源并未改變讳侨,因此服務(wù)端不會返回資源實體,而是只返回頭部奏属,通知瀏覽器可以使用本地緩存跨跨。Last-Modified,顧名思義囱皿,指的是文件最后的修改時間勇婴,而且只能精確到1s以內(nèi)

http1.1中的緩存控制:

  • Cache-Control:緩存控制頭部,有no-cache嘱腥、max-age等多種取值
  • Max-Age:服務(wù)端配置的耕渴,用來控制強(qiáng)緩存,在規(guī)定的時間之內(nèi)齿兔,瀏覽器無需發(fā)出請求橱脸,直接使用本地緩存,注意分苇,Max-Age是Cache-Control頭部的值添诉,不是獨立的頭部,譬如Cache-Control: max-age=3600组砚,而且它值得是絕對時間,由瀏覽器自己計算
  • If-None-Match/E-tag:這兩個是成對出現(xiàn)的掏颊,屬于協(xié)商緩存的內(nèi)容糟红,其中瀏覽器的頭部是If-None-Match,而服務(wù)端的是E-tag乌叶,同樣盆偿,發(fā)出請求后,如果If-None-MatchE-tag匹配准浴,則代表內(nèi)容未變事扭,通知瀏覽器使用本地緩存,和Last-Modified不同乐横,E-tag更精確求橄,它是類似于指紋一樣的東西今野,基于FileEtag INode Mtime Size生成,也就是說罐农,只要文件變条霜,指紋就會變,而且沒有1s精確度的限制涵亏。

Max-Age相比Expires宰睡?

Expires使用的是服務(wù)器端的時間

但是有時候會有這樣一種情況-客戶端時間和服務(wù)端不同步

那這樣,可能就會出問題了气筋,造成了瀏覽器本地的緩存無用或者一直無法過期

所以一般http1.1后不推薦使用Expires

Max-Age使用的是客戶端本地時間的計算拆内,因此不會有這個問題

因此推薦使用Max-Age

注意宠默,如果同時啟用了Cache-ControlExpires麸恍,Cache-Control優(yōu)先級高。

E-tag相比Last-Modified光稼?

Last-Modified

  • 表明服務(wù)端的文件最后何時改變的
  • 它有一個缺陷就是只能精確到1s或南,
  • 然后還有一個問題就是有的服務(wù)端的文件會周期性的改變,導(dǎo)致緩存失效

E-tag

  • 是一種指紋機(jī)制艾君,代表文件相關(guān)指紋
  • 只有文件變才會變采够,也只要文件變就會變,
  • 也沒有精確時間的限制冰垄,只要文件一遍蹬癌,立馬E-tag就不一樣了

如果同時帶有E-tagLast-Modified,服務(wù)端會優(yōu)先檢查E-tag

各大緩存頭部的整體關(guān)系如下圖

[圖片上傳失敗...(image-54c8c3-1611625004013)]

解析頁面流程

前面有提到http交互虹茶,那么接下來就是瀏覽器獲取到html逝薪,然后解析,渲染

這部分很多都參考了網(wǎng)上資源蝴罪,特別是圖片董济,參考了來源中的文章

流程簡述

瀏覽器內(nèi)核拿到內(nèi)容后,渲染步驟大致可以分為以下幾步:

1. 解析HTML要门,構(gòu)建DOM樹

2. 解析CSS虏肾,生成CSS規(guī)則樹

3. 合并DOM樹和CSS規(guī)則,生成render樹

4. 布局render樹(Layout/reflow)欢搜,負(fù)責(zé)各元素尺寸封豪、位置的計算

5. 繪制render樹(paint),繪制頁面像素信息

6. 瀏覽器會將各層的信息發(fā)送給GPU炒瘟,GPU會將各層合成(composite)吹埠,顯示在屏幕上

如下圖:

[圖片上傳失敗...(image-1b0fec-1611625004013)]

HTML解析,構(gòu)建DOM

整個渲染步驟中,HTML解析是第一步缘琅。

簡單的理解粘都,這一步的流程是這樣的:瀏覽器解析HTML,構(gòu)建DOM樹胯杭。

但實際上驯杜,在分析整體構(gòu)建時,卻不能一筆帶過做个,得稍微展開鸽心。

解析HTML到構(gòu)建出DOM當(dāng)然過程可以簡述如下:

Bytes → characters → tokens → nodes → DOM

譬如假設(shè)有這樣一個HTML頁面:(以下部分的內(nèi)容出自參考來源,修改了下格式)

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>

瀏覽器的處理如下:

[圖片上傳失敗...(image-feaef9-1611625004012)]

列舉其中的一些重點過程:

1. Conversion轉(zhuǎn)換:瀏覽器將獲得的HTML內(nèi)容(Bytes)基于他的編碼轉(zhuǎn)換為單個字符

2. Tokenizing分詞:瀏覽器按照HTML規(guī)范標(biāo)準(zhǔn)將這些字符轉(zhuǎn)換為不同的標(biāo)記token居暖。每個token都有自己獨特的含義以及規(guī)則集

3. Lexing詞法分析:分詞的結(jié)果是得到一堆的token顽频,此時把他們轉(zhuǎn)換為對象,這些對象分別定義他們的屬性和規(guī)則

4. DOM構(gòu)建:因為HTML標(biāo)記定義的就是不同標(biāo)簽之間的關(guān)系太闺,這個關(guān)系就像是一個樹形結(jié)構(gòu)一樣
例如:body對象的父節(jié)點就是HTML對象糯景,然后段略p對象的父節(jié)點就是body對象

最后的DOM樹如下:

[圖片上傳失敗...(image-95538a-1611625004012)]

生成CSS規(guī)則

同理,CSS規(guī)則樹的生成也是類似省骂。簡述為:

Bytes → characters → tokens → nodes → CSSOM

譬如style.css內(nèi)容如下:

body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }

那么最終的CSSOM樹就是:

[圖片上傳失敗...(image-988232-1611625004012)]

構(gòu)建渲染樹

當(dāng)DOM樹和CSSOM都有了后蟀淮,就要開始構(gòu)建渲染樹了

一般來說,渲染樹和DOM樹相對應(yīng)的钞澳,但不是嚴(yán)格意義上的一一對應(yīng)

因為有一些不可見的DOM元素不會插入到渲染樹中怠惶,如head這種不可見的標(biāo)簽或者display: none

整體來說可以看圖:

[圖片上傳失敗...(image-96889-1611625004012)]

渲染

有了render樹,接下來就是開始渲染轧粟,基本流程如下:

[圖片上傳失敗...(image-dde2c9-1611625004012)]

圖中重要的四個步驟就是:

1. 計算CSS樣式

2. 構(gòu)建渲染樹

3. 布局策治,主要定位坐標(biāo)和大小,是否換行兰吟,各種position overflow z-index屬性

4. 繪制通惫,將圖像繪制出來

然后,圖中的線與箭頭代表通過js動態(tài)修改了DOM或CSS混蔼,導(dǎo)致了重新布局(Layout)或渲染(Repaint)

這里L(fēng)ayout和Repaint的概念是有區(qū)別的:

  • Layout履腋,也稱為Reflow,即回流惭嚣。一般意味著元素的內(nèi)容遵湖、結(jié)構(gòu)、位置或尺寸發(fā)生了變化料按,需要重新計算樣式和渲染樹
  • Repaint奄侠,即重繪卓箫。意味著元素發(fā)生的改變只是影響了元素的一些外觀之類的時候(例如载矿,背景色,邊框顏色,文字顏色等)闷盔,此時只需要應(yīng)用新樣式繪制這個元素就可以了

回流的成本開銷要高于重繪弯洗,而且一個節(jié)點的回流往往回導(dǎo)致子節(jié)點以及同級節(jié)點的回流,
所以優(yōu)化方案中一般都包括逢勾,盡量避免回流牡整。

什么會引起回流?

1.頁面渲染初始化

2.DOM結(jié)構(gòu)改變溺拱,比如刪除了某個節(jié)點

3.render樹變化逃贝,比如減少了padding

4.窗口resize

5.最復(fù)雜的一種:獲取某些屬性,引發(fā)回流迫摔,
很多瀏覽器會對回流做優(yōu)化沐扳,會等到數(shù)量足夠時做一次批處理回流,
但是除了render樹的直接變化句占,當(dāng)獲取一些屬性時沪摄,瀏覽器為了獲得正確的值也會觸發(fā)回流,這樣使得瀏覽器優(yōu)化無效纱烘,包括
    (1)offset(Top/Left/Width/Height)
     (2) scroll(Top/Left/Width/Height)
     (3) cilent(Top/Left/Width/Height)
     (4) width,height
     (5) 調(diào)用了getComputedStyle()或者IE的currentStyle

回流一定伴隨著重繪杨拐,重繪卻可以單獨出現(xiàn)

所以一般會有一些優(yōu)化方案,如:

  • 減少逐項更改樣式擂啥,最好一次性更改style哄陶,或者將樣式定義為class并一次性更新
  • 避免循環(huán)操作dom,創(chuàng)建一個documentFragment或div啤它,在它上面應(yīng)用所有DOM操作奕筐,最后再把它添加到window.document
  • 避免多次讀取offset等屬性。無法避免則將它們緩存到變量
  • 將復(fù)雜的元素絕對定位或固定定位变骡,使得它脫離文檔流离赫,否則回流代價會很高

注意:改變字體大小會引發(fā)回流

再來看一個示例:

var s = document.body.style;

s.padding = "2px"; // 回流+重繪
s.border = "1px solid red"; // 再一次 回流+重繪
s.color = "blue"; // 再一次重繪
s.backgroundColor = "#ccc"; // 再一次 重繪
s.fontSize = "14px"; // 再一次 回流+重繪
// 添加node,再一次 回流+重繪
document.body.appendChild(document.createTextNode('abc!'));

簡單層與復(fù)合層

上述中的渲染中止步于繪制塌碌,但實際上繪制這一步也沒有這么簡單渊胸,它可以結(jié)合復(fù)合層和簡單層的概念來講。

這里不展開台妆,進(jìn)簡單介紹下:

  • 可以認(rèn)為默認(rèn)只有一個復(fù)合圖層翎猛,所有的DOM節(jié)點都是在這個復(fù)合圖層下的
  • 如果開啟了硬件加速功能,可以將某個節(jié)點變成復(fù)合圖層
  • 復(fù)合圖層之間的繪制互不干擾接剩,由GPU直接控制
  • 而簡單圖層中切厘,就算是absolute等布局,變化時不影響整體的回流懊缺,但是由于在同一個圖層中疫稿,仍然是會影響繪制的,因此做動畫時性能仍然很低。而復(fù)合層是獨立的遗座,所以一般做動畫推薦使用硬件加速

更多參考:

普通圖層和復(fù)合圖層

Chrome中的調(diào)試

Chrome的開發(fā)者工具中舀凛,Performance中可以看到詳細(xì)的渲染過程:

[圖片上傳失敗...(image-1d9b1-1611625004012)]
[圖片上傳失敗...(image-72343b-1611625004012)]

資源外鏈的下載

上面介紹了html解析,渲染流程途蒋。但實際上猛遍,在解析html時,會遇到一些資源連接号坡,此時就需要進(jìn)行單獨處理了

簡單起見懊烤,這里將遇到的靜態(tài)資源分為一下幾大類(未列舉所有):

  • CSS樣式資源
  • JS腳本資源
  • img圖片類資源

遇到外鏈時的處理

當(dāng)遇到上述的外鏈時,會單獨開啟一個下載線程去下載資源(http1.1中是每一個資源的下載都要開啟一個http請求宽堆,對應(yīng)一個tcp/ip鏈接)

遇到CSS樣式資源

CSS資源的處理有幾個特點:

  • CSS下載時異步奸晴,不會阻塞瀏覽器構(gòu)建DOM樹
  • 但是會阻塞渲染,也就是在構(gòu)建render時日麸,會等到css下載解析完畢后才進(jìn)行(這點與瀏覽器優(yōu)化有關(guān)寄啼,防止css規(guī)則不斷改變,避免了重復(fù)的構(gòu)建)
  • 有例外代箭,media query聲明的CSS是不會阻塞渲染的

遇到JS腳本資源

JS腳本資源的處理有幾個特點:

  • 阻塞瀏覽器的解析墩划,也就是說發(fā)現(xiàn)一個外鏈腳本時,需等待腳本下載完成并執(zhí)行后才會繼續(xù)解析HTML
  • 瀏覽器的優(yōu)化嗡综,一般現(xiàn)代瀏覽器有優(yōu)化乙帮,在腳本阻塞時,也會繼續(xù)下載其它資源(當(dāng)然有并發(fā)上限)极景,但是雖然腳本可以并行下載察净,解析過程仍然是阻塞的,也就是說必須這個腳本執(zhí)行完畢后才會接下來的解析盼樟,并行下載只是一種優(yōu)化而已
  • defer與async氢卡,普通的腳本是會阻塞瀏覽器解析的,但是可以加上defer或async屬性晨缴,這樣腳本就變成異步了译秦,可以等到解析完畢后再執(zhí)行

注意,defer和async是有區(qū)別的: defer是延遲執(zhí)行击碗,而async是異步執(zhí)行筑悴。

簡單的說(不展開):

  • async是異步執(zhí)行,異步下載完畢后就會執(zhí)行稍途,不確保執(zhí)行順序阁吝,一定在onload前,但不確定在DOMContentLoaded事件的前或后
  • defer是延遲執(zhí)行械拍,在瀏覽器看起來的效果像是將腳本放在了body后面一樣(雖然按規(guī)范應(yīng)該是在DOMContentLoaded事件前突勇,但實際上不同瀏覽器的優(yōu)化效果不一樣射沟,也有可能在它后面)

遇到img圖片類資源

遇到圖片等資源時,直接就是異步下載与境,不會阻塞解析,下載完畢后直接用圖片替換原有src的地方

loaded和domcontentloaded

簡單的對比:

  • DOMContentLoaded 事件觸發(fā)時猖吴,僅當(dāng)DOM加載完成摔刁,不包括樣式表,圖片(譬如如果有async加載的腳本就不一定完成)
  • load 事件觸發(fā)時海蔽,頁面上所有的DOM共屈,樣式表,腳本党窜,圖片都已經(jīng)加載完成了

CSS的可視化格式模型

這一部分內(nèi)容很多參考《精通CSS-高級Web標(biāo)準(zhǔn)解決方案》以及參考來源

前面提到了整體的渲染概念拗引,但實際上文檔樹中的元素是按什么渲染規(guī)則渲染的,是可以進(jìn)一步展開的幌衣,此部分內(nèi)容即: CSS的可視化格式模型

先了解:

  • CSS中規(guī)定每一個元素都有自己的盒子模型(相當(dāng)于規(guī)定了這個元素如何顯示)
  • 然后可視化格式模型則是把這些盒子按照規(guī)則擺放到頁面上矾削,也就是如何布局
  • 換句話說,盒子模型規(guī)定了怎么在頁面里擺放盒子豁护,盒子的相互作用等等

說到底: CSS的可視化格式模型就是規(guī)定了瀏覽器在頁面中如何處理文檔樹

關(guān)鍵字:

包含塊(Containing Block)
控制框(Controlling Box)
BFC(Block Formatting Context)
IFC(Inline Formatting Context)
定位體系
浮動
...

另外哼凯,CSS有三種定位機(jī)制:普通流浮動楚里,絕對定位断部,如無特別提及,下文中都是針對普通流中的

包含塊(Containing Block)

一個元素的box的定位和尺寸班缎,會與某一矩形框有關(guān)蝴光,這個框就稱之為包含塊。

元素會為它的子孫元素創(chuàng)建包含塊达址,但是蔑祟,并不是說元素的包含塊就是它的父元素,元素的包含塊與它的祖先元素的樣式等有關(guān)系

譬如:

  • 根元素是最頂端的元素沉唠,它沒有父節(jié)點做瞪,它的包含塊就是初始包含塊

  • static和relative的包含塊由它最近的塊級、單元格或者行內(nèi)塊祖先元素的內(nèi)容框(content)創(chuàng)建

  • fixed的包含塊是當(dāng)前可視窗口

  • absolute的包含塊由它最近的position 屬性為absolute右冻、relative或者fixed的祖先元素創(chuàng)建

    • 如果其祖先元素是行內(nèi)元素装蓬,則包含塊取決于其祖先元素的direction特性
    • 如果祖先元素不是行內(nèi)元素,那么包含塊的區(qū)域應(yīng)該是祖先元素的內(nèi)邊距邊界

控制框(Controlling Box)

塊級元素和塊框以及行內(nèi)元素和行框的相關(guān)概念

塊框:

  • 塊級元素會生成一個塊框(Block Box),塊框會占據(jù)一整行装畅,用來包含子box和生成的內(nèi)容
  • 塊框同時也是一個塊包含框(Containing Box)伍伤,里面要么只包含塊框,要么只包含行內(nèi)框(不能混雜)暗赶,如果塊框內(nèi)部有塊級元素也有行內(nèi)元素鄙币,那么行內(nèi)元素會被匿名塊框包圍

關(guān)于匿名塊框的生成,示例:

<DIV>
Some text
<P>More text
</DIV>

div生成了一個塊框蹂随,包含了另一個塊框p以及文本內(nèi)容Some text十嘿,此時Some text文本會被強(qiáng)制加到一個匿名的塊框里面,被div生成的塊框包含(其實這個就是IFC中提到的行框岳锁,包含這些行內(nèi)框的這一行匿名塊形成的框绩衷,行框和行內(nèi)框不同)

換句話說:

如果一個塊框在其中包含另外一個塊框,那么我們強(qiáng)迫它只能包含塊框激率,因此其它文本內(nèi)容生成出來的都是匿名塊框(而不是匿名行內(nèi)框)

行內(nèi)框:

  • 一個行內(nèi)元素生成一個行內(nèi)框
  • 行內(nèi)元素能排在一行咳燕,允許左右有其它元素

關(guān)于匿名行內(nèi)框的生成,示例:

<P>Some <EM>emphasized</EM> text</P>

P元素生成一個塊框乒躺,其中有幾個行內(nèi)框(如EM)招盲,以及文本Sometext嘉冒,此時會專門為這些文本生成匿名行內(nèi)框

display屬性的影響

display的幾個屬性也可以影響不同框的生成:

  • block曹货,元素生成一個塊框
  • inline,元素產(chǎn)生一個或多個的行內(nèi)框
  • inline-block讳推,元素產(chǎn)生一個行內(nèi)級塊框控乾,行內(nèi)塊框的內(nèi)部會被當(dāng)作塊塊來格式化,而此元素本身會被當(dāng)作行內(nèi)級框來格式化(這也是為什么會產(chǎn)生BFC
  • none娜遵,不生成框蜕衡,不再格式化結(jié)構(gòu)中,當(dāng)然了设拟,另一個visibility: hidden則會產(chǎn)生一個不可見的框

總結(jié):

  • 如果一個框里慨仿,有一個塊級元素,那么這個框里的內(nèi)容都會被當(dāng)作塊框來進(jìn)行格式化纳胧,因為只要出現(xiàn)了塊級元素镰吆,就會將里面的內(nèi)容分塊幾塊,每一塊獨占一行(出現(xiàn)行內(nèi)可以用匿名塊框解決)
  • 如果一個框里跑慕,沒有任何塊級元素万皿,那么這個框里的內(nèi)容會被當(dāng)成行內(nèi)框來格式化,因為里面的內(nèi)容是按照順序成行的排列

BFC(Block Formatting Context)

FC(格式上下文)核行?

FC即格式上下文牢硅,它定義框內(nèi)部的元素渲染規(guī)則,比較抽象芝雪,譬如

FC像是一個大箱子减余,里面裝有很多元素

箱子可以隔開里面的元素和外面的元素(所以外部并不會影響FC內(nèi)部的渲染)

內(nèi)部的規(guī)則可以是:如何定位,寬高計算惩系,margin折疊等等

不同類型的框參與的FC類型不同位岔,譬如塊級框?qū)?yīng)BFC如筛,行內(nèi)框?qū)?yīng)IFC

注意,并不是說所有的框都會產(chǎn)生FC抒抬,而是符合特定條件才會產(chǎn)生杨刨,只有產(chǎn)生了對應(yīng)的FC后才會應(yīng)用對應(yīng)渲染規(guī)則

BFC規(guī)則:

在塊格式化上下文中

每一個元素左外邊與包含塊的左邊相接觸(對于從右到左的格式化,右外邊接觸右邊)

即使存在浮動也是如此(所以浮動元素正常會直接貼近它的包含塊的左邊擦剑,與普通元素重合)

除非這個元素也創(chuàng)建了一個新的BFC

總結(jié)幾點BFC特點:

  1. 內(nèi)部box在垂直方向妖胀,一個接一個的放置
  2. box的垂直方向由margin決定,屬于同一個BFC的兩個box間的margin會重疊
  3. BFC區(qū)域不會與float box重疊(可用于排版)
  4. BFC就是頁面上的一個隔離的獨立容器抓于,容器里面的子元素不會影響到外面的元素。反之也如此
  5. 計算BFC的高度時浇借,浮動元素也參與計算(不會浮動坍塌)

如何觸發(fā)BFC捉撮?

  1. 根元素
  2. float屬性不為none
  3. positionabsolutefixed
  4. displayinline-block, flex, inline-flextable妇垢,table-cell巾遭,table-caption
  5. overflow不為visible

這里提下,display: table闯估,它本身不產(chǎn)生BFC灼舍,但是它會產(chǎn)生匿名框(包含display: table-cell的框),而這個匿名框產(chǎn)生BFC

更多請自行網(wǎng)上搜索

IFC(Inline Formatting Context)

IFC即行內(nèi)框產(chǎn)生的格式上下文

IFC規(guī)則

在行內(nèi)格式化上下文中

框一個接一個地水平排列涨薪,起點是包含塊的頂部骑素。

水平方向上的 margin,border 和 padding 在框之間得到保留

框在垂直方向上可以以不同的方式對齊:它們的頂部或底部對齊刚夺,或根據(jù)其中文字的基線對齊

行框

包含那些框的長方形區(qū)域献丑,會形成一行,叫做行框

行框的寬度由它的包含塊和其中的浮動元素決定侠姑,高度的確定由行高度計算規(guī)則決定

行框的規(guī)則:

如果幾個行內(nèi)框在水平方向無法放入一個行框內(nèi)创橄,它們可以分配在兩個或多個垂直堆疊的行框中(即行內(nèi)框的分割)

行框在堆疊時沒有垂直方向上的分割且永不重疊

行框的高度總是足夠容納所包含的所有框。不過莽红,它可能高于它包含的最高的框(例如妥畏,框?qū)R會引起基線對齊)

行框的左邊接觸到其包含塊的左邊,右邊接觸到其包含塊的右邊安吁。

結(jié)合補(bǔ)充下IFC規(guī)則:

浮動元素可能會處于包含塊邊緣和行框邊緣之間

盡管在相同的行內(nèi)格式化上下文中的行框通常擁有相同的寬度(包含塊的寬度)醉蚁,它們可能會因浮動元素縮短了可用寬度,而在寬度上發(fā)生變化

同一行內(nèi)格式化上下文中的行框通常高度不一樣(如鬼店,一行包含了一個高的圖形馍管,而其它行只包含文本)

當(dāng)一行中行內(nèi)框?qū)挾鹊目偤托∮诎鼈兊男锌虻膶挘鼈冊谒椒较蛏系膶R薪韩,取決于 `text-align` 特性

空的行內(nèi)框應(yīng)該被忽略

即不包含文本确沸,保留空白符捌锭,margin/padding/border非0的行內(nèi)元素,
以及其他常規(guī)流中的內(nèi)容(比如罗捎,圖片观谦,inline blocks 和 inline tables),
并且不是以換行結(jié)束的行框桨菜,
必須被當(dāng)作零高度行框?qū)Υ?

總結(jié):

  • 行內(nèi)元素總是會應(yīng)用IFC渲染規(guī)則
  • 行內(nèi)元素會應(yīng)用IFC規(guī)則渲染豁状,譬如text-align可以用來居中等
  • 塊框內(nèi)部,對于文本這類的匿名元素倒得,會產(chǎn)生匿名行框包圍泻红,而行框內(nèi)部就應(yīng)用IFC渲染規(guī)則
  • 行內(nèi)框內(nèi)部,對于那些行內(nèi)元素霞掺,一樣應(yīng)用IFC渲染規(guī)則
  • 另外谊路,inline-block,會在元素外層產(chǎn)生IFC(所以這個元素是可以通過text-align水平居中的)菩彬,當(dāng)然缠劝,它內(nèi)部則按照BFC規(guī)則渲染

相比BFC規(guī)則來說,IFC可能更加抽象(因為沒有那么條理清晰的規(guī)則和觸發(fā)條件)

但總的來說骗灶,它就是行內(nèi)元素自身如何顯示以及在框內(nèi)如何擺放的渲染規(guī)則惨恭,這樣描述應(yīng)該更容易理解

其它

當(dāng)然還有有一些其它內(nèi)容:

  • 譬如常規(guī)流,浮動耙旦,絕對定位等區(qū)別
  • 譬如浮動元素不包含在常規(guī)流中
  • 譬如相對定位脱羡,絕對定位,Fixed定位等區(qū)別
  • 譬如z-index的分層顯示機(jī)制等

這里不一一展開免都,更多請參考:

http://bbs.csdn.net/topics/340204423

JS引擎解析過程

前面有提到遇到JS腳本時轻黑,會等到它的執(zhí)行,實際上是需要引擎解析的琴昆,這里展開描述(介紹主干流程)

JS的解釋階段

首先得明確: JS是解釋型語音氓鄙,所以它無需提前編譯,而是由解釋器實時運行

引擎對JS的處理過程可以簡述如下:

1. 讀取代碼业舍,進(jìn)行詞法分析(Lexical analysis)抖拦,然后將代碼分解成詞元(token)

2. 對詞元進(jìn)行語法分析(parsing),然后將代碼整理成語法樹(syntax tree)

3. 使用翻譯器(translator)舷暮,將代碼轉(zhuǎn)為字節(jié)碼(bytecode)

4. 使用字節(jié)碼解釋器(bytecode interpreter)态罪,將字節(jié)碼轉(zhuǎn)為機(jī)器碼

最終計算機(jī)執(zhí)行的就是機(jī)器碼。

為了提高運行速度下面,現(xiàn)代瀏覽器一般采用即時編譯(JIT-Just In Time compiler

即字節(jié)碼只在運行時編譯复颈,用到哪一行就編譯哪一行,并且把編譯結(jié)果緩存(inline cache

這樣整個程序的運行速度能得到顯著提升沥割。

而且耗啦,不同瀏覽器策略可能還不同凿菩,有的瀏覽器就省略了字節(jié)碼的翻譯步驟,直接轉(zhuǎn)為機(jī)器碼(如chrome的v8)

總結(jié)起來可以認(rèn)為是: 核心的JIT編譯器將源碼編譯成機(jī)器碼運行

JS的預(yù)處理階段

上述將的是解釋器的整體過程帜讲,這里再提下在正式執(zhí)行JS前衅谷,還會有一個預(yù)處理階段
(譬如變量提升,分號補(bǔ)全等)

預(yù)處理階段會做一些事情似将,確保JS可以正確執(zhí)行获黔,這里僅提部分:

分號補(bǔ)全

JS執(zhí)行是需要分號的,但為什么以下語句卻可以正常運行呢在验?

console.log('a')
console.log('b')

原因就是JS解釋器有一個Semicolon Insertion規(guī)則玷氏,它會按照一定規(guī)則,在適當(dāng)?shù)奈恢醚a(bǔ)充分號

譬如列舉幾條自動加分號的規(guī)則:

  • 當(dāng)有換行符(包括含有換行符的多行注釋)腋舌,并且下一個token沒法跟前面的語法匹配時盏触,會自動補(bǔ)分號。
  • 當(dāng)有}時侦厚,如果缺少分號耻陕,會補(bǔ)分號拙徽。
  • 程序源代碼結(jié)束時刨沦,如果缺少分號,會補(bǔ)分號膘怕。

于是想诅,上述的代碼就變成了

console.log('a');
console.log('b');

所以可以正常運行

當(dāng)然了,這里有一個經(jīng)典的例子:

function b() {
    return
    {
        a: 'a'
    };
}

由于分號補(bǔ)全機(jī)制岛心,所以它變成了:

function b() {
    return;
    {
        a: 'a'
    };
}

所以運行后是undefined

變量提升

一般包括函數(shù)提升和變量提升

譬如:

a = 1;
b();
function b() {
    console.log('b');
}
var a;

經(jīng)過變量提升后来破,就變成:

function b() {
    console.log('b');
}
var a;
a = 1;
b();

這里沒有展開,其實展開也可以牽涉到很多內(nèi)容的

譬如可以提下變量聲明忘古,函數(shù)聲明徘禁,形參,實參的優(yōu)先級順序髓堪,以及es6中l(wèi)et有關(guān)的臨時死區(qū)等

JS的執(zhí)行階段

此階段的內(nèi)容中的圖片來源:深入理解JavaScript系列(10):JavaScript核心(晉級高手必讀篇)

解釋器解釋完語法規(guī)則后送朱,就開始執(zhí)行,然后整個執(zhí)行流程中大致包含以下概念:

  • 執(zhí)行上下文干旁,執(zhí)行堆棧概念(如全局上下文驶沼,當(dāng)前活動上下文)
  • VO(變量對象)和AO(活動對象)
  • 作用域鏈
  • this機(jī)制等

這些概念如果深入講解的話內(nèi)容過多,因此這里僅提及部分特性

執(zhí)行上下文簡單解釋

  • JS有執(zhí)行上下文
  • 瀏覽器首次載入腳本争群,它將創(chuàng)建全局執(zhí)行上下文回怜,并壓入執(zhí)行棧棧頂(不可被彈出)
  • 然后每進(jìn)入其它作用域就創(chuàng)建對應(yīng)的執(zhí)行上下文并把它壓入執(zhí)行棧的頂部
  • 一旦對應(yīng)的上下文執(zhí)行完畢,就從棧頂彈出换薄,并將上下文控制權(quán)交給當(dāng)前的棧玉雾。
  • 這樣依次執(zhí)行(最終都會回到全局執(zhí)行上下文)

譬如翔试,如果程序執(zhí)行完畢,被彈出執(zhí)行棧抹凳,然后有沒有被引用(沒有形成閉包)遏餐,那么這個函數(shù)中用到的內(nèi)存就會被垃圾處理器自動回收

[圖片上傳失敗...(image-5d1ae6-1611625173082)]

然后執(zhí)行上下文與VO,作用域鏈赢底,this的關(guān)系是:

每一個執(zhí)行上下文失都,都有三個重要屬性:

  • 變量對象(Variable object,VO)
  • 作用域鏈(Scope chain)
  • this

[圖片上傳失敗...(image-32b686-1611625173082)]

VO與AO

VO是執(zhí)行上下文的屬性(抽象概念)幸冻,但是只有全局上下文的變量對象允許通過VO的屬性名稱來間接訪問(因為在全局上下文里粹庞,全局對象自身就是變量對象)

AO(activation object),當(dāng)函數(shù)被調(diào)用者激活洽损,AO就被創(chuàng)建了

可以理解為:

  • 在函數(shù)上下文中:VO === AO
  • 在全局上下文中:VO === this === global

總的來說庞溜,VO中會存放一些變量信息(如聲明的變量,函數(shù)碑定,arguments參數(shù)等等)

作用域鏈

它是執(zhí)行上下文中的一個屬性流码,原理和原型鏈很相似,作用很重要延刘。

譬如流程簡述:

在函數(shù)上下文中漫试,查找一個變量foo

如果函數(shù)的VO中找到了,就直接使用

否則去它的父級作用域鏈中(__parent__)找

如果父級中沒找到碘赖,繼續(xù)往上找

直到全局上下文中也沒找到就報錯

[圖片上傳失敗...(image-416ea0-1611625173082)]

this指針

這也是JS的核心知識之一驾荣,由于內(nèi)容過多,這里就不展開普泡,僅提及部分

注意:this是執(zhí)行上下文環(huán)境的一個屬性播掷,而不是某個變量對象的屬性

因此:

  • this是沒有一個類似搜尋變量的過程
  • 當(dāng)代碼中使用了this,這個 this的值就直接從執(zhí)行的上下文中獲取了撼班,而不會從作用域鏈中搜尋
  • this的值只取決中進(jìn)入上下文時的情況

所以經(jīng)典的例子:

var baz = 200;
var bar = {
    baz: 100,
    foo: function() {
        console.log(this.baz);
    }
};
var foo = bar.foo;

// 進(jìn)入環(huán)境:global
foo(); // 200歧匈,嚴(yán)格模式中會報錯,Cannot read property 'baz' of undefined

// 進(jìn)入環(huán)境:global bar
bar.foo(); // 100

就要明白了上面this的介紹砰嘁,上述例子很好理解

更多參考:

深入理解JavaScript系列(13):This? Yes,this!

回收機(jī)制

JS有垃圾處理器件炉,所以無需手動回收內(nèi)存,而是由垃圾處理器自動處理般码。

一般來說妻率,垃圾處理器有自己的回收策略。

譬如對于那些執(zhí)行完畢的函數(shù)板祝,如果沒有外部引用(被引用的話會形成閉包)宫静,則會回收。(當(dāng)然一般會把回收動作切割到不同的時間段執(zhí)行,防止影響性能)

常用的兩種垃圾回收規(guī)則是:

  • 標(biāo)記清除
  • 引用計數(shù)

Javascript引擎基礎(chǔ)GC方案是(simple GC):mark and sweep(標(biāo)記清除)孤里,簡單解釋如下:

  1. 遍歷所有可訪問的對象伏伯。
  2. 回收已不可訪問的對象。

譬如:(出自javascript高程)

當(dāng)變量進(jìn)入環(huán)境時捌袜,例如说搅,在函數(shù)中聲明一個變量,就將這個變量標(biāo)記為“進(jìn)入環(huán)境”虏等。

從邏輯上講弄唧,永遠(yuǎn)不能釋放進(jìn)入環(huán)境的變量所占用的內(nèi)存,因為只要執(zhí)行流進(jìn)入相應(yīng)的環(huán)境霍衫,就可能會用到它們候引。

而當(dāng)變量離開環(huán)境時,則將其標(biāo)記為“離開環(huán)境”敦跌。

垃圾回收器在運行的時候會給存儲在內(nèi)存中的所有變量都加上標(biāo)記(當(dāng)然澄干,可以使用任何標(biāo)記方式)。

然后柠傍,它會去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記(閉包麸俘,也就是說在環(huán)境中的以及相關(guān)引用的變量會被去除標(biāo)記)。

而在此之后再被加上標(biāo)記的變量將被視為準(zhǔn)備刪除的變量惧笛,原因是環(huán)境中的變量已經(jīng)無法訪問到這些變量了从媚。

最后,垃圾回收器完成內(nèi)存清除工作徐紧,銷毀那些帶標(biāo)記的值并回收它們所占用的內(nèi)存空間静檬。

關(guān)于引用計數(shù)炭懊,簡單點理解:

跟蹤記錄每個值被引用的次數(shù)并级,當(dāng)一個值被引用時,次數(shù)+1侮腹,減持時-1嘲碧,下次垃圾回收器會回收次數(shù)為0的值的內(nèi)存(當(dāng)然了,容易出循環(huán)引用的bug)

GC的缺陷

和其他語言一樣父阻,javascript的GC策略也無法避免一個問題: GC時愈涩,停止響應(yīng)其他操作

這是為了安全考慮。

而Javascript的GC在100ms甚至以上

對一般的應(yīng)用還好加矛,但對于JS游戲履婉,動畫對連貫性要求比較高的應(yīng)用,就麻煩了斟览。

這就是引擎需要優(yōu)化的點: 避免GC造成的長時間停止響應(yīng)毁腿。

GC優(yōu)化策略

這里介紹常用到的:分代回收(Generation GC)

目的是通過區(qū)分“臨時”與“持久”對象:

  • 多回收“臨時對象”區(qū)(young generation
  • 少回收“持久對象”區(qū)(tenured generation
  • 減少每次需遍歷的對象,從而減少每次GC的耗時。

像node v8引擎就是采用的分代回收(和java一樣已烤,作者是java虛擬機(jī)作者鸠窗。)

更多可以參考:

V8 內(nèi)存淺析

其它

可以提到跨域

譬如發(fā)出網(wǎng)絡(luò)請求時,會用AJAX胯究,如果接口跨域稍计,就會遇到跨域問題

可以參考:

ajax跨域,這應(yīng)該是最全的解決方案了

可以提到web安全

譬如瀏覽器在解析HTML時裕循,有XSSAuditor臣嚣,可以延伸到web安全相關(guān)領(lǐng)域

可以參考:

AJAX請求真的不安全么?談?wù)刉eb安全與AJAX的關(guān)系剥哑。

更多

如可以提到viewport概念茧球,講講物理像素,邏輯像素星持,CSS像素等概念

如熟悉Hybrid開發(fā)的話可以提及一下Hybrid相關(guān)內(nèi)容以及優(yōu)化

...

總結(jié)

上述這么多內(nèi)容抢埋,目的是:梳理出自己的知識體系

本文由于是前端向,所以知識梳理時有重點督暂,很多其它的知識點都簡述或略去了揪垄,重點介紹的模塊總結(jié):

  • 瀏覽器的進(jìn)程/線程模型、JS運行機(jī)制(這一塊的詳細(xì)介紹鏈接到了另一篇文章)
  • http規(guī)范(包括報文結(jié)構(gòu)逻翁,頭部饥努,優(yōu)化,http2.0八回,https等)
  • http緩存(單獨列出來酷愧,因為它很重要)
  • 頁面解析流程(HTML解析,構(gòu)建DOM缠诅,生成CSS規(guī)則溶浴,構(gòu)建渲染樹,渲染流程管引,復(fù)合層的合成士败,外鏈的處理等)
  • JS引擎解析過程(包括解釋階段,預(yù)處理階段褥伴,執(zhí)行階段谅将,包括執(zhí)行上下文、VO重慢、作用域鏈饥臂、this、回收機(jī)制等)
  • 跨域相關(guān)似踱,web安全單獨鏈接到了具體文章隅熙,其它如CSS盒模型志衣,viewport等僅是提及概念
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市猛们,隨后出現(xiàn)的幾起案子念脯,更是在濱河造成了極大的恐慌,老刑警劉巖弯淘,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绿店,死亡現(xiàn)場離奇詭異,居然都是意外死亡庐橙,警方通過查閱死者的電腦和手機(jī)假勿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來态鳖,“玉大人转培,你說我怎么就攤上這事〗撸” “怎么了浸须?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長邦泄。 經(jīng)常有香客問我删窒,道長,這世上最難降的妖魔是什么顺囊? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任肌索,我火速辦了婚禮,結(jié)果婚禮上特碳,老公的妹妹穿的比我還像新娘诚亚。我一直安慰自己,他們只是感情好午乓,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布站宗。 她就那樣靜靜地躺著,像睡著了一般硅瞧。 火紅的嫁衣襯著肌膚如雪份乒。 梳的紋絲不亂的頭發(fā)上恕汇,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天腕唧,我揣著相機(jī)與錄音,去河邊找鬼瘾英。 笑死枣接,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缺谴。 我是一名探鬼主播但惶,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膀曾?” 一聲冷哼從身側(cè)響起县爬,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎添谊,沒想到半個月后财喳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡斩狱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年耳高,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片所踊。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡泌枪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秕岛,到底是詐尸還是另有隱情碌燕,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布继薛,位于F島的核電站陆蟆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惋增。R本人自食惡果不足惜叠殷,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诈皿。 院中可真熱鬧林束,春花似錦、人聲如沸稽亏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽截歉。三九已至胖腾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘪松,已是汗流浹背咸作。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留宵睦,地道東北人记罚。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像壳嚎,于是被迫代替她去往敵國和親桐智。 傳聞我的和親對象是個殘疾皇子末早,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容