首先我們需要明確一個(gè)問(wèn)題:將url輸入瀏覽器到頁(yè)面的出現(xiàn)經(jīng)歷了什么?可以歸納為兩個(gè)關(guān)鍵方面:瀏覽器渲染流程;網(wǎng)絡(luò)交互層面上的優(yōu)化。
一、瀏覽器渲染流程
構(gòu)建DOM樹(shù)、CSSOM樹(shù)、渲染樹(shù)
(1)構(gòu)建DOM樹(shù)

- 轉(zhuǎn)換: 瀏覽器從磁盤(pán)或網(wǎng)絡(luò)讀取 HTML 的原始字節(jié),并根據(jù)文件的指定編碼(例如 UTF-8)將它們轉(zhuǎn)換成各個(gè)字符。
- 令牌化: 瀏覽器將字符串轉(zhuǎn)換成 W3C HTML5 標(biāo)準(zhǔn)規(guī)定的各種令牌,例如,“<html>”、“<body>”,以及其他尖括號(hào)內(nèi)的字符串。每個(gè)令牌都具有特殊含義和一組規(guī)則。
- 詞法分析: 發(fā)出的令牌轉(zhuǎn)換成定義其屬性和規(guī)則的“對(duì)象”。
- DOM 構(gòu)建: 最后,由于 HTML 標(biāo)記定義不同標(biāo)記之間的關(guān)系(一些標(biāo)記包含在其他標(biāo)記內(nèi)),創(chuàng)建的對(duì)象鏈接在一個(gè)樹(shù)數(shù)據(jù)結(jié)構(gòu)內(nèi),此結(jié)構(gòu)也會(huì)捕獲原始標(biāo)記中定義的父項(xiàng)-子項(xiàng)關(guān)系:HTML 對(duì)象是 body 對(duì)象的父項(xiàng),body 是 paragraph 對(duì)象的父項(xiàng),依此類推。
整個(gè)流程的最終輸出是我們這個(gè)簡(jiǎn)單頁(yè)面的文檔對(duì)象模型 (DOM),瀏覽器對(duì)頁(yè)面進(jìn)行的所有進(jìn)一步處理都會(huì)用到它。
瀏覽器每次處理 HTML 標(biāo)記時(shí),都會(huì)完成以上所有步驟:將字節(jié)轉(zhuǎn)換成字符,確定令牌,將令牌轉(zhuǎn)換成節(jié)點(diǎn),然后構(gòu)建 DOM 樹(shù)。這整個(gè)流程可能需要一些時(shí)間才能完成,有大量 HTML 需要處理時(shí)更是如此。
根據(jù)以上結(jié)構(gòu)分析,使DOM樹(shù)構(gòu)建更快:
1. 標(biāo)簽語(yǔ)義化。
2. 避免深層次嵌套。
(2)CSSOM樹(shù)(CSS 對(duì)象模型)

cssom的構(gòu)建與dom樹(shù)構(gòu)建流程基本相同
根據(jù)以上分析,使CSSOM樹(shù)構(gòu)建更快:
CSS是從右到左解析的,因此也要避免嵌套層級(jí)過(guò)多。(使用less、scss等css預(yù)處理器時(shí),也要盡可能少的使用層級(jí)嵌套)
(3)渲染樹(shù)

1、 真正渲染到頁(yè)面中的時(shí)候:一定發(fā)生在DOM-tree和CSSOM樹(shù)都已經(jīng)完成了。
2、 HTML和CSS都是阻礙頁(yè)面渲染的東西。
3、 當(dāng)加載html代碼時(shí),是主線程加載,此時(shí)是自上而下的解析代碼。當(dāng)遇到link時(shí)就相當(dāng)于發(fā)送一個(gè)http請(qǐng)求,每個(gè)http請(qǐng)求都是單獨(dú)的線程去處理,http在谷歌里面一般同時(shí)可以發(fā)送6-7個(gè)。@import是同步的,會(huì)阻塞渲染。遇到<style></style>時(shí),它是在html拿回來(lái)時(shí)就已經(jīng)請(qǐng)求,減少發(fā)送請(qǐng)求,(當(dāng)style樣式比較少時(shí),比link稍微較好)。

優(yōu)化方案:
● 標(biāo)簽語(yǔ)義化和避免深層次嵌套
● CSS選擇器渲染是從右到左,避免多層級(jí)選擇器的嵌套
● 盡早盡快地把CSS下載到客戶端(充分利用HTTP多請(qǐng)求并發(fā)機(jī)制),當(dāng)css樣式較少時(shí),優(yōu)先使用style。其次就是link,它就相當(dāng)于發(fā)送一個(gè)http請(qǐng)求,是異步的。不建議使用@import,因?yàn)樗峭降?。將css放在頂部,從而更早的發(fā)送請(qǐng)求。
● 避免js阻塞,故將其放在底部。(也可以使用異步加載 ,使用async(無(wú)序的加載)或者defer(等頁(yè)面渲染完了再依次加載))
● 減少DOM的回流和重繪(【回流:元素的大小或者位置發(fā)生了變化(當(dāng)頁(yè)面布局和幾何信息發(fā)生變化的時(shí)候),觸發(fā)了重新布局,導(dǎo)致渲染樹(shù)重新計(jì)算布局和渲染】?!局乩L:元素樣式的改變(但寬高、大小位置等不變)】)
修改了元素的位置或者結(jié)構(gòu)等,就會(huì)觸發(fā)回流
元素樣式更改,則會(huì)觸發(fā)重繪。
回流一定會(huì)引發(fā)重繪,重繪不一定會(huì)引發(fā)回流。
image.png
image.png
二、網(wǎng)絡(luò)交互層面上的優(yōu)化
具體什么是DNS?
DNS( Domain Name System)是“域名系統(tǒng)”的英文縮寫(xiě),是一種組織成域?qū)哟谓Y(jié)構(gòu)的計(jì)算機(jī)和網(wǎng)絡(luò)服務(wù)命名系統(tǒng),它用于TCP/IP網(wǎng)絡(luò),它所提供的服務(wù)是用來(lái)將主機(jī)名和域名轉(zhuǎn)換為IP地址的工作。DNS就是這樣的一位“翻譯官”,它的基本工作原理可用下圖來(lái)表示。

1. DNS方面的優(yōu)化
每一次DNS解析時(shí)間預(yù)計(jì)在20—120毫秒
● 減少DNS請(qǐng)求次數(shù)
● DNS預(yù)獲?。―NS Prefetch)
// 提前解析DNS,并緩存到本地,此過(guò)程可以理解成異步
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" />
<link rel="dns-prefetch" />
<link rel="dns-prefetch" />
<link rel="dns-prefetch" />
2. 減少HTTP請(qǐng)求次數(shù)和請(qǐng)求資源大小
● 資源合并壓縮
● 字體圖標(biāo)(iconfont)
● Base64
● GZIP(一般的文件能壓縮60%多)
● 圖片懶加載
● 數(shù)據(jù)延遲分批加載
● CDN資源
3. 應(yīng)用緩存
緩存位置
- Service Worker:瀏覽器獨(dú)立線程進(jìn)行緩存
- Memory Cache:內(nèi)存緩存(內(nèi)存相對(duì)硬盤(pán)小,但是讀取快)
- Disk Cache:硬盤(pán)緩存(內(nèi)存大,但是讀取慢)
- Push Cache:推送緩存(HTTP/2中的)
打開(kāi)網(wǎng)頁(yè),地址欄輸入地址:查找disk cache中是否有匹配,如果有則使用,如果沒(méi)有則發(fā)送網(wǎng)絡(luò)請(qǐng)求。
普通刷新(F5):因?yàn)門(mén)AB并沒(méi)有關(guān)閉,因此memory cache是可用的,會(huì)被優(yōu)先使用(如果匹配的話),其次才是disk Cache。
強(qiáng)制刷新(Ctrl+F5):瀏覽器不使用緩存,因此發(fā)送的請(qǐng)求頭部均帶有Cache-control:no-cache(為了兼容,還帶了Pragma:no-cache),服務(wù)器直接返回200和最新內(nèi)容。
1)強(qiáng)緩存Expires/Cache-Control
瀏覽器對(duì)于強(qiáng)緩存的處理:根據(jù)第一次請(qǐng)求資源時(shí)返回的響應(yīng)頭來(lái)確定的

兩者同時(shí)存在的話,Cache-Control優(yōu)先級(jí)高于Expires
2)協(xié)商緩存Last-Modified/ETag
協(xié)商緩存就是強(qiáng)制緩存失效后,瀏覽器攜帶緩存標(biāo)識(shí)向服務(wù)器發(fā)起請(qǐng)求,由服務(wù)器根據(jù)緩存標(biāo)識(shí)決定是否使用緩存的過(guò)程。
● 協(xié)商緩存生效,返回304和Not Modified
image.png
image.png



