前言
最近跟同事一直忙于關(guān)于前端項(xiàng)目的性能分析以及性能優(yōu)化,前端性能直接影響了用戶的體驗(yàn),針對(duì)于前端性能問題,一直是一個(gè)大家熱議的話題,也并沒有一個(gè)比較完整通用的解決方案,以下是我個(gè)人的一些認(rèn)識(shí)與實(shí)踐。
性能優(yōu)化概述
從輸入 URL 到頁面加載完成,完整的鏈路

1.DNS 解析
2.TCP 連接
3.HTTP 請(qǐng)求拋出
4.服務(wù)端處理請(qǐng)求,HTTP 響應(yīng)返回
5.瀏覽器拿到響應(yīng)數(shù)據(jù),解析響應(yīng)內(nèi)容,把解析的結(jié)果展示給用戶
整個(gè)性能消化

http層面優(yōu)化
- DNS 解析:
DNS 實(shí)現(xiàn)域名到IP的映射。通過域名訪問站點(diǎn),每次請(qǐng)求都要做DNS解析。目前每次DNS解析,通常在200ms以下。一般采用DNS Prefetch 一種DNS 預(yù)解析技術(shù),當(dāng)你瀏覽網(wǎng)頁時(shí),瀏覽器會(huì)在加載網(wǎng)頁時(shí)對(duì)網(wǎng)頁中的域名進(jìn)行解析緩存,這樣在你單擊當(dāng)前網(wǎng)頁中的連接時(shí)就無需進(jìn)行DNS的解析,減少用戶等待時(shí)間,提高用戶體驗(yàn)。
<link rel="dns-prefetch" href="www.baidu.com" />
只有部分瀏覽器支持
TCP 連接:
采用http2.0,可以復(fù)用tcp通道,采用二進(jìn)制格式而非文本格式,使用報(bào)頭壓縮,HTTP/2降低了開銷,支持cache push瀏覽器并發(fā)
基于端口跟線程切換開銷,瀏覽器不可能無限的并發(fā)請(qǐng)求。chrome的并發(fā)為6,超過限制數(shù)目的請(qǐng)求就會(huì)被阻塞;
對(duì)于某些靜態(tài)資源,圖片等等,我們可以對(duì)其URL分散處理 ,不同的資源域名(部署在cdn上)。http請(qǐng)求次數(shù)
減少http的請(qǐng)求次數(shù),將多個(gè)請(qǐng)求合并成同一個(gè),減少http的開銷webpack
充分利用webpack提供給我們的能力,利用DllPlugin與commonPlugins等插件對(duì)我們代碼進(jìn)行
優(yōu)化,文件的分割與合并,公共代碼的提取,長緩存等策略,webpack是個(gè)很好的東西,值得大家仔細(xì)研究http壓縮
采用Gzip壓縮:HTTP 壓縮就是以縮小體積為目的,對(duì) HTTP 內(nèi)容進(jìn)行重新編碼的過程,原理是找出一些重復(fù)出現(xiàn)的字符串、臨時(shí)替換它們,從而使整個(gè)文件變小,文件中代碼的重復(fù)率越高,那么壓縮的效率就越高,使用 Gzip 的收益也就越大
圖片優(yōu)化

緩存
瀏覽器緩存
-
強(qiáng)緩存:
瀏覽器在請(qǐng)求某一資源時(shí),會(huì)先獲取該資源緩存的header信息,判斷是否命中強(qiáng)緩存(cache-control和expires信息),若命中直接從緩存中獲取資源信息,包括緩存header信息;本次請(qǐng)求根本就不會(huì)與服務(wù)器進(jìn)行通信- expires:這是http1.0時(shí)的規(guī)范;它的值為一個(gè)絕對(duì)時(shí)間的GMT格式的時(shí)間字符串,如Mon, 10 Jun 2015 21:31:12 GMT,如果發(fā)送請(qǐng)求的時(shí)間在expires之前,那么本地緩存始終有效,否則就會(huì)發(fā)送請(qǐng)求到服務(wù)器來獲取資源
- cache-control:max-age=number,這是http1.1時(shí)出現(xiàn)的header信息,主要是利用該字段的max-age值來進(jìn)行判斷,它是一個(gè)相對(duì)值;資源第一次的請(qǐng)求時(shí)間和Cache-Control設(shè)定的有效期,計(jì)算出一個(gè)資源過期時(shí)間,再拿這個(gè)過期時(shí)間跟當(dāng)前的請(qǐng)求時(shí)間比較,如果請(qǐng)求時(shí)間在過期時(shí)間之前,就能命中緩存,否則就不行;cache-control除了該字段外,還有下面幾個(gè)比較常用的設(shè)置值:no-cache ,no-store,public,private
-
協(xié)商緩存(對(duì)比緩存)
- Last-Modified/If-Modified-Since:第一次請(qǐng)求,服務(wù)端在Response Headers :Last-Modified:Fri, 27 Oct 2017 06:35:57 GMT,也就是服務(wù)端最后修改該資源的時(shí)間。
瀏覽器再次跟服務(wù)器請(qǐng)求這個(gè)資源時(shí),在request的header上加上If-Modified-Since的header,這個(gè)header的值就是上一次請(qǐng)求時(shí)返回的Last-Modified的值,服務(wù)器進(jìn)行比較,如果相同則返回304,否則瀏覽器直接從服務(wù)器加載資源時(shí),Last-Modified的Header在重新加載的時(shí)候會(huì)被更新,下次請(qǐng)求時(shí),If-Modified-Since會(huì)啟用上次返回的Last-Modified值 - Etag/If-None-Match: 服務(wù)器會(huì)為每個(gè)資源生成一個(gè)唯一的標(biāo)識(shí)字符串,只要文件內(nèi)容不同,它們對(duì)應(yīng)的 Etag 就是不同的;If-Modified-Since能檢查到的精度是s級(jí)的,某些服務(wù)器不能精確的得到文件的最后修改時(shí)間,我們編輯了文件,但文件的內(nèi)容沒有改變。因?yàn)榉?wù)器是根據(jù)文件的最后修改時(shí)間來判斷的,導(dǎo)致重新請(qǐng)求所以才出現(xiàn)了Etag,Etag對(duì)服務(wù)器也有性能損耗
Last-Modified與ETag是可以一起使用的,服務(wù)器會(huì)優(yōu)先驗(yàn)證ETag,一致的情況下,才會(huì)繼續(xù)比對(duì)Last-Modified,最后才決定是否返回304。
- Last-Modified/If-Modified-Since:第一次請(qǐng)求,服務(wù)端在Response Headers :Last-Modified:Fri, 27 Oct 2017 06:35:57 GMT,也就是服務(wù)端最后修改該資源的時(shí)間。
-
請(qǐng)求過程總結(jié):
image.png memory cache 與 disk cache
from memory cache代表使用內(nèi)存中的緩存,from disk cache則代表使用的是硬盤中的緩存,瀏覽器讀取緩存的順序?yàn)閙emory –> disk。在瀏覽器中,瀏覽器會(huì)在js和圖片等文件解析執(zhí)行后直接存入內(nèi)存緩存中,那么當(dāng)刷新頁面時(shí)只需直接從內(nèi)存緩存中讀取(from memory cache);而css文件則會(huì)存入硬盤文件中,所以每次渲染頁面都需要從硬盤讀取緩存(from disk cache)。CDN緩存
CDN緩存一般是由網(wǎng)站管理員自己部署,為了讓他們的網(wǎng)站更容易擴(kuò)展并獲得更好的性能。通常情況下,瀏覽器先向CDN網(wǎng)關(guān)發(fā)起Web請(qǐng)求,網(wǎng)關(guān)服務(wù)器后面對(duì)應(yīng)著一臺(tái)或多臺(tái)負(fù)載均衡源服務(wù)器,會(huì)根據(jù)它們的負(fù)載請(qǐng)求,動(dòng)態(tài)將請(qǐng)求轉(zhuǎn)發(fā)到合適的源服務(wù)器上。從瀏覽器角度來看,整個(gè)CDN就是一個(gè)源服務(wù)器,從這個(gè)層面來說,瀏覽器和服務(wù)器之間的緩存機(jī)制,在這種架構(gòu)下同樣適用應(yīng)用緩存
Cookie:同一個(gè)域名下的所有請(qǐng)求,都會(huì)攜帶 Cookie,大小限制4kb
Session Storage:用來存儲(chǔ)生命周期和它同步的會(huì)話級(jí)別的信息,關(guān)閉瀏覽器就不存在
Local Storage:持久化緩存 5-10Mb
Service Worker緩存:
pwa,會(huì)攔截http請(qǐng)求,對(duì)資源進(jìn)行離線緩存、消息推送,無法直接訪問dom,
利用workbox插件非常容易接入pwa技術(shù)
瀏覽器渲染
瀏覽器渲染機(jī)制
- DOM樹:
解析 HTML 以創(chuàng)建的是 DOM 樹(DOM tree ):渲染引擎開始解析 HTML 文檔,轉(zhuǎn)換樹中的標(biāo)簽到 DOM 節(jié)點(diǎn),它被稱為“內(nèi)容樹”。 - CSSOM樹:
解析 CSS(包括外部 CSS 文件和樣式元素)創(chuàng)建的是 CSSOM 樹。CSSOM 的解析過程與 DOM 的解析過程是并行的。
-渲染樹:
CSSOM 與 DOM 結(jié)合,之后我們得到的就是渲染樹(Render tree )。 - 布局渲染樹:
從根節(jié)點(diǎn)遞歸調(diào)用,計(jì)算每一個(gè)元素的大小、位置等,給每個(gè)節(jié)點(diǎn)所應(yīng)該出現(xiàn)在屏幕上的精確坐標(biāo),我們便得到了基于渲染樹的布局渲染樹(Layout of the render tree)。 - 繪制渲染樹:
遍歷渲染樹,每個(gè)節(jié)點(diǎn)將使用 UI 后端層來繪制。整個(gè)過程叫做繪制渲染樹(Painting the render tree)。
阻塞
- 普通模式,JS 會(huì)阻塞瀏覽器,瀏覽器必須等待 index.js 加載和執(zhí)?完畢才能去做其它事情。一般將此類js放在在<body>標(biāo)簽的底部,減少對(duì)整個(gè)頁面下載的影響
<script src="index.js"></script>
- async 模式:JS 不會(huì)阻塞瀏覽器做任何其它的事情。它的加載是異步的,當(dāng)它加載結(jié)束,JS 腳本會(huì)?即執(zhí)?。
<script async src="index.js"></script>
- defer 模式:JS 的加載是異步的,執(zhí)?是被推遲的。等整個(gè)?檔解析完成DOMContentLoaded 事件即將被觸發(fā)時(shí),被標(biāo)記了defer 的 JS ?件才會(huì)開始依次執(zhí)?
<script defer src="index.js"></script>
?般當(dāng)我們的腳本與 DOM 元素和其它腳本之間的依賴關(guān)系不強(qiáng)時(shí),我們會(huì)選? async;當(dāng)腳本依賴于 DOM
元素和其它腳本的執(zhí)?結(jié)果時(shí),我們會(huì)選? defer。
- 動(dòng)態(tài)加載腳本:此文件當(dāng)元素添加到頁面之后立刻開始下載。無論在何處啟動(dòng)下載,文件的下載和運(yùn)行都不會(huì)阻塞其他頁面處理過程。甚至可以將這些代碼放在<head>部分而不會(huì)對(duì)其余部分的頁面代碼造成影響
var script = document.createElement ("script");
script.type = "text/javascript";
script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);
服務(wù)器ssr渲染
定義:
服務(wù)端渲染的模式下,當(dāng)?戶第?次請(qǐng)求??時(shí),由服務(wù)器把需要的組件或??渲染成 HTML字符串,然后把它返回給客戶端。客戶端拿到?的,是可以直接渲染然后呈現(xiàn)給?戶的 HTML 內(nèi)容,不需要為了?成 DOM 內(nèi)容??再去跑?遍 JS 代碼。所見即為所得優(yōu)缺點(diǎn):
SEO :可以有“現(xiàn)成的內(nèi)容”拿給搜索引擎看
?屏加載速度:服務(wù)端渲染模式下,服務(wù)器給到客戶端的已經(jīng)是?個(gè)服務(wù)端處理好的可以拿來呈現(xiàn)給?戶的??
缺點(diǎn): ?常吃硬件資源
css、js性能
編碼能力
