對知識體系進(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ā)回流的操作,如offsetXXX
,scrollXXX
咕村,clientXXX
场钉,currentStyle
等等
再舉一個例子:
-
visibility: hidden
和display: 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請求時,瀏覽器會把
headers
和data
一起發(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-Since
、If-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.1
和http1.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-Since
和Last-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-Match
和E-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-Control
與Expires
麸恍,Cache-Control
優(yōu)先級高。
E-tag相比Last-Modified光稼?
Last-Modified
:
- 表明服務(wù)端的文件最后何時改變的
- 它有一個缺陷就是只能精確到1s或南,
- 然后還有一個問題就是有的服務(wù)端的文件會周期性的改變,導(dǎo)致緩存失效
而E-tag
:
- 是一種指紋機(jī)制艾君,代表文件相關(guān)指紋
- 只有文件變才會變采够,也只要文件變就會變,
- 也沒有精確時間的限制冰垄,只要文件一遍蹬癌,立馬E-tag就不一樣了
如果同時帶有E-tag
和Last-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ù)合層是獨立的遗座,所以一般做動畫推薦使用硬件加速
更多參考:
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)邊距邊界
- 如果其祖先元素是行內(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
)招盲,以及文本Some
,text
嘉冒,此時會專門為這些文本生成匿名行內(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特點:
- 內(nèi)部
box
在垂直方向妖胀,一個接一個的放置 - box的垂直方向由
margin
決定,屬于同一個BFC的兩個box間的margin會重疊 - BFC區(qū)域不會與
float box
重疊(可用于排版) - BFC就是頁面上的一個隔離的獨立容器抓于,容器里面的子元素不會影響到外面的元素。反之也如此
- 計算BFC的高度時浇借,浮動元素也參與計算(不會浮動坍塌)
如何觸發(fā)BFC捉撮?
- 根元素
-
float
屬性不為none
-
position
為absolute
或fixed
-
display
為inline-block
,flex
,inline-flex
,table
妇垢,table-cell
巾遭,table-caption
-
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)記清除)孤里,簡單解釋如下:
- 遍歷所有可訪問的對象伏伯。
- 回收已不可訪問的對象。
譬如:(出自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ī)作者鸠窗。)
更多可以參考:
其它
可以提到跨域
譬如發(fā)出網(wǎng)絡(luò)請求時,會用AJAX胯究,如果接口跨域稍计,就會遇到跨域問題
可以參考:
可以提到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等僅是提及概念