成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

從輸入URL到頁面加載的過程?如何由一道題完善自己的前端知識體系!

kel / 2164人閱讀

摘要:前言見解有限,如有描述不當(dāng)之處,請幫忙指出,如有錯誤,會及時修正。為什么要梳理這篇文章最近恰好被問到這方面的問題,嘗試整理后發(fā)現(xiàn),這道題的覆蓋面可以非常廣,很適合作為一道承載知識體系的題目。

前言

見解有限,如有描述不當(dāng)之處,請幫忙指出,如有錯誤,會及時修正。

為什么要梳理這篇文章?

最近恰好被問到這方面的問題,嘗試整理后發(fā)現(xiàn),這道題的覆蓋面可以非常廣,很適合作為一道承載知識體系的題目。

關(guān)于這道題目的吐槽暫且不提(這是一道被提到無數(shù)次的題,得到不少人的贊同,也被很多人反感),本文的目的是如何借助這道題梳理自己的前端知識體系!

竊認為,每一個前端人員,如果要往更高階發(fā)展,必然會將自己的知識體系梳理一遍,沒有牢固的知識體系,無法往更高處走!

展現(xiàn)形式:本文并不是將所有的知識點列一遍,而是偏向于分析+梳理

內(nèi)容:在本文中只會梳理一些比較重要的前端向知識點,其它的可能會被省略

目標(biāo):本文的目標(biāo)是梳理一個較為完整的前端向知識體系

本文是個人階段性梳理知識體系的成果,然后加以修繕后發(fā)布成文章,因此并不確保適用于所有人員,但是,個人認為本文還是有一定參考價值的

另外,如有不同見解,可以一起討論

----------超長文預(yù)警,需要花費大量時間。----------

本文適合有一定經(jīng)驗的前端人員,新手請規(guī)避。

本文內(nèi)容超多,建議先了解主干,然后分成多批次閱讀。

本文是前端向,以前端領(lǐng)域的知識為重點

大綱

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

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

梳理主干流程

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

多進程的瀏覽器

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

解析URL

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

更多

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

DNS查詢得到IP

tcp/ip請求

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

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

負載均衡

后臺的處理

后臺和前臺的http交互

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

cookie以及優(yōu)化

gzip壓縮

長連接與短連接

http 2.0

https

多帶帶拎出來的緩存問題,http的緩存

強緩存與弱緩存

緩存頭部簡述

頭部的區(qū)別

解析頁面流程

流程簡述

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

生成CSS規(guī)則

構(gòu)建渲染樹

渲染

簡單層與復(fù)合層

Chrome中的調(diào)試

資源外鏈的下載

loaded和domcontentloaded

CSS的可視化格式模型

包含塊(Containing Block)

控制框(Controlling Box)

BFC(Block Formatting Context)

IFC(Inline Formatting Context)

其它

JS引擎解析過程

JS的解釋階段

JS的預(yù)處理階段

JS的執(zhí)行階段

回收機制

其它

總結(jié)

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

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

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

level1:

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

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

這類人員一般都是:

萌新(剛接觸前端的,包括0-6個月都有可能有這種回答)

沉淀人員(就是那種可能已經(jīng)接觸了前端幾年,但是仍然處于初級階段的那種。。。)

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

level2:

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

知道瀏覽器輸入url后會有http請求這個概念

有后臺這個概念,大致知道前后端的交互,知道前后端只要靠http報文通信

知道瀏覽器接收到數(shù)據(jù)后會進行解析,有一定概念,但是具體流程不熟悉(如render樹構(gòu)建流程,layout、paint,復(fù)合層與簡單層,常用優(yōu)化方案等不是很熟悉)

對于js引擎的解析流程有一定概念,但是細節(jié)不熟悉(如具體的形參,函數(shù),變量提升,執(zhí)行上下文以及VO、AO、作用域鏈,回收機制等概念不是很熟悉)

如可能知道一些http規(guī)范初步概念,但是不熟悉(如http報文結(jié)構(gòu),常用頭部,緩存機制,http2.0,https等特性,跨域與web安全等不是很熟悉)

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

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

這類人員一般都是:

工作1-3年左右的普通人員(占大多數(shù),而且大多數(shù)人員工作3年左右并沒有實質(zhì)上的提升)

工作3年以上的老人(這部分人大多都業(yè)務(wù)十分嫻熟,一個當(dāng)好幾個用,但是,基礎(chǔ)比較薄弱,可能沒有嘗試寫過框架、組件、腳手架等)

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

level3:

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

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

首先略去那些鍵盤輸入、和操作系統(tǒng)交互、以及屏幕顯示原理、網(wǎng)卡等硬件交互之類的(前端向中,很多硬件原理暫時略去。。。)

對瀏覽器模型有整體概念,知道瀏覽器是多進程的,瀏覽器內(nèi)核是多線程的,清楚進程與線程之間得區(qū)別,以及輸入url后會開一個新的網(wǎng)絡(luò)線程

對從開啟網(wǎng)絡(luò)線程到發(fā)出一個完整的http請求中間的過程有所了解(如dns查詢,tcp/ip鏈接,五層因特網(wǎng)協(xié)議棧等等,以及一些優(yōu)化方案,如dns-prefetch

對從服務(wù)器接收到請求到對應(yīng)后臺接收到請求有一定了解(如負載均衡,安全攔截以及后臺代碼處理等)

對后臺和前臺的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īng)如此之多的概念了,所以,個人認為如果沒有自己的見解,沒有形成自己的知識體系,僅僅是看看,背背是沒用的,過一段時間就會忘光了。

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

工作2年以上的前端(基本上如果按正常進度的話,至少接觸前端兩年左右才會開始走向高階,當(dāng)然,現(xiàn)在很多都是上學(xué)時就開始學(xué)了的,還有部分是天賦異稟,不好預(yù)估。。。)

或者是已經(jīng)十分熟悉其它某門語言,再轉(zhuǎn)前端的人(基本上是很快就可以將前端水準(zhǔn)提升上去)

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

level4:

由于本人層次尚未達到,所以大致說下自己的見解吧。

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

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

從鍵盤談起到系統(tǒng)交互,從瀏覽器到CPU,從調(diào)度機制到系統(tǒng)內(nèi)核,從數(shù)據(jù)請求到二進制、匯編,從GPU繪圖到LCD顯示,然后再分析系統(tǒng)底層的進程、內(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)用

詳細版本: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é)點的一些信息進行計算(譬如寬高等),所以用的時候慎用,回流會引起性能問題。然后合適的話會將話題引導(dǎo)回流,重繪,瀏覽器渲染原理等等。當(dāng)然也可以列舉一些其它會引發(fā)回流的操作,如offsetXXX,scrollXXX,clientXXX,currentStyle等等

再舉一個例子:

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

可以如下回答:

普通回答,一個隱藏,但占據(jù)位置,一個隱藏,不占據(jù)位置

進一步,display由于隱藏后不占據(jù)位置,所以造成了dom樹的改變,會引發(fā)回流,代價較大

再進一步,當(dāng)一個頁面某個元素經(jīng)常需要切換display時如何優(yōu)化,一般會用復(fù)合層優(yōu)化,或者要求低一點用absolute讓其脫離普通文檔流也行。然后可以將話題引到普通文檔流,absolute文檔流,復(fù)合圖層的區(qū)別,

再進一步可以描述下瀏覽器渲染原理以及復(fù)合圖層和普通圖層的繪制區(qū)別(復(fù)合圖層多帶帶分配資源,獨立繪制,性能提升,但是不能過多,還有隱式合成等等)

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

前端向知識的重點

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

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

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

核心知識,必須掌握的,也是最基礎(chǔ)的,譬如瀏覽器模型,渲染原理,JS解析過程,JS運行機制等,作為骨架來承載知識體系

重點知識,往往每一塊都是一個知識點,而且這些知識點都很重要,譬如http相關(guān),web安全相關(guān),跨域處理等

拓展知識,這一塊可能更多的是了解,稍微實踐過,但是認識上可能沒有上面那么深刻,譬如五層因特網(wǎng)協(xié)議棧,hybrid模式,移動原生開發(fā),后臺相關(guān)等等(當(dāng)然,在不同領(lǐng)域,可能有某些知識就上升到重點知識層次了,譬如hybrid開發(fā)時,懂原生開發(fā)是很重要的)

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

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

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

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

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

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

梳理主干流程

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

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

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

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

3. 從服務(wù)器接收到請求到對應(yīng)后臺接收到請求(這一部分可能涉及到負載均衡,安全攔截以及后臺內(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,作用域鏈、回收機制等等)

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

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

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

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

多進程的瀏覽器

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

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

Browser進程:瀏覽器的主進程(負責(zé)協(xié)調(diào)、主控),只有一個

第三方插件進程:每種類型的插件對應(yīng)一個進程,僅當(dāng)使用該插件時才創(chuàng)建

GPU進程:最多一個,用于3D繪制

瀏覽器渲染進程(內(nèi)核):默認每個Tab頁面一個進程,互不影響,控制頁面渲染,腳本執(zhí)行,事件處理等(有時候會優(yōu)化,如多個空白tab會合并成一個進程)

如下圖:

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

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

GUI線程

JS引擎線程

事件觸發(fā)線程

定時器線程

網(wǎng)絡(luò)請求線程

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

解析URL

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

URL一般包括幾大部分:

protocol,協(xié)議頭,譬如有http,ftp等

host,主機域名或IP地址

port,端口號

path,目錄路徑

query,即查詢參數(shù)

fragment,即#后的hash值,一般用來定位到某個位置

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

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

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

更多

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

從瀏覽器多進程到JS單線程,JS運行機制最全面的一次梳理

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

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

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

DNS查詢得到IP

如果輸入的是域名,需要進行dns解析成IP,大致流程:

如果瀏覽器有緩存,直接使用瀏覽器緩存,否則使用本機緩存,再沒有的話就是用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)上可以看到很詳細的解答)

tcp/ip請求

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

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

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

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

客戶端:hello,你是server么?
服務(wù)端:hello,我是server,你是client么
客戶端:yes,我是client

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

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

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

主動方:我已經(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)換等

會話層:它具體管理不同用戶和進程之間的對話,如控制登陸和注銷過程
從服務(wù)器接收到請求到對應(yīng)后臺接收到請求

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

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

負載均衡

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

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

簡單的說:

用戶發(fā)起的請求都指向調(diào)度服務(wù)器(反向代理服務(wù)器,譬如安裝了nginx控制負載均衡),然后調(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)驗證通過后,才會進入實際的后臺代碼,此時是程序接收到請求,然后執(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: 請求的遠程服務(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——重定向,要完成請求必須進行更進一步的操作
4xx——客戶端錯誤,請求有語法錯誤或請求無法實現(xiàn)
5xx——服務(wù)器端錯誤,服務(wù)器未能實現(xiàn)合法的請求

總之,當(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)遵循的緩存機制,如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ù)器通信時對于長連接如何進行處理,如keep-alive
Host:請求的服務(wù)器URL
Origin:最初的請求是從哪里發(fā)起的(只會精確到端口),Origin比Referer更尊重隱私
Referer:該頁面的來源URL(適用于所有類型的請求,會精確到詳細頁面地址,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)該在什么時候認為文檔已經(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ù)都放入進入(用于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)的簡要分析

cookie以及優(yōu)化

cookie是瀏覽器的一種本地存儲方式,一般用來幫助客戶端和服務(wù)端通信的,常用來進行身份校驗,結(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是不允許存放敏感信息的(千萬不要明文存儲用戶名、密碼),因為非常不安全,如果一定要強行存儲,首先,一定要在cookie中設(shè)置httponly(這樣就無法通過js操作了),另外可以考慮rsa等非對稱加密(因為實際上,瀏覽器本地也是容易被攻克的,并不安全)

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

譬如以下場景:

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

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

此時就有一個問題,頁面加載,請求這些靜態(tài)資源時,瀏覽器會默認帶上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é)

gzip壓縮

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

然后gzip一般是由apachetomcat等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中,默認使用的是短連接,也就是說,瀏覽器沒進行一次http操作,就建立一次連接,任務(wù)結(jié)束就中斷連接,譬如每一個靜態(tài)資源請求時都是一個多帶帶的連接

http1.1起,默認使用長連接,使用長連接會有這一行Connection: keep-alive,在長連接的情況下,當(dāng)一個網(wǎng)頁打開完成后,客戶端和服務(wù)端之間用于傳輸http的tcp連接不會關(guān)閉,如果客戶端再次訪問這個服務(wù)器的頁面,會繼續(xù)使用這一條已經(jīng)建立的連接

注意: keep-alive不會永遠保持,它有一個持續(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頭部壓縮,減少體積)

二進制分幀(在應(yīng)用層跟傳送層之間增加了一個二進制分幀層,改進傳輸性能,實現(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ā)送一個隨機數(shù)–Client random和客戶端支持的加密方法,比如RSA加密,此時是明文傳輸。 

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

3. 瀏覽器收到服務(wù)端的證書后
    
    - 驗證證書的合法性(頒發(fā)機構(gòu)是否合法,證書中包含的網(wǎng)址是否和正在訪問的一樣),如果證書信任,則瀏覽器會顯示一個小鎖頭,否則會有提示
    
    - 用戶接收證書后(不管信不信任),瀏覽會生產(chǎn)新的隨機數(shù)–Premaster secret,然后證書中的公鑰以及指定的加密方法加密`Premaster secret`,發(fā)送給服務(wù)器。
    
    - 利用Client random、Server random和Premaster secret通過一定的算法生成HTTP鏈接數(shù)據(jù)傳輸?shù)膶ΨQ加密key-`session key`
    
    - 使用約定好的HASH算法計算握手消息,并使用生成的`session key`對消息進行加密,最后將之前生成的所有信息發(fā)送給服務(wù)端。 
    
4. 服務(wù)端收到瀏覽器的回復(fù)

    - 利用已知的加解密方式與自己的私鑰進行解密,獲取`Premaster secret`
    
    - 和瀏覽器相同規(guī)則生成`session key`
    
    - 使用`session key`解密瀏覽器發(fā)來的握手消息,并驗證Hash是否與瀏覽器發(fā)來的一致
    
    - 使用`session key`加密一段握手消息,發(fā)送給瀏覽器
    
5. 瀏覽器解密并計算握手消息的HASH,如果與服務(wù)端發(fā)來的HASH一致,此時握手過程結(jié)束,

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

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

多帶帶拎出來的緩存問題,http的緩存

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

強緩存與弱緩存

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

區(qū)別簡述如下:

強緩存(200 from cache)時,瀏覽器如果判斷本地緩存未過期,就直接使用,無需發(fā)起http請求

協(xié)商緩存(304)時,瀏覽器會向服務(wù)端發(fā)起http請求,然后服務(wù)端告訴瀏覽器文件未改變,讓瀏覽器使用本地緩存

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

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

緩存頭部簡述

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

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

先看下這幾個頭部:

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

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

屬于強緩存控制的:

(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

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

頭部的區(qū)別

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

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

http1.0中的緩存控制:

Pragma:嚴(yán)格來說,它不屬于專門的緩存控制頭部,但是它設(shè)置no-cache時可以讓本地強緩存失效(屬于編譯控制,來實現(xiàn)特定的指令,主要是因為兼容http1.0,所以以前又被大量應(yīng)用)

Expires:服務(wù)端配置的,屬于強緩存,用來控制在規(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ù)端配置的,用來控制強緩存,在規(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

是一種指紋機制,代表文件相關(guān)指紋

只有文件變才會變,也只要文件變就會變,

也沒有精確時間的限制,只要文件一遍,立馬E-tag就不一樣了

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

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

解析頁面流程

前面有提到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),負責(zé)各元素尺寸、位置的計算

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

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

如下圖:

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)容出自參考來源,修改了下格式)


  
    
    
    Critical Path
  
  
    

Hello web performance students!

瀏覽器的處理如下:

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

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樹如下:

生成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樹就是:

構(gòu)建渲染樹

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

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

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

整體來說可以看圖:

渲染

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

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

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ù)合層和簡單層的概念來講。

這里不展開,進簡單介紹下:

可以認為默認只有一個復(fù)合圖層,所有的DOM節(jié)點都是在這個復(fù)合圖層下的

如果開啟了硬件加速功能,可以將某個節(jié)點變成復(fù)合圖層

復(fù)合圖層之間的繪制互不干擾,由GPU直接控制

而簡單圖層中,就算是absolute等布局,變化時不影響整體的回流,但是由于在同一個圖層中,仍然是會影響繪制的,因此做動畫時性能仍然很低。而復(fù)合層是獨立的,所以一般做動畫推薦使用硬件加速

更多參考:

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

Chrome中的調(diào)試

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


資源外鏈的下載

上面介紹了html解析,渲染流程。但實際上,在解析html時,會遇到一些資源連接,此時就需要進行多帶帶處理了

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

CSS樣式資源

JS腳本資源

img圖片類資源

遇到外鏈時的處理

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

遇到CSS樣式資源

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

CSS下載時異步,不會阻塞瀏覽器構(gòu)建DOM樹

但是會阻塞渲染,也就是在構(gòu)建render時,會等到css下載解析完畢后才進行(這點與瀏覽器優(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ī)則渲染的,是可以進一步展開的,此部分內(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有三種定位機制:普通流浮動,絕對定位,如無特別提及,下文中都是針對普通流中的

包含塊(Containing Block)

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

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

譬如:

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

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

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

absolute的包含塊由它最近的position 屬性為absoluterelative或者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)于匿名塊框的生成,示例:

Some text

More text

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

換句話說:

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

行內(nèi)框:

一個行內(nèi)元素生成一個行內(nèi)框

行內(nèi)元素能排在一行,允許左右有其它元素

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

Some emphasized text

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)作塊框來進行格式化,因為只要出現(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

positionabsolutefixed

displayinline-block, flex, inline-flex,table,table-celltable-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ī)則決定<

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/93349.html

相關(guān)文章

  • 前端最實用書簽(持續(xù)更新)

    摘要:前言一直混跡社區(qū)突然發(fā)現(xiàn)自己收藏了不少好文但是管理起來有點混亂所以將前端主流技術(shù)做了一個書簽整理不求最多最全但求最實用。 前言 一直混跡社區(qū),突然發(fā)現(xiàn)自己收藏了不少好文但是管理起來有點混亂; 所以將前端主流技術(shù)做了一個書簽整理,不求最多最全,但求最實用。 書簽源碼 書簽導(dǎo)入瀏覽器效果截圖showImg(https://segmentfault.com/img/bVbg41b?w=107...

    sshe 評論0 收藏0
  • 前端性能優(yōu)化之重排和重繪

    摘要:接下來深入的介紹幾種性能優(yōu)化的小最小化重繪和重排既然重排重繪是會影響頁面的性能,尤其是糟糕的代碼更會將重排帶來的性能問題放大。前面有講到,當(dāng)訪問諸如,這種屬性時,會沖破瀏覽器自有的優(yōu)化通過隊列化修改和批量運行的方法,減少重排重繪版次。 前言,最近利用碎片時間拜讀了一下尼古拉斯的另一巨作《高性能JavaScript》,今天寫的文章從老生常談的頁面重繪和重排入手,去探究這兩個概念在頁面性能...

    vpants 評論0 收藏0
  • 深入理解js

    摘要:詳解十大常用設(shè)計模式力薦深度好文深入理解大設(shè)計模式收集各種疑難雜癥的問題集錦關(guān)于,工作和學(xué)習(xí)過程中遇到過許多問題,也解答過許多別人的問題。介紹了的內(nèi)存管理。 延遲加載 (Lazyload) 三種實現(xiàn)方式 延遲加載也稱為惰性加載,即在長網(wǎng)頁中延遲加載圖像。用戶滾動到它們之前,視口外的圖像不會加載。本文詳細介紹了三種延遲加載的實現(xiàn)方式。 詳解 Javascript十大常用設(shè)計模式 力薦~ ...

    caikeal 評論0 收藏0
  • 最近遇前端面試(2017.03.08更新版)

    摘要:通過管理組件通信通過驅(qū)動視圖比較差異進行更新操作作者第七頁鏈接來源知乎著作權(quán)歸作者所有,轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)。達到無刷新的效果。對象的狀態(tài)不受外界影響。對象代表一個異步操作,有三種狀態(tài)進行中已完成,又稱和已失敗。 以下問題解釋非本人原創(chuàng),是根據(jù)面試經(jīng)驗整理后覺得更容易理解的解釋版本,歡迎補充。 一. 輸入url后的加載過程 從輸入 URL 到頁面加載完成的過程中都發(fā)生了什么 計算機...

    linkFly 評論0 收藏0
  • 最近遇前端面試(2017.03.08更新版)

    摘要:通過管理組件通信通過驅(qū)動視圖比較差異進行更新操作作者第七頁鏈接來源知乎著作權(quán)歸作者所有,轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)。達到無刷新的效果。對象的狀態(tài)不受外界影響。對象代表一個異步操作,有三種狀態(tài)進行中已完成,又稱和已失敗。 以下問題解釋非本人原創(chuàng),是根據(jù)面試經(jīng)驗整理后覺得更容易理解的解釋版本,歡迎補充。 一. 輸入url后的加載過程 從輸入 URL 到頁面加載完成的過程中都發(fā)生了什么 計算機...

    Nosee 評論0 收藏0

發(fā)表評論

0條評論

kel

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<