亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

從渲染原理談前端性能優(yōu)化

everfly / 2417人閱讀

摘要:通過主機(jī)名,最終得到該主機(jī)名對(duì)應(yīng)的地址的過程叫做域名解析或主機(jī)名解析。因此去掉不必要的資源和資源合并包括及資源合并雪碧圖等才會(huì)成為性能優(yōu)化繞不開的方案。

作者:李佳曉 原文:學(xué)而思網(wǎng)校技術(shù)團(tuán)隊(duì)

前言

合格的開發(fā)者知道怎么做,而優(yōu)秀的開發(fā)者知道為什么這么做。

這句話來自《web性能權(quán)威指南》,我一直很喜歡,而本文嘗試從瀏覽器渲染原理探討如何進(jìn)行性能提升。
全文將從網(wǎng)絡(luò)通信以及頁(yè)面渲染兩個(gè)過程去探討瀏覽器的行為及在此過程中我們可以針對(duì)那些點(diǎn)進(jìn)行優(yōu)化,有些的不足之處還請(qǐng)各位不吝雅正。

一、關(guān)于瀏覽器渲染的容易誤解點(diǎn)總結(jié)

關(guān)于瀏覽器渲染機(jī)制已經(jīng)是老生常談,而且網(wǎng)上現(xiàn)有資料中有非常多的優(yōu)秀資料對(duì)此進(jìn)行闡述。遺憾的是網(wǎng)上的資料良莠不齊,經(jīng)常在不同的文檔中對(duì)同一件事的描述出現(xiàn)了極大的差異。懷著嚴(yán)謹(jǐn)求學(xué)的態(tài)度經(jīng)過大量資料的查閱和請(qǐng)教,將會(huì)在后文總結(jié)出一個(gè)完整的流程。

1、DOM樹的構(gòu)建是文檔加載完成開始的?

DOM樹的構(gòu)建是從接受到文檔開始的,先將字節(jié)轉(zhuǎn)化為字符,然后字符轉(zhuǎn)化為標(biāo)記,接著標(biāo)記構(gòu)建dom樹。這個(gè)過程被分為標(biāo)記化和樹構(gòu)建
而這是一個(gè)漸進(jìn)的過程。為達(dá)到更好的用戶體驗(yàn),呈現(xiàn)引擎會(huì)力求盡快將內(nèi)容顯示在屏幕上。它不必等到整個(gè) HTML 文檔解析完畢之后,就會(huì)開始構(gòu)建呈現(xiàn)樹和設(shè)置布局。在不斷接收和處理來自網(wǎng)絡(luò)的其余內(nèi)容的同時(shí),呈現(xiàn)引擎會(huì)將部分內(nèi)容解析并顯示出來。
參考文檔:
http://taligarsiel.com/Projec...

2、渲染樹是在DOM樹和CSS樣式樹構(gòu)建完畢才開始構(gòu)建的嗎?

這三個(gè)過程在實(shí)際進(jìn)行的時(shí)候又不是完全獨(dú)立,而是會(huì)有交叉。會(huì)造成一邊加載,一邊解析,一邊渲染的工作現(xiàn)象。
參考文檔:

http://www.jianshu.com/p/2d52...

3、css的標(biāo)簽嵌套越多,越容易定位到元素

css的解析是自右至左逆向解析的,嵌套越多越增加瀏覽器的工作量,而不會(huì)越快。
因?yàn)槿绻蚪馕觯纭竏iv div p em」,我們首先就要檢查當(dāng)前元素到 html 的整條路徑,找到最上層的 div,再往下找,如果遇到不匹配就必須回到最上層那個(gè) div,往下再去匹配選擇器中的第一個(gè) div,回溯若干次才能確定匹配與否,效率很低。
逆向匹配則不同,如果當(dāng)前的 DOM 元素是 div,而不是 selector 最后的 em,那只要一步就能排除。只有在匹配時(shí),才會(huì)不斷向上找父節(jié)點(diǎn)進(jìn)行驗(yàn)證。
打個(gè)比如 p span.showing
你認(rèn)為從一個(gè)p元素下面找到所有的span元素并判斷是否有class showing快,還是找到所有的span元素判斷是否有class showing并且包括一個(gè)p父元素快
參考文檔:
http://www.imooc.com/code/4570

二、頁(yè)面渲染的完整流程

當(dāng)瀏覽器拿到HTTP報(bào)文時(shí)呈現(xiàn)引擎將開始解析 HTML 文檔,并將各標(biāo)記逐個(gè)轉(zhuǎn)化成“內(nèi)容樹”上的 DOM 節(jié)點(diǎn)。同時(shí)也會(huì)解析外部 CSS 文件以及樣式元素中的樣式數(shù)據(jù)。HTML 中這些帶有視覺指令的樣式信息將用于創(chuàng)建另一個(gè)樹結(jié)構(gòu):呈現(xiàn)樹。瀏覽器將根據(jù)呈現(xiàn)樹進(jìn)行布局繪制。

以上就是頁(yè)面渲染的大致流程。那么瀏覽器從用戶輸入網(wǎng)址之后到底做了什么呢?以下將會(huì)進(jìn)行一個(gè)完整的梳理。鑒于本文是前端向的所以梳理內(nèi)容會(huì)有所偏重。而從輸入到呈現(xiàn)可以分為兩個(gè)部分:網(wǎng)絡(luò)通信頁(yè)面渲染

我們首先來看網(wǎng)絡(luò)通信部分:

1、用戶輸入url并敲擊回車。 2、進(jìn)行DNS解析。

如果用戶輸入的是ip地址則直接進(jìn)入第三條。但去記錄毫無規(guī)律且冗長(zhǎng)的ip地址顯然不是易事,所以通常都是輸入的域名,此時(shí)就會(huì)進(jìn)行dns解析。所謂DNS(Domain Name System)指域名系統(tǒng)。因特網(wǎng)上作為域名和IP地址相互映射的一個(gè)分布式數(shù)據(jù)庫(kù),能夠使用戶更方便的訪問互聯(lián)網(wǎng),而不用去記住能夠被機(jī)器直接讀取的IP數(shù)串。通過主機(jī)名,最終得到該主機(jī)名對(duì)應(yīng)的IP地址的過程叫做域名解析(或主機(jī)名解析)。這個(gè)過程如下所示:

瀏覽器會(huì)首先搜索瀏覽器自身的DNS緩存(緩存時(shí)間比較短,大概只有2分鐘左右,且只能容納1000條緩存)。

如果瀏覽器自身緩存找不到則會(huì)查看系統(tǒng)的DNS緩存,如果找到且沒有過期則停止搜索解析到此結(jié)束.

而如果本機(jī)沒有找到DNS緩存,則瀏覽器會(huì)發(fā)起一個(gè)DNS的系統(tǒng)調(diào)用,就會(huì)向本地配置的首選DNS服務(wù)器發(fā)起域名解析請(qǐng)求(通過的是UDP協(xié)議向DNS的53端口發(fā)起請(qǐng)求,這個(gè)請(qǐng)求是遞歸的請(qǐng)求,也就是運(yùn)營(yíng)商的DNS服務(wù)器必須得提供給我們?cè)撚蛎腎P地址),運(yùn)營(yíng)商的DNS服務(wù)器首先查找自身的緩存,找到對(duì)應(yīng)的條目,且沒有過期,則解析成功。

如果沒有找到對(duì)應(yīng)的條目,則有運(yùn)營(yíng)商的DNS代我們的瀏覽器發(fā)起迭代DNS解析請(qǐng)求,它首先是會(huì)找根域的DNS的IP地址(這個(gè)DNS服務(wù)器都內(nèi)置13臺(tái)根域的DNS的IP地址),找打根域的DNS地址,就會(huì)向其發(fā)起請(qǐng)求(請(qǐng)問www.xxxx.com這個(gè)域名的IP地址是多少???)

根域發(fā)現(xiàn)這是一個(gè)頂級(jí)域com域的一個(gè)域名,于是就告訴運(yùn)營(yíng)商的DNS我不知道這個(gè)域名的IP地址,但是我知道com域的IP地址,你去找它去,于是運(yùn)營(yíng)商的DNS就得到了com域的IP地址,又向com域的IP地址發(fā)起了請(qǐng)求(請(qǐng)問www.xxxx.com這個(gè)域名的IP地址是多少?),com域這臺(tái)服務(wù)器告訴運(yùn)營(yíng)商的DNS我不知道www.xxxx.com這個(gè)域名的IP地址,但是我知道xxxx.com這個(gè)域的DNS地址,你去找它去,于是運(yùn)營(yíng)商的DNS又向linux178.com這個(gè)域名的DNS地址(這個(gè)一般就是由域名注冊(cè)商提供的,像萬(wàn)網(wǎng),新網(wǎng)等)發(fā)起請(qǐng)求(請(qǐng)問www.xxxx.com這個(gè)域名的IP地址是多少?),這個(gè)時(shí)候xxxx.com域的DNS服務(wù)器一查,誒,果真在我這里,于是就把找到的結(jié)果發(fā)送給運(yùn)營(yíng)商的DNS服務(wù)器,這個(gè)時(shí)候運(yùn)營(yíng)商的DNS服務(wù)器就拿到了www.xxxx.com這個(gè)域名對(duì)應(yīng)的IP地址,并返回給Windows系統(tǒng)內(nèi)核,內(nèi)核又把結(jié)果返回給瀏覽器,終于瀏覽器拿到了www.xxxx.com對(duì)應(yīng)的IP地址,這次dns解析圓滿成功。

3、建立tcp連接

拿到域名對(duì)應(yīng)的IP地址之后,User-Agent(一般是指瀏覽器)會(huì)以一個(gè)隨機(jī)端口(1024< 端口 < 65535)向服務(wù)器的WEB程序(常用的有httpd,nginx等)80端口發(fā)起TCP的連接請(qǐng)求。這個(gè)連接請(qǐng)求(原始的http請(qǐng)求經(jīng)過TCP/IP4層模型的層層封包)到達(dá)服務(wù)器端后(這中間通過各種路由設(shè)備,局域網(wǎng)內(nèi)除外),進(jìn)入到網(wǎng)卡,然后是進(jìn)入到內(nèi)核的TCP/IP協(xié)議棧(用于識(shí)別該連接請(qǐng)求,解封包,一層一層的剝開),還有可能要經(jīng)過Netfilter防火墻(屬于內(nèi)核的模塊)的過濾,最終到達(dá)WEB程序,最終建立了TCP/IP的連接。

tcp建立連接和關(guān)閉連接均需要一個(gè)完善的確認(rèn)機(jī)制,我們一般將連接稱為三次握手,而連接關(guān)閉稱為四次揮手。而不論是三次握手還是四次揮手都需要數(shù)據(jù)從客戶端到服務(wù)器的一次完整傳輸。將數(shù)據(jù)從客戶端到服務(wù)端經(jīng)歷的一個(gè)完整時(shí)延包括:

發(fā)送時(shí)延:把消息中的所有比特轉(zhuǎn)移到鏈路中需要的時(shí)間,是消息長(zhǎng)度和鏈路速度的函數(shù)

傳播時(shí)延:消息從發(fā)送端到接受端需要的時(shí)間,是信號(hào)傳播距離和速度的函數(shù)

處理時(shí)延:處理分組首部,檢查位錯(cuò)誤及確定分組目標(biāo)所需的時(shí)間

排隊(duì)時(shí)延:到來的分組排隊(duì)等待處理的時(shí)間以上的延遲總和就是客戶端到服務(wù)器的總延遲時(shí)間

以上的延遲總和就是客戶端到服務(wù)器的總延遲時(shí)間。因此每一次的連接建立和斷開都是有巨大代價(jià)的。因此去掉不必要的資源和資源合并(包括js及css資源合并、雪碧圖等)才會(huì)成為性能優(yōu)化繞不開的方案。但是好消息是隨著協(xié)議的發(fā)展我們將對(duì)性能優(yōu)化這個(gè)主題有著新的看法和思考。雖然還未到來,但也不遠(yuǎn)了。如果你感到好奇那就接著往下看。

以下簡(jiǎn)述下tcp建立連接的過程:

第一次握手:客戶端發(fā)送syn包(syn=x,x為客戶端隨機(jī)序列號(hào))的數(shù)據(jù)包到服務(wù)器,并進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器確認(rèn);

第二次握手:服務(wù)器收到syn包,必須確認(rèn)客戶的SYN(ack=x+1),同時(shí)自己也發(fā)送一個(gè)SYN包(syn=y,y為服務(wù)端生成的隨機(jī)序列號(hào)),即SYN+ACK包,此時(shí)服務(wù)器進(jìn)入SYN_RECV狀態(tài);

第三次握手:客戶端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認(rèn)包ACK(ack=y+1)

此包發(fā)送完畢,客戶端和服務(wù)器進(jìn)入ESTABLISHED狀態(tài),完成三次握手。握手過程中傳送的包里不包含數(shù)據(jù),三次握手完畢后,客戶端與服務(wù)器才正式開始傳送數(shù)據(jù)。理想狀態(tài)下,TCP連接一旦建立,在通信雙方中的任何一方主動(dòng)關(guān)閉連接之前,TCP連接都將被一直保持下去

這里注意, 三次握手是不攜帶數(shù)據(jù)的,而是在握手完畢才開始數(shù)據(jù)傳輸。因此如果每次數(shù)據(jù)請(qǐng)求都需要重新進(jìn)行完整的tcp連接建立,通信時(shí)延的耗時(shí)是難以估量的!這也就是為什么我們總是能聽到資源合并減少請(qǐng)求次數(shù)的原因。

下面來看看HTTP如何在協(xié)議層面幫我們進(jìn)行優(yōu)化的:

HTTP1.0

在http1.0時(shí)代,每個(gè)TCP連接只能發(fā)送一個(gè)請(qǐng)求。發(fā)送數(shù)據(jù)完畢,連接就關(guān)閉,如果還要請(qǐng)求其他資源,就必須再新建一個(gè)連接。 TCP連接的新建成本很高,因?yàn)樾枰蛻舳撕头?wù)器三次握手,并且開始時(shí)發(fā)送速率較慢(TCP的擁塞控制開始時(shí)會(huì)啟動(dòng)慢啟動(dòng)算法)。在數(shù)據(jù)傳輸?shù)拈_始只能發(fā)送少量包,并隨著網(wǎng)絡(luò)狀態(tài)良好(無擁塞)指數(shù)增長(zhǎng)。但遇到擁塞又要重新從1個(gè)包開始進(jìn)行傳輸。

以下圖為例,慢啟動(dòng)時(shí)第一次數(shù)據(jù)傳輸只能傳輸一組數(shù)據(jù),得到確認(rèn)后傳輸2組,每次翻倍,直到達(dá)到閾值16時(shí)開始啟用擁塞避免算法,既每次得到確認(rèn)后數(shù)據(jù)包只增加一個(gè)。當(dāng)發(fā)生網(wǎng)絡(luò)擁塞后,閾值減半重新開始慢啟動(dòng)算法。

因此為避免tcp連接的三次握手耗時(shí)及慢啟動(dòng)引起的發(fā)送速度慢的情況,應(yīng)盡量減少tcp連接的次數(shù)。

而HTTP1.0每個(gè)數(shù)據(jù)請(qǐng)求都需要重新建立連接的特點(diǎn)使得HTTP 1.0版本的性能比較差。隨著網(wǎng)頁(yè)加載的外部資源越來越多,這個(gè)問題就愈發(fā)突出了。 為了解決這個(gè)問題,有些瀏覽器在請(qǐng)求時(shí),用了一個(gè)非標(biāo)準(zhǔn)的Connection字段。 Kepp-alive 一個(gè)可以復(fù)用的TCP連接就建立了,直到客戶端或服務(wù)器主動(dòng)關(guān)閉連接。但是,這不是標(biāo)準(zhǔn)字段,不同實(shí)現(xiàn)的行為可能不一致,因此不是根本的解決辦法。

HTTP1.1

http1.1(以下簡(jiǎn)稱h1.1) 版的最大變化,就是引入了持久連接(persistent connection),即TCP連接默認(rèn)不關(guān)閉,可以被多個(gè)請(qǐng)求復(fù)用,不用聲明Connection: keep-alive。 客戶端和服務(wù)器發(fā)現(xiàn)對(duì)方一段時(shí)間沒有活動(dòng),就可以主動(dòng)關(guān)閉連接。不過,規(guī)范的做法是,客戶端在最后一個(gè)請(qǐng)求時(shí),發(fā)送Connection: close,明確要求服務(wù)器關(guān)閉TCP連接。 目前,對(duì)于同一個(gè)域名,大多數(shù)瀏覽器允許同時(shí)建立6個(gè)持久連接。相比與http1.0,1.1的頁(yè)面性能有了巨大提升,因?yàn)槭∪チ撕芏鄑cp的握手揮手時(shí)間。下圖第一種是tcp建立后只能發(fā)一個(gè)請(qǐng)求的http1.0的通信狀態(tài),而擁有了持久連接的h1.1則避免了tcp握手及慢啟動(dòng)帶來的漫長(zhǎng)時(shí)延。

從圖中可以看到相比h1.0,h1.1的性能有所提升。然而雖然1.1版允許復(fù)用TCP連接,但是同一個(gè)TCP連接里面,所有的數(shù)據(jù)通信是按次序進(jìn)行的。服務(wù)器只有處理完一個(gè)回應(yīng),才會(huì)進(jìn)行下一個(gè)回應(yīng)。要是前面的回應(yīng)特別慢,后面就會(huì)有許多請(qǐng)求排隊(duì)等著。這稱為"隊(duì)頭堵塞"(Head-of-line blocking)。 為了避免這個(gè)問題,只有三種方法:一是減少請(qǐng)求數(shù),二是同時(shí)多開持久連接。這導(dǎo)致了很多的網(wǎng)頁(yè)優(yōu)化技巧,比如合并腳本和樣式表、將圖片嵌入CSS代碼、域名分片(domain sharding)等等。如果HTTP協(xié)議能繼續(xù)優(yōu)化,這些額外的工作是可以避免的。三是開啟pipelining,不過pipelining并不是救世主,它也存在不少缺陷:

pipelining只能適用于http1.1,一般來說,支持http1.1的server都要求支持pipelining

只有冪等的請(qǐng)求(GET,HEAD)能使用pipelining,非冪等請(qǐng)求比如POST不能使用,因?yàn)檎?qǐng)求之間可能會(huì)存在先后依賴關(guān)系。

head of line blocking并沒有完全得到解決,server的response還是要求依次返回,遵循FIFO(first in first out)原則。也就是說如果請(qǐng)求1的response沒有回來,2,3,4,5的response也不會(huì)被送回來。

絕大部分的http代理服務(wù)器不支持pipelining。 和不支持pipelining的老服務(wù)器協(xié)商有問題。 可能會(huì)導(dǎo)致新的隊(duì)首阻塞問題。

鑒于以上種種原因,pipelining的支持度并不友好??梢钥纯碿hrome對(duì)pipelining的描述:

https://www.chromium.org/deve...

HTTP2

2015年,HTTP/2 發(fā)布。它不叫 HTTP/2.0,是因?yàn)闃?biāo)準(zhǔn)委員會(huì)不打算再發(fā)布子版本了,下一個(gè)新版本將是 HTTP/3。HTTP2將具有以下幾個(gè)主要特點(diǎn):

二進(jìn)制協(xié)議 :HTTP/1.1 版的頭信息肯定是文本(ASCII編碼),數(shù)據(jù)體可以是文本,也可以是二進(jìn)制。HTTP/2 則是一個(gè)徹底的二進(jìn)制協(xié)議,頭信息和數(shù)據(jù)體都是二進(jìn)制,并且統(tǒng)稱為"幀"(frame):頭信息幀和數(shù)據(jù)幀。

多工 :HTTP/2 復(fù)用TCP連接,在一個(gè)連接里,客戶端和瀏覽器都可以同時(shí)發(fā)送多個(gè)請(qǐng)求或回應(yīng),而且不用按照順序一一對(duì)應(yīng),這樣就避免了"隊(duì)頭堵塞"。

數(shù)據(jù)流:因?yàn)?HTTP/2 的數(shù)據(jù)包是不按順序發(fā)送的,同一個(gè)連接里面連續(xù)的數(shù)據(jù)包,可能屬于不同的回應(yīng)。因此,必須要對(duì)數(shù)據(jù)包做標(biāo)記,指出它屬于哪個(gè)回應(yīng)。 HTTP/2 將每個(gè)請(qǐng)求或回應(yīng)的所有數(shù)據(jù)包,稱為一個(gè)數(shù)據(jù)流(stream)。每個(gè)數(shù)據(jù)流都有一個(gè)獨(dú)一無二的編號(hào)。數(shù)據(jù)包發(fā)送的時(shí)候,都必須標(biāo)記數(shù)據(jù)流ID,用來區(qū)分它屬于哪個(gè)數(shù)據(jù)流。另外還規(guī)定,客戶端發(fā)出的數(shù)據(jù)流,ID一律為奇數(shù),服務(wù)器發(fā)出的,ID為偶數(shù)。 數(shù)據(jù)流發(fā)送到一半的時(shí)候,客戶端和服務(wù)器都可以發(fā)送信號(hào)(RST_STREAM幀),取消這個(gè)數(shù)據(jù)流。1.1版取消數(shù)據(jù)流的唯一方法,就是關(guān)閉TCP連接。這就是說,HTTP/2 可以取消某一次請(qǐng)求,同時(shí)保證TCP連接還打開著,可以被其他請(qǐng)求使用。 客戶端還可以指定數(shù)據(jù)流的優(yōu)先級(jí)。優(yōu)先級(jí)越高,服務(wù)器就會(huì)越早回應(yīng)。

頭信息壓縮: HTTP 協(xié)議不帶有狀態(tài),每次請(qǐng)求都必須附上所有信息。所以,請(qǐng)求的很多字段都是重復(fù)的,比如Cookie和User Agent,一模一樣的內(nèi)容,每次請(qǐng)求都必須附帶,這會(huì)浪費(fèi)很多帶寬,也影響速度。 HTTP2對(duì)這一點(diǎn)做了優(yōu)化,引入了頭信息壓縮機(jī)制(header compression)。一方面,頭信息使用gzip或compress壓縮后再發(fā)送;另一方面,客戶端和服務(wù)器同時(shí)維護(hù)一張頭信息表,所有字段都會(huì)存入這個(gè)表,生成一個(gè)索引號(hào),以后就不發(fā)送同樣字段了,只發(fā)送索引號(hào),這樣就提高速度了。

服務(wù)器推送: HTTP/2 允許服務(wù)器未經(jīng)請(qǐng)求,主動(dòng)向客戶端發(fā)送資源,這叫做服務(wù)器推送(server push)。 常見場(chǎng)景是客戶端請(qǐng)求一個(gè)網(wǎng)頁(yè),這個(gè)網(wǎng)頁(yè)里面包含很多靜態(tài)資源。正常情況下,客戶端必須收到網(wǎng)頁(yè)后,解析HTML源碼,發(fā)現(xiàn)有靜態(tài)資源,再發(fā)出靜態(tài)資源請(qǐng)求。其實(shí),服務(wù)器可以預(yù)期到客戶端請(qǐng)求網(wǎng)頁(yè)后,很可能會(huì)再請(qǐng)求靜態(tài)資源,所以就主動(dòng)把這些靜態(tài)資源隨著網(wǎng)頁(yè)一起發(fā)給客戶端了。

就這幾個(gè)點(diǎn)我們分別討論一下:
就多工來看:雖然http1.1支持了pipelining,但是仍然會(huì)有隊(duì)首阻塞問題,如果瀏覽器同時(shí)發(fā)出http請(qǐng)求請(qǐng)求和css,服務(wù)器端處理css請(qǐng)求耗時(shí)20ms,但是因?yàn)橄日?qǐng)求資源是html,此時(shí)的css盡管已經(jīng)處理好了但仍不能返回,而需要等待html處理好一起返回,此時(shí)的客戶端就處于盲等狀態(tài),而事實(shí)上如果服務(wù)器先處理好css就先返回css的話,瀏覽器就可以開始解析css了。而多工的出現(xiàn)就解決了http之前版本協(xié)議的問題,極大的提升了頁(yè)面性能??s短了通信時(shí)間。我們來看看有了多工之后有那些影響:

無需進(jìn)行資源分片:為了避免請(qǐng)求tcp連接耗時(shí)長(zhǎng)的和初始發(fā)送速率低的問題,瀏覽器允許同時(shí)打開多個(gè)tcp連接讓資源同時(shí)請(qǐng)求。但是為了避免服務(wù)器壓力,一般針對(duì)一個(gè)域名會(huì)有最大并發(fā)數(shù)的限制,一般來說是6個(gè)。允許一個(gè)頁(yè)面同時(shí)對(duì)相同域名打開6個(gè)tcp連接。為了繞過最大并發(fā)數(shù)的限制,會(huì)將資源分布在不同的域名下,避免資源在超過并發(fā)數(shù)后需要等待才能開始請(qǐng)求。而有了http2,可以同步請(qǐng)求資源,資源分片這種方式就可以不再使用。

資源合并:資源合并會(huì)不利于緩存機(jī)制,因?yàn)閱挝募薷臅?huì)影響整個(gè)資源包。而且單文件過大對(duì)于 HTTP/2 的傳輸不好,盡量做到細(xì)?;欣?HTTP/2 傳輸。而且內(nèi)置資源也是同理,將資源以base64的形式放進(jìn)代碼中不利于緩存。且編碼后的圖片資源大小是要超過圖片大小的。這兩者都是以減少tcp請(qǐng)求次數(shù)增大單個(gè)文件大小來進(jìn)行優(yōu)化的。

就頭部壓縮來看:HTTP/1.1 版的頭信息是ASCII編碼,也就是不經(jīng)過壓縮的,當(dāng)我們請(qǐng)求只攜帶少量數(shù)據(jù)時(shí),http頭部可能要比載荷要大許多,尤其是有了很長(zhǎng)的cookie之后這一點(diǎn)尤為顯著,頭部壓縮毫無疑問可以對(duì)性能有很大提升。

就服務(wù)器推送來看:少去了資源請(qǐng)求的時(shí)間,服務(wù)端可以將可能用到的資源推送給服務(wù)端以待使用。這項(xiàng)能力幾乎是革新了之前應(yīng)答模式的認(rèn)知,對(duì)性能提升也有巨大幫助。

因此很多優(yōu)化都是在基于tcp及http的一些問題來避免和繞過的。事實(shí)上多數(shù)的優(yōu)化都是針對(duì)網(wǎng)絡(luò)通信這個(gè)部分在做。

4、建立TCP連接后發(fā)起http請(qǐng)求 5、服務(wù)器端響應(yīng)http請(qǐng)求,瀏覽器得到html代碼

以上是網(wǎng)絡(luò)通信部分,接下來將會(huì)對(duì)頁(yè)面渲染部分進(jìn)行敘述。

當(dāng)瀏覽器拿到HTML文檔時(shí)首先會(huì)進(jìn)行HTML文檔解析,構(gòu)建DOM樹。

遇到css樣式如link標(biāo)簽或者style標(biāo)簽時(shí)開始解析css,構(gòu)建樣式樹。HTML解析構(gòu)建和CSS的解析是相互獨(dú)立的并不會(huì)造成沖突,因此我們通常將css樣式放在head中,讓瀏覽器盡早解析css。

當(dāng)html的解析遇到script標(biāo)簽會(huì)怎樣呢?答案是停止DOM樹的解析開始下載js。因?yàn)閖s是會(huì)阻塞html解析的,是阻塞資源。其原因在于js可能會(huì)改變html現(xiàn)有結(jié)構(gòu)。例如有的節(jié)點(diǎn)是用js動(dòng)態(tài)構(gòu)建的,在這種情況下就會(huì)停止dom樹的構(gòu)建開始下載解析js。腳本在文檔的何處插入,就在何處執(zhí)行。當(dāng) HTML 解析器遇到一個(gè) script 標(biāo)記時(shí),它會(huì)暫停構(gòu)建 DOM,將控制權(quán)移交給 JavaScript 引擎;等 JavaScript 引擎運(yùn)行完畢,瀏覽器會(huì)從中斷的地方恢復(fù) DOM 構(gòu)建。而因此就會(huì)推遲頁(yè)面首繪的時(shí)間。可以在首繪不需要js的情況下用async和defer實(shí)現(xiàn)異步加載。這樣js就不會(huì)阻塞html的解析了。當(dāng)HTML解析完成后,瀏覽器會(huì)將文檔標(biāo)注為交互狀態(tài),并開始解析那些處于“deferred”模式的腳本,也就是那些應(yīng)在文檔解析完成后才執(zhí)行的腳本。然后,文檔狀態(tài)將設(shè)置為“完成”,一個(gè)“加載”事件將隨之觸發(fā)。

注意,異步執(zhí)行是指下載。執(zhí)行js時(shí)仍然會(huì)阻塞。

在得到DOM樹和樣式樹后就可以進(jìn)行渲染樹的構(gòu)建了。應(yīng)注意的是渲染樹和 DOM 元素相對(duì)應(yīng)的,但并非一一對(duì)應(yīng)。比如非可視化的 DOM 元素不會(huì)插入呈現(xiàn)樹中,例如“head”元素。如果元素的 display 屬性值為“none”,那么也不會(huì)顯示在呈現(xiàn)樹中(但是 visibility 屬性值為“hidden”的元素仍會(huì)顯示)

渲染樹構(gòu)建完畢后將會(huì)進(jìn)行布局。布局使用流模型的Layout算法。所謂流模型,即是指Layout的過程只需進(jìn)行一遍即可完成,后出現(xiàn)在流中的元素不會(huì)影響前出現(xiàn)在流中的元素,Layout過程只需從左至右從上至下一遍完成即可。但實(shí)際實(shí)現(xiàn)中,流模型會(huì)有例外。Layout是一個(gè)遞歸的過程,每個(gè)節(jié)點(diǎn)都負(fù)責(zé)自己及其子節(jié)點(diǎn)的Layout。Layout結(jié)果是相對(duì)父節(jié)點(diǎn)的坐標(biāo)和尺寸。其過程可以簡(jiǎn)述為:

此時(shí)renderTree已經(jīng)構(gòu)建完畢,不過瀏覽器渲染樹引擎并不直接使用渲染樹進(jìn)行繪制,為了方便處理定位(裁剪),溢出滾動(dòng)(頁(yè)內(nèi)滾動(dòng)),CSS轉(zhuǎn)換/不透明/動(dòng)畫/濾鏡,蒙版或反射,Z (Z排序)等,瀏覽器需要生成另外一棵樹 - 層樹。因此繪制過程如下:1、獲取 DOM 并將其分割為多個(gè)層(RenderLayer) 2、將每個(gè)層?xùn)鸥窕ⅹ?dú)立的繪制進(jìn)位圖中 3、將這些位圖作為紋理上傳至 GPU 4、復(fù)合多個(gè)層來生成最終的屏幕圖像(終極layer)。

三、HTML及CSS樣式的解析

HTML解析是一個(gè)將字節(jié)轉(zhuǎn)化為字符,字符解析為標(biāo)記,標(biāo)記生成節(jié)點(diǎn),節(jié)點(diǎn)構(gòu)建樹的過程。。CSS樣式的解析則由于復(fù)雜的樣式層疊而變得復(fù)雜。對(duì)此不同的渲染引擎在處理上有所差異,后文將會(huì)就這點(diǎn)進(jìn)行詳細(xì)講解

1、HTML的解析分為標(biāo)記化和樹構(gòu)建兩個(gè)階段

標(biāo)記化算法:

是詞法分析過程,將輸入內(nèi)容解析成多個(gè)標(biāo)記。HTML標(biāo)記包括起始標(biāo)記、結(jié)束標(biāo)記、屬性名稱和屬性值。標(biāo)記生成器識(shí)別標(biāo)記,傳遞給樹構(gòu)造器,然后接受下一個(gè)字符以識(shí)別下一個(gè)標(biāo)記;如此反復(fù)直到輸入的結(jié)束。
該算法的輸出結(jié)果是 HTML 標(biāo)記。該算法使用狀態(tài)機(jī)來表示。每一個(gè)狀態(tài)接收來自輸入信息流的一個(gè)或多個(gè)字符,并根據(jù)這些字符更新下一個(gè)狀態(tài)。當(dāng)前的標(biāo)記化狀態(tài)和樹結(jié)構(gòu)狀態(tài)會(huì)影響進(jìn)入下一狀態(tài)的決定。這意味著,即使接收的字符相同,對(duì)于下一個(gè)正確的狀態(tài)也會(huì)產(chǎn)生不同的結(jié)果,具體取決于當(dāng)前的狀態(tài)。
樹構(gòu)建算法:

在樹構(gòu)建階段,以 Document 為根節(jié)點(diǎn)的 DOM 樹也會(huì)不斷進(jìn)行修改,向其中添加各種元素。
標(biāo)記生成器發(fā)送的每個(gè)節(jié)點(diǎn)都會(huì)由樹構(gòu)建器進(jìn)行處理。規(guī)范中定義了每個(gè)標(biāo)記所對(duì)應(yīng)的 DOM 元素,這些元素會(huì)在接收到相應(yīng)的標(biāo)記時(shí)創(chuàng)建。這些元素不僅會(huì)添加到 DOM 樹中,還會(huì)添加到開放元素的堆棧中。此堆棧用于糾正嵌套錯(cuò)誤和處理未關(guān)閉的標(biāo)記。其算法也可以用狀態(tài)機(jī)來描述。這些狀態(tài)稱為“插入模式”。

以下將會(huì)舉一個(gè)例子來分析這兩個(gè)階段:

標(biāo)記化:初始狀態(tài)是數(shù)據(jù)狀態(tài)。

遇到字符 < 時(shí),狀態(tài)更改為“標(biāo)記打開狀態(tài)”。接收一個(gè) a-z字符會(huì)創(chuàng)建“起始標(biāo)記”,狀態(tài)更改為“標(biāo)記名稱狀態(tài)”。這個(gè)狀態(tài)會(huì)一直保持到接收> 字符。在此期間接收的每個(gè)字符都會(huì)附加到新的標(biāo)記名稱上。在本例中,我們創(chuàng)建的標(biāo)記是 html 標(biāo)記。

遇到 > 標(biāo)記時(shí),會(huì)發(fā)送當(dāng)前的標(biāo)記,狀態(tài)改回“數(shù)據(jù)狀態(tài)”。 標(biāo)記也會(huì)進(jìn)行同樣的處理。目前 html 和 body 標(biāo)記均已發(fā)出。現(xiàn)在我們回到“數(shù)據(jù)狀態(tài)”。接收到 Hello world 中的 H 字符時(shí),將創(chuàng)建并發(fā)送字符標(biāo)記,直到接收 中的<。我們將為 Hello world 中的每個(gè)字符都發(fā)送一個(gè)字符標(biāo)記。

現(xiàn)在我們回到“標(biāo)記打開狀態(tài)”。接收下一個(gè)輸入字符 / 時(shí),會(huì)創(chuàng)建 end tag token 并改為“標(biāo)記名稱狀態(tài)”。我們會(huì)再次保持這個(gè)狀態(tài),直到接收 >。然后將發(fā)送新的標(biāo)記,并回到“數(shù)據(jù)狀態(tài)”。 輸入也會(huì)進(jìn)行同樣的處理。

還是以上的例子,我們來看看樹構(gòu)建

樹構(gòu)建:樹構(gòu)建階段的輸入是一個(gè)來自標(biāo)記化階段的標(biāo)記序列。

第一個(gè)模式是“initial mode”。接收 HTML 標(biāo)記后轉(zhuǎn)為“before html”模式,并在這個(gè)模式下重新處理此標(biāo)記。這樣會(huì)創(chuàng)建一個(gè) HTMLHtmlElement 元素,并將其附加到 Document 根對(duì)象上。

然后狀態(tài)將改為“before head”。此時(shí)我們接收“body”標(biāo)記。即使我們的示例中沒有“head”標(biāo)記,系統(tǒng)也會(huì)隱式創(chuàng)建一個(gè) HTMLHeadElement,并將其添加到樹中。

現(xiàn)在我們進(jìn)入了“in head”模式,然后轉(zhuǎn)入“after head”模式。系統(tǒng)對(duì) body 標(biāo)記進(jìn)行重新處理,創(chuàng)建并插入 HTMLBodyElement,同時(shí)模式轉(zhuǎn)變?yōu)椤癰ody”。

現(xiàn)在,接收由“Hello world”字符串生成的一系列字符標(biāo)記。接收第一個(gè)字符時(shí)會(huì)創(chuàng)建并插入“Text”節(jié)點(diǎn),而其他字符也將附加到該節(jié)點(diǎn)

接收 body 結(jié)束標(biāo)記會(huì)觸發(fā)“after body”模式?,F(xiàn)在我們將接收 HTML 結(jié)束標(biāo)記,然后進(jìn)入“after after body”模式。接收到文件結(jié)束標(biāo)記后,解析過程就此結(jié)束。解析結(jié)束后的操作

在此階段,瀏覽器會(huì)將文檔標(biāo)注為交互狀態(tài),并開始解析那些處于“deferred”模式的腳本,也就是那些應(yīng)在文檔解析完成后才執(zhí)行的腳本。然后,文檔狀態(tài)將設(shè)置為“完成”,一個(gè)“加載”事件將隨之觸發(fā)。

完整解析過程如下圖:

2、CSS的解析與層疊規(guī)則

每一個(gè)呈現(xiàn)器都代表了一個(gè)矩形的區(qū)域,通常對(duì)應(yīng)于相關(guān)節(jié)點(diǎn)的 CSS 框,這一點(diǎn)在 CSS2 規(guī)范中有所描述。它包含諸如寬度、高度和位置等幾何信息。就是我們 CSS 里常提到的盒子模型。構(gòu)建呈現(xiàn)樹時(shí),需要計(jì)算每一個(gè)呈現(xiàn)對(duì)象的可視化屬性。這是通過計(jì)算每個(gè)元素的樣式屬性來完成的。由于應(yīng)用規(guī)則涉及到相當(dāng)復(fù)雜的層疊規(guī)則,所以給樣式樹的構(gòu)建造成了巨大的困難。為什么說它復(fù)雜?因?yàn)橥粋€(gè)元素可能涉及多條樣式,就需要判斷最終到底哪條樣式生效。首先我們來了解一下css的樣式層疊規(guī)則

①層疊規(guī)則:

根據(jù)不同的樣式來源優(yōu)先級(jí)排列從小到大:

1>、用戶端聲明:來自瀏覽器的樣式,被稱作 UA style,是瀏覽器默認(rèn)的樣式。 比如,對(duì)于 DIV 元素,瀏覽器默認(rèn)其 ‘display’ 的特性值是 “block”,而 SPAN 是 “inline”。

2>、一般用戶聲明:這個(gè)樣式表是使用瀏覽器的用戶,根據(jù)自己的偏好設(shè)置的樣式表。比如,用戶希望所有 P 元素中的字體都默認(rèn)顯示成藍(lán)色,可以先定義一個(gè)樣式表,存成 css 文件。

3>、一般作者聲明:即開發(fā)者在開發(fā)網(wǎng)頁(yè)時(shí),所定義的樣式表。

4>、加了’!important’ 的作者聲明

5>、加了’!important’ 的用戶聲明

!important 規(guī)則1:根據(jù) CSS2.1 規(guī)范中的描述,’!important’ 可以提高樣式的優(yōu)先級(jí),它對(duì)樣式優(yōu)先級(jí)的影響是巨大的。
注意,’!important’ 規(guī)則在 IE7 以前的版本中是被支持不完善。因此,經(jīng)常被用作 CSS hack2。

如果來源和重要性相同則根據(jù)CSS specificity來進(jìn)行判定。

特殊性的值可以看作是一個(gè)由四個(gè)數(shù)組成的一個(gè)組合,用 a,b,c,d 來表示它的四個(gè)位置。 依次比較 a,b,c,d 這個(gè)四個(gè)數(shù)比較其特殊性的大小。比如,a 值相同,那么 b 值大的組合特殊性會(huì)較大,以此類推。 注意,W3C 中并不是把它作為一個(gè) 4 位數(shù)來看待的。
a,b,c,d 值的確定規(guī)則:

如果 HTML 標(biāo)簽的 ‘style’ 屬性中該樣式存在,則記 a 為 1;

數(shù)一下選擇器中 ID 選擇器的個(gè)數(shù)作為 b 的值。比如,樣式中包含 ‘#c1’ 和 ‘#c2’ 的選擇器;

其他屬性以及偽類(pseudo-classes)的總數(shù)量是 c 的值。比如’.con’,’:hover’ 等;

元素名和偽元素的數(shù)量是 d 的值

在這里我們來看一個(gè)W3C給出的例子:

那么在如下例子中字體的顯示應(yīng)當(dāng)為綠色:

總結(jié)為表格的話計(jì)算規(guī)則如下:

②CSS解析

為了簡(jiǎn)化樣式計(jì)算,F(xiàn)irefox 還采用了另外兩種樹:規(guī)則樹和樣式上下文樹。Webkit 也有樣式對(duì)象,但它們不是保存在類似樣式上下文樹這樣的樹結(jié)構(gòu)中,只是由 DOM 節(jié)點(diǎn)指向此類對(duì)象的相關(guān)樣式。

1>、Firefox的規(guī)則樹和樣式上下文樹:

樣式上下文包含端值。要計(jì)算出這些值,應(yīng)按照正確順序應(yīng)用所有的匹配規(guī)則,并將其從邏輯值轉(zhuǎn)化為具體的值。例如,如果邏輯值是屏幕大小的百分比,則需要換算成絕對(duì)的單位。規(guī)則樹的點(diǎn)子真的很巧妙,它使得節(jié)點(diǎn)之間可以共享這些值,以避免重復(fù)計(jì)算,還可以節(jié)約空間。
所有匹配的規(guī)則都存儲(chǔ)在樹中。路徑中的底層節(jié)點(diǎn)擁有較高的優(yōu)先級(jí)。規(guī)則樹包含了所有已知規(guī)則匹配的路徑。規(guī)則的存儲(chǔ)是延遲進(jìn)行的。規(guī)則樹不會(huì)在開始的時(shí)候就為所有的節(jié)點(diǎn)進(jìn)行計(jì)算,而是只有當(dāng)某個(gè)節(jié)點(diǎn)樣式需要進(jìn)行計(jì)算時(shí),才會(huì)向規(guī)則樹添加計(jì)算的路徑。
這個(gè)想法相當(dāng)于將規(guī)則樹路徑視為詞典中的單詞。如果我們已經(jīng)計(jì)算出如下的規(guī)則樹:

假設(shè)我們需要為內(nèi)容樹中的另一個(gè)元素匹配規(guī)則,并且找到匹配路徑是 B - E - I(按照此順序)。由于我們?cè)跇渲幸呀?jīng)計(jì)算出了路徑 A - B - E - I - L,因此就已經(jīng)有了此路徑,這就減少了現(xiàn)在所需的工作量。

那么Firefox是如何解決樣式計(jì)算難題的呢?接下來看一個(gè)樣例,假設(shè)我們有如下HTML代碼:

并且我們有如下規(guī)則:

為了簡(jiǎn)便起見,我們只需要填充兩個(gè)結(jié)構(gòu):color 結(jié)構(gòu)和 margin 結(jié)構(gòu)。color 結(jié)構(gòu)只包含一個(gè)成員(即“color”),而 margin 結(jié)構(gòu)包含四條邊。
形成的規(guī)則樹如下圖所示(節(jié)點(diǎn)的標(biāo)記方式為“節(jié)點(diǎn)名 : 指向的規(guī)則序號(hào)”):

上下文樹如下圖所示(節(jié)點(diǎn)名 : 指向的規(guī)則節(jié)點(diǎn)):

假設(shè)我們解析 HTML 時(shí)遇到了第二個(gè)

標(biāo)記,我們需要為此節(jié)點(diǎn)創(chuàng)建樣式上下文,并填充其樣式結(jié)構(gòu)。
經(jīng)過規(guī)則匹配,我們發(fā)現(xiàn)該
的匹配規(guī)則是第 1、2 和 6 條。這意味著規(guī)則樹中已有一條路徑可供我們的元素使用,我們只需要再為其添加一個(gè)節(jié)點(diǎn)以匹配第 6 條規(guī)則(規(guī)則樹中的 F 節(jié)點(diǎn))。
我們將創(chuàng)建樣式上下文并將其放入上下文樹中。新的樣式上下文將指向規(guī)則樹中的 F 節(jié)點(diǎn)。
現(xiàn)在我們需要填充樣式結(jié)構(gòu)。首先要填充的是 margin 結(jié)構(gòu)。由于最后的規(guī)則節(jié)點(diǎn) (F) 并沒有添加到 margin 結(jié)構(gòu),我們需要上溯規(guī)則樹,直至找到在先前節(jié)點(diǎn)插入中計(jì)算過的緩存結(jié)構(gòu),然后使用該結(jié)構(gòu)。我們會(huì)在指定 margin 規(guī)則的最上層節(jié)點(diǎn)(即 B 節(jié)點(diǎn))上找到該結(jié)構(gòu)。
我們已經(jīng)有了 color 結(jié)構(gòu)的定義,因此不能使用緩存的結(jié)構(gòu)。由于 color 有一個(gè)屬性,我們無需上溯規(guī)則樹以填充其他屬性。我們將計(jì)算端值(將字符串轉(zhuǎn)化為 RGB 等)并在此節(jié)點(diǎn)上緩存經(jīng)過計(jì)算的結(jié)構(gòu)。
第二個(gè) 元素處理起來更加簡(jiǎn)單。我們將匹配規(guī)則,最終發(fā)現(xiàn)它和之前的 span 一樣指向規(guī)則 G。由于我們找到了指向同一節(jié)點(diǎn)的同級(jí),就可以共享整個(gè)樣式上下文了,只需指向之前 span 的上下文即可。
對(duì)于包含了繼承自父代的規(guī)則的結(jié)構(gòu),緩存是在上下文樹中進(jìn)行的(事實(shí)上 color 屬性是繼承的,但是 Firefox 將其視為 reset 屬性,并緩存到規(guī)則樹上)。
例如,如果我們?cè)谀硞€(gè)段落中添加 font 規(guī)則:

那么,該段落元素作為上下文樹中的 div 的子代,就會(huì)共享與其父代相同的 font 結(jié)構(gòu)(前提是該段落沒有指定 font 規(guī)則)。

2>、Webkit的樣式解析

在 Webkit 中沒有規(guī)則樹,因此會(huì)對(duì)匹配的聲明遍歷 4 次。首先應(yīng)用非重要高優(yōu)先級(jí)的屬性(由于作為其他屬性的依據(jù)而應(yīng)首先應(yīng)用的屬性,例如 display),接著是高優(yōu)先級(jí)重要規(guī)則,然后是普通優(yōu)先級(jí)非重要規(guī)則,最后是普通優(yōu)先級(jí)重要規(guī)則。這意味著多次出現(xiàn)的屬性會(huì)根據(jù)正確的層疊順序進(jìn)行解析。最后出現(xiàn)的最終生效。

四、渲染樹的構(gòu)建

樣式樹和DOM樹連接在一起形成一個(gè)渲染樹,渲染樹用來計(jì)算可見元素的布局并且作為將像素渲染到屏幕上的過程的輸入。值得一提的是,Gecko 將視覺格式化元素組成的樹稱為“框架樹”。每個(gè)元素都是一個(gè)框架。Webkit 使用的術(shù)語(yǔ)是“渲染樹”,它由“呈現(xiàn)對(duì)象”組成。 Webkit 和 Gecko 使用的術(shù)語(yǔ)略有不同,但整體流程是基本相同的。

接下來將來看一下兩種渲染引擎的工作流程:
Webkit 主流程:

Mozilla 的 Gecko 呈現(xiàn)引擎主流程

雖然 Webkit 和 Gecko 使用的術(shù)語(yǔ)略有不同,但整體流程是基本相同的。

Gecko 將視覺格式化元素組成的樹稱為“框架樹”。每個(gè)元素都是一個(gè)框架。
Webkit 使用的術(shù)語(yǔ)是“呈現(xiàn)樹”,它由“呈現(xiàn)對(duì)象”組成。
對(duì)于元素的放置,Webkit 使用的術(shù)語(yǔ)是“布局”,而 Gecko 稱之為“重排”。
對(duì)于連接 DOM 節(jié)點(diǎn)和可視化信息從而創(chuàng)建呈現(xiàn)樹的過程,Webkit 使用的術(shù)語(yǔ)是“附加”。有一個(gè)細(xì)微的非語(yǔ)義差別,就是 Gecko 在 HTML 與 DOM 樹之間還有一個(gè)稱為“內(nèi)容槽”的層,用于生成 DOM 元素。我們會(huì)逐一論述流程中的每一部分。

五、關(guān)于瀏覽器渲染過程中需要了解的概念

Repaint(重繪)——屏幕的一部分要重畫,比如某個(gè)CSS的背景色變了。但是元素的幾何尺寸沒有變。
Reflow(重排)——意味著元件的幾何尺寸變了,我們需要重新驗(yàn)證并計(jì)算Render Tree。是Render Tree的一部分或全部發(fā)生了變化。這就是Reflow,或是Layout。reflow 會(huì)從這個(gè)root frame開始遞歸往下,依次計(jì)算所有的結(jié)點(diǎn)幾何尺寸和位置,在reflow過程中,可能會(huì)增加一些frame,比如一個(gè)文本字符串必需被包裝起來。
onload事件——當(dāng) onload 事件觸發(fā)時(shí),頁(yè)面上所有的DOM,樣式表,腳本,圖片,flash都已經(jīng)加載完成了。
DOMContentLoaded事件——當(dāng) DOMContentLoaded 事件觸發(fā)時(shí),僅當(dāng)DOM加載完成,不包括樣式表,圖片,flash。
首屏?xí)r間——當(dāng)瀏覽器顯示第一屏頁(yè)面所消耗的時(shí)間,在國(guó)內(nèi)的網(wǎng)絡(luò)條件下,通常一個(gè)網(wǎng)站,如果“首屏?xí)r間”在2秒以內(nèi)是比較優(yōu)秀的,5秒以內(nèi)用戶可以接受,10秒以上就不可容忍了。
白屏?xí)r間——指瀏覽器開始顯示內(nèi)容的時(shí)間。但是在傳統(tǒng)的采集方式里,是在HTML的頭部標(biāo)簽結(jié)尾里記錄時(shí)間戳,來計(jì)算白屏?xí)r間。在這個(gè)時(shí)刻,瀏覽器開始解析身體標(biāo)簽內(nèi)的內(nèi)容。而現(xiàn)代瀏覽器不會(huì)等待CSS樹(所有CSS文件下載和解析完成)和DOM樹(整個(gè)身體標(biāo)簽解析完成)構(gòu)建完成才開始繪制,而是馬上開始顯示中間結(jié)果。所以經(jīng)常在低網(wǎng)速的環(huán)境中,觀察到頁(yè)面由上至下緩慢顯示完,或者先顯示文本內(nèi)容后再重繪成帶有格式的頁(yè)面內(nèi)容。

六、頁(yè)面優(yōu)化方案

本文的主題在于從瀏覽器的渲染過程談頁(yè)面優(yōu)化。了解瀏覽器如何通信并將拿到的數(shù)據(jù)如何進(jìn)行解析渲染,本節(jié)將從網(wǎng)絡(luò)通信、頁(yè)面渲染、資源預(yù)取及如何除了以上方案外,如何借助chrome來針對(duì)一個(gè)頁(yè)面進(jìn)行實(shí)戰(zhàn)優(yōu)化四個(gè)方面來談。

從網(wǎng)絡(luò)通信過程入手可以做的優(yōu)化

減少DNS查找

每一次主機(jī)名解析都需要一次網(wǎng)絡(luò)往返,從而增加請(qǐng)求的延遲時(shí)間,同時(shí)還會(huì)阻塞后續(xù)請(qǐng)求。

重用TCP連接

盡可能使用持久連接,以消除 TCP 握手和慢啟動(dòng)延遲;

減少HTTP重定向

HTTP 重定向極費(fèi)時(shí)間,特別是不同域名之間的重定向,更加費(fèi)時(shí);這里面既有額外的 DNS 查詢、TCP 握手,還有其他延遲。最佳的重定向次數(shù)為零。

使用 CDN(內(nèi)容分發(fā)網(wǎng)絡(luò))

把數(shù)據(jù)放到離用戶地理位置更近的地方,可以顯著減少每次 TCP 連接的網(wǎng)絡(luò)延遲,增大吞吐量。

去掉不必要的資源

任何請(qǐng)求都不如沒有請(qǐng)求快。說到這,所有建議都無需解釋。延遲是瓶頸,最快的速度莫過于什么也不傳輸。然而,HTTP 也提供了很多額外的機(jī)制,比如緩存和壓縮,還有與其版本對(duì)應(yīng)的一些性能技巧。

在客戶端緩存資源

應(yīng)該緩存應(yīng)用資源,從而避免每次請(qǐng)求都發(fā)送相同的內(nèi)容。(瀏覽器緩存)

傳輸壓縮過的內(nèi)容

傳輸前應(yīng)該壓縮應(yīng)用資源,把要傳輸?shù)淖止?jié)減至最少:確保每種要傳輸?shù)馁Y源采用最好的壓縮手段。(Gzip,減少60%~80%的文件大?。?/p>

消除不必要的請(qǐng)求開銷

減少請(qǐng)求的 HTTP 首部數(shù)據(jù)(比如HTTPcookie),節(jié)省的時(shí)間相當(dāng)于幾次往返的延遲時(shí)間。

并行處理請(qǐng)求和響應(yīng)

請(qǐng)求和響應(yīng)的排隊(duì)都會(huì)導(dǎo)致延遲,無論是客戶端還是服務(wù)器端。這一點(diǎn)經(jīng)常被忽視,但卻會(huì)無謂地導(dǎo)致很長(zhǎng)延遲。

針對(duì)協(xié)議版本采取優(yōu)化措施

HTTP 1.x 支持有限的并行機(jī)制,要求打包資源、跨域分散資源,等等。相對(duì)而言,
HTTP 2.0 只要建立一個(gè)連接就能實(shí)現(xiàn)最優(yōu)性能,同時(shí)無需針對(duì) HTTP 1.x 的那些優(yōu)化方法。
但是壓縮、使用緩存、減少dns等的優(yōu)化方案無論在哪個(gè)版本都同樣適用

你需要了解的資源預(yù)取

preload :可以對(duì)當(dāng)前頁(yè)面所需的腳本、樣式等資源進(jìn)行預(yù)加載,而無需等到解析到 script 和 link 標(biāo)簽時(shí)才進(jìn)行加載。這一機(jī)制使得資源可以更早的得到加載并可用,且更不易阻塞頁(yè)面的初步渲染,進(jìn)而提升性能。
用法文檔:

https://developer.mozilla.org...

prefetch:prefetch 和 preload 一樣,都是對(duì)資源進(jìn)行預(yù)加載,但是 prefetch 一般預(yù)加載的是其他頁(yè)面會(huì)用到的資源。 當(dāng)然,prefetch 不會(huì)像 preload 一樣,在頁(yè)面渲染的時(shí)候加載資源,而是利用瀏覽器空閑時(shí)間來下載。當(dāng)進(jìn)入下一頁(yè)面,就可直接從 disk cache 里面取,既不影響當(dāng)前頁(yè)面的渲染,又提高了其他頁(yè)面加載渲染的速度。
用法文檔:

https://developer.mozilla.org...

subresource: 被Chrome支持了有一段時(shí)間,并且已經(jīng)有些搔到預(yù)加載當(dāng)前導(dǎo)航/頁(yè)面(所含有的資源)的癢處了。但它有一個(gè)問題——沒有辦法處理所獲取內(nèi)容的優(yōu)先級(jí)(as也并不存在),所以最終,這些資源會(huì)以一個(gè)相當(dāng)?shù)偷膬?yōu)先級(jí)被加載,這使得它能提供的幫助相當(dāng)有限

prerender:prerender 就像是在后臺(tái)打開了一個(gè)隱藏的 tab,會(huì)下載所有的資源、創(chuàng)建DOM、渲染頁(yè)面、執(zhí)行js等等。如果用戶進(jìn)入指定的鏈接,隱藏的這個(gè)頁(yè)面就會(huì)立馬進(jìn)入用戶的視線。 但是要注意,一定要在十分確定用戶會(huì)點(diǎn)擊某個(gè)鏈接時(shí)才使用該特性,否則客戶端會(huì)無端的下載很多資源和渲染這個(gè)頁(yè)面。 正如任何提前動(dòng)作一樣,預(yù)判總是有一定風(fēng)險(xiǎn)出錯(cuò)。如果提前的動(dòng)作是昂貴的(比如高CPU、耗電、占用帶寬),就要謹(jǐn)慎使用了。

preconnect: preconnect 允許瀏覽器在一個(gè) HTTP 請(qǐng)求正式發(fā)給服務(wù)器前預(yù)先執(zhí)行一些操作,這包括

dns-prefetch:通過 DNS 預(yù)解析來告訴瀏覽器未來我們可能從某個(gè)特定的 URL 獲取資源,當(dāng)瀏覽器真正使用到該域中的某個(gè)資源時(shí)就可以盡快地完成 DNS 解析

這些屬性雖然并非所有瀏覽器都支持,但是不支持的瀏覽器也只是不處理而已,而是別的話則會(huì)省去很多時(shí)間。因此,合理的使用資源預(yù)取可以顯著提高頁(yè)面性能。

高效合理的css選擇符可以減輕瀏覽器的解析負(fù)擔(dān)。

因?yàn)閏ss是逆向解析的所以應(yīng)當(dāng)避免多層嵌套。

避免使用通配規(guī)則。如 *{} 計(jì)算次數(shù)驚人!只對(duì)需要用到的元素進(jìn)行選擇

盡量少的去對(duì)標(biāo)簽進(jìn)行選擇,而是用class。如:#nav li{},可以為li加上nav_item的類名,如下選擇.nav_item{}

不要去用標(biāo)簽限定ID或者類選擇符。如:ul#nav,應(yīng)該簡(jiǎn)化為#nav

盡量少的去使用后代選擇器,降低選擇器的權(quán)重值。后代選擇器的開銷是最高的,盡量將選擇器的深度降到最低,最高不要超過三層,更多的使用類來關(guān)聯(lián)每一個(gè)標(biāo)簽元素。

考慮繼承。了解哪些屬性是可以通過繼承而來的,然后避免對(duì)這些屬性重復(fù)指定規(guī)則

從js層面談頁(yè)面優(yōu)化

①解決渲染阻塞
如果在解析HTML標(biāo)記時(shí),瀏覽器遇到了JavaScript,解析會(huì)停止。只有在該腳本執(zhí)行完畢后,HTML渲染才會(huì)繼續(xù)進(jìn)行。所以這阻塞了頁(yè)面的渲染。
解決方法:在標(biāo)簽中使用 async或defer特性
②減少對(duì)DOM的操作
對(duì)DOM操作的代價(jià)是高昂的,這在網(wǎng)頁(yè)應(yīng)用中的通常是一個(gè)性能瓶頸。
解決辦法:修改和訪問DOM元素會(huì)造成頁(yè)面的Repaint和Reflow,循環(huán)對(duì)DOM操作更是罪惡的行為。所以請(qǐng)合理的使用JavaScript變量?jī)?chǔ)存內(nèi)容,考慮大量DOM元素中循環(huán)的性能開銷,在循環(huán)結(jié)束時(shí)一次性寫入。
減少對(duì)DOM元素的查詢和修改,查詢時(shí)可將其賦值給局部變量。
③使用JSON格式來進(jìn)行數(shù)據(jù)交換
JSON是一種輕量級(jí)的數(shù)據(jù)交換格式,采用完全獨(dú)立于語(yǔ)言的文本格式,是理想的數(shù)據(jù)交換格式。同時(shí),JSON是 JavaScript原生格式,這意味著在 JavaScript 中處理 JSON數(shù)據(jù)不需要任何特殊的 API 或工具包。
④讓需要經(jīng)常改動(dòng)的節(jié)點(diǎn)脫離文檔流
因?yàn)橹乩L有時(shí)確實(shí)不可避免,所以只能盡可能限制重繪的影響范圍。

如何借助chrome針對(duì)性優(yōu)化頁(yè)面

首先打開控制臺(tái),點(diǎn)擊Audits一欄,會(huì)看到如下表單。在選取自己需要模擬測(cè)試的情況后點(diǎn)擊run audits,即可開始頁(yè)面性能分析。

然后將會(huì)得到分析結(jié)果及優(yōu)化建議:

我們可以逐項(xiàng)根據(jù)現(xiàn)有問題進(jìn)行優(yōu)化,如性能類目(performance)中的第一項(xiàng)優(yōu)化建議延遲加載屏幕外圖像(defer offscreen images),點(diǎn)擊后就能看到詳情以下詳情:

而具體頁(yè)面的指標(biāo)優(yōu)化可以根據(jù)給出的建議進(jìn)行逐條優(yōu)化。目前提供的性能分析及建議的列表包括性能分析、漸進(jìn)式web應(yīng)用、最佳實(shí)踐、無障礙訪問及搜索引擎優(yōu)化五個(gè)部分?;旧虾w了常見優(yōu)化方案及性能點(diǎn)的方方面面,開發(fā)時(shí)合理使用也能更好的提升頁(yè)面性能

相信以上優(yōu)化方案之所以行之有效的原因大都可以在本文中找出原因。理論是用來指導(dǎo)實(shí)踐的,即不能閉門造車式的埋頭苦干,也不能毫不實(shí)踐的夸夸其談。這樣才會(huì)形成完整的知識(shí)體系,讓知識(shí)體系樹更加龐大。知道該如何優(yōu)化是一回事,真正合理應(yīng)用是另一回事,要有好的性能,要著手于能做的每一件“小事”。

七、附錄

性能優(yōu)化是一門藝術(shù),更是一門綜合藝術(shù)。這其中涉及很多知識(shí)點(diǎn)。而這些知識(shí)點(diǎn)都有很多不錯(cuò)的文章進(jìn)行了總結(jié)。如果你想深入探究或許這里推薦的文章會(huì)給你啟發(fā)。

HTTP2詳解:

https://www.jianshu.com/p/e57...
TCP擁塞控制:

https://www.cnblogs.com/losby...
頁(yè)面性能分析網(wǎng)站:

https://gtmetrix.com/analyze....
Timing官方文檔:

https://www.w3.org/TR/navigat...
chrome中的高性能網(wǎng)絡(luò):

https://www.cnblogs.com/xuan5...

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/104794.html

相關(guān)文章

  • 網(wǎng)站性能前端性能優(yōu)化

    摘要:淺談網(wǎng)站性能之前端性能優(yōu)化性能優(yōu)化的目的無非是減少用戶流量消耗,提升用戶首屏體驗(yàn),提升用戶訪問速度,讓用戶專注內(nèi)容本身。前端性能優(yōu)化減少請(qǐng)求數(shù)量基本原理在瀏覽器與服務(wù)器進(jìn)行通信時(shí),主要是通過進(jìn)行通信。 最近項(xiàng)目慢慢走上正軌,需求趨于平穩(wěn),這才想起需要對(duì)整站進(jìn)行性能優(yōu)化。經(jīng)過一段時(shí)間的學(xué)習(xí),結(jié)合現(xiàn)在項(xiàng)目的實(shí)際性能情況,發(fā)現(xiàn)確實(shí)有許多地方可以進(jìn)行優(yōu)化。于是就開始了我的前端性能優(yōu)化之旅。以下...

    Winer 評(píng)論0 收藏0
  • 網(wǎng)站性能前端性能優(yōu)化

    摘要:淺談網(wǎng)站性能之前端性能優(yōu)化性能優(yōu)化的目的無非是減少用戶流量消耗,提升用戶首屏體驗(yàn),提升用戶訪問速度,讓用戶專注內(nèi)容本身。前端性能優(yōu)化減少請(qǐng)求數(shù)量基本原理在瀏覽器與服務(wù)器進(jìn)行通信時(shí),主要是通過進(jìn)行通信。 最近項(xiàng)目慢慢走上正軌,需求趨于平穩(wěn),這才想起需要對(duì)整站進(jìn)行性能優(yōu)化。經(jīng)過一段時(shí)間的學(xué)習(xí),結(jié)合現(xiàn)在項(xiàng)目的實(shí)際性能情況,發(fā)現(xiàn)確實(shí)有許多地方可以進(jìn)行優(yōu)化。于是就開始了我的前端性能優(yōu)化之旅。以下...

    philadelphia 評(píng)論0 收藏0
  • 前端性能優(yōu)化

    摘要:端優(yōu)談?wù)勱P(guān)于前端的緩存的問題我們都知道對(duì)頁(yè)面進(jìn)行緩存能夠有利于減少請(qǐng)求發(fā)送,從而達(dá)到對(duì)頁(yè)面的優(yōu)化。而作為一名有追求的前端,勢(shì)必要力所能及地優(yōu)化我們前端頁(yè)面的性能。這種方式主要解決了淺談前端中的過早優(yōu)化問題過早優(yōu)化是萬(wàn)惡之源。 優(yōu)化向:?jiǎn)雾?yè)應(yīng)用多路由預(yù)渲染指南 Ajax 技術(shù)的出現(xiàn),讓我們的 Web 應(yīng)用能夠在不刷新的狀態(tài)下顯示不同頁(yè)面的內(nèi)容,這就是單頁(yè)應(yīng)用。在一個(gè)單頁(yè)應(yīng)用中,往往只有一...

    Dean 評(píng)論0 收藏0
  • 2018 淺前端面試那些事

    摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒有換工作的打算 但為了跟上時(shí)代的腳步 還是忍不住整理了一份最新前端知識(shí)點(diǎn) 知識(shí)點(diǎn)匯總 1.HTML HTML5新特性,語(yǔ)義化瀏覽器的標(biāo)準(zhǔn)模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標(biāo)簽canvasHTML廢棄的標(biāo)簽IE6 bug,和一些定位寫法css js放置位置和原因...

    LiuRhoRamen 評(píng)論0 收藏0
  • 2018 淺前端面試那些事

    摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒有換工作的打算 但為了跟上時(shí)代的腳步 還是忍不住整理了一份最新前端知識(shí)點(diǎn) 知識(shí)點(diǎn)匯總 1.HTML HTML5新特性,語(yǔ)義化瀏覽器的標(biāo)準(zhǔn)模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標(biāo)簽canvasHTML廢棄的標(biāo)簽IE6 bug,和一些定位寫法css js放置位置和原因...

    stormgens 評(píng)論0 收藏0
  • 2018 淺前端面試那些事

    摘要:聲明的變量不得改變值,這意味著,一旦聲明變量,就必須立即初始化,不能留到以后賦值。 雖然今年沒有換工作的打算 但為了跟上時(shí)代的腳步 還是忍不住整理了一份最新前端知識(shí)點(diǎn) 知識(shí)點(diǎn)匯總 1.HTML HTML5新特性,語(yǔ)義化瀏覽器的標(biāo)準(zhǔn)模式和怪異模式xhtml和html的區(qū)別使用data-的好處meta標(biāo)簽canvasHTML廢棄的標(biāo)簽IE6 bug,和一些定位寫法css js放置位置和原因...

    Hujiawei 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<