前端面試題庫 (面試必備)?? ? ? ? ? ?推薦:★★★★★
地址:前端面試題庫文章來源地址http://www.zghlxwxcb.cn/news/detail-665340.html
性能優(yōu)化
一、性能指標(biāo)
要在 Chrome 中查看性能指標(biāo),可以按照以下步驟操作:
- 打開 Chrome 瀏覽器,并訪問你想要測(cè)試的網(wǎng)頁。
- 使用快捷鍵 F12 或右鍵點(diǎn)擊頁面并選擇 “檢查”,打開開發(fā)者工具。
- 在開發(fā)者工具中,切換到 “Performance”(性能)選項(xiàng)卡。
- 點(diǎn)擊開始錄制按鈕,即紅色的圓點(diǎn)按鈕。開始加載頁面。
- 等待頁面加載完成,然后停止錄制,點(diǎn)擊紅色的圓點(diǎn)按鈕或按下快捷鍵 Ctrl + E。
- 在性能分析結(jié)果中,你會(huì)看到一些性能指標(biāo)圖表。在 “Timings”(時(shí)間)圖表中,你可以找到各個(gè)性能指標(biāo)的時(shí)間點(diǎn)。
常規(guī)指標(biāo)
- DCL:DOMContentLoaded 事件
- L:Onload 事件
- FP - First Paint:首次繪制,瀏覽器開始將像素繪制到屏幕上的時(shí)間點(diǎn)。
- FCP - First Contentful Paint:首次內(nèi)容繪制,表示瀏覽器在加載頁面時(shí)第一次繪制了一部分頁面內(nèi)容(文本、圖像等)的時(shí)間點(diǎn)。它標(biāo)志著用戶可見的內(nèi)容開始出現(xiàn)在屏幕上的時(shí)間。
- FMP - First Meaningful Paint:首次有意義繪制,指?面關(guān)鍵元素渲染時(shí)間。這個(gè)概念并沒有標(biāo)準(zhǔn)化定義,因?yàn)殛P(guān)鍵元素可以由開發(fā)者自行定義——究竟什么是“有意義”的內(nèi)容,只有開發(fā)者或者產(chǎn)品經(jīng)理自己了解。
- TTI - Time To Interactive:用戶可以與頁面進(jìn)行交互的時(shí)間點(diǎn)。一般來講,我們認(rèn)為是 domready 的時(shí)間,因?yàn)槲覀兺ǔ?huì)在這時(shí)候綁定事件操作。如果?面中涉及交互的腳本沒有下載完成,那么當(dāng)然沒有到達(dá)所謂的用戶可交互時(shí)間。那么如何定義 domready 時(shí)間呢?
- TTFB - Time To First Byte:發(fā)出?面請(qǐng)求到接收到應(yīng)答數(shù)據(jù)第一個(gè)字節(jié)所花費(fèi)的毫秒數(shù)。
Chrome 最新指標(biāo)
- LCP - Largest Contentful Paint:最大內(nèi)容繪制,衡量?面的加載體驗(yàn),它表示視口內(nèi)可?的最大內(nèi)容元素的渲染時(shí)間。相比 FCP,這個(gè)指標(biāo)可以更加真實(shí)地反映具體內(nèi)容加載速度。比如,如果?面渲染前有一個(gè) loading 動(dòng)畫,那么 FCP 可能會(huì)以 loading 動(dòng)畫出現(xiàn)的時(shí)間為準(zhǔn),而 LCP 定義了 loading 動(dòng)畫加載后,真實(shí)渲染出內(nèi)容的時(shí)間。
- FID - First Input Delay:首次輸入延遲,衡量可交互性,它表示用戶和?面進(jìn)行首次交互操作所花費(fèi)的時(shí)間。它比 TTI(Time To Interactive)更加提前,這個(gè)階段雖然?面已經(jīng)顯示出部分內(nèi)容,但并不能完全具備可交互性,對(duì)于用戶的響應(yīng)可能會(huì)有較大的延遲。
- CLS - Cumulative Layout Shift:積累布局位移,是一個(gè)衡量頁面布局穩(wěn)定性的性能指標(biāo),它衡量頁面上元素在加載過程中發(fā)生的意外布局變化的總和。CLS 反映了用戶在瀏覽頁面時(shí)會(huì)遇到的突然、意外的元素移動(dòng),導(dǎo)致用戶難以準(zhǔn)確點(diǎn)擊操作或?yàn)g覽內(nèi)容。CLS 的值通常為在加載期間發(fā)生布局變化的元素面積的百分比,范圍是從 0 到 1。CLS 值越接近 0,表示頁面布局越穩(wěn)定,用戶體驗(yàn)越好。如果 CLS 值較高,則可能需要優(yōu)化頁面布局,例如確定元素的固定尺寸、合理設(shè)置圖片和廣告的尺寸等。通過監(jiān)測(cè) CLS,可以改進(jìn)頁面的布局穩(wěn)定性,提高用戶體驗(yàn)和滿意度。
總下載時(shí)間
?面所有資源加載完成所需要的時(shí)間。一般可以統(tǒng)計(jì) window.onload 時(shí)間,這樣可以統(tǒng)計(jì)出同步加載的資源全部加載完的耗時(shí)。如果?面中存在較多異步渲染,也可以將異步渲染全部完成的時(shí)間作為總下載時(shí)間。
DOMContentLoaded 與 load 事件的區(qū)別
DOMContentLoaded 指的是文檔中 DOM 結(jié)構(gòu)加載完畢的時(shí)間,也就是說 HTML 結(jié)構(gòu)已經(jīng)完整。但是我們知道,頁面可能包含了圖片,特殊字體,視頻,音頻等資源,這些資源由網(wǎng)絡(luò)請(qǐng)求獲取,DOM 結(jié)構(gòu)加載完畢時(shí),由于這些資源往往額外的網(wǎng)絡(luò)請(qǐng)求,這些資源可能還有沒有請(qǐng)求或者渲染完成。而當(dāng)頁面上所有資源加載完成后,load 時(shí)間見才會(huì)被觸發(fā)。因此,在時(shí)間線上,load 事件往往會(huì)落后于 DOMContentLoaded 事件。
關(guān)于 DOMContentLoaded 和 domReady
我們簡(jiǎn)單說一下,瀏覽器是從上到下,從左到右,一個(gè)個(gè)字符串讀入,大致可以認(rèn)為兩個(gè)同名的開標(biāo)簽和閉標(biāo)簽就是一個(gè) DOM(有的是沒有閉標(biāo)簽),這時(shí)候就忽略掉兩個(gè)標(biāo)簽之間的內(nèi)容。頁面上有許多標(biāo)簽,但標(biāo)簽會(huì)生成同樣多的 DOM,因?yàn)橛械臉?biāo)簽下允許存在特定的子標(biāo)簽,比如:tr 下面一定是 td, th, select 下面一定是 opgrounp, option, 而 option 下面,就算你寫了?<span></span>
?,它都會(huì)忽略掉,option 只存在文本,這就是我們需要自定義下拉框的原因。
我們說過,這個(gè)順序是從上到下的,有的元素很簡(jiǎn)單,構(gòu)建的很快,但是標(biāo)簽存在 src, href 屬性,它們會(huì)引用外部資源,這就要區(qū)別對(duì)待了。比如說,script 標(biāo)簽,它一定會(huì)等 src 指定的腳本文件加載下來,然后全部執(zhí)行了里面的腳本,才會(huì)分析下一個(gè)標(biāo)簽,這種現(xiàn)象叫做堵塞。
堵塞是一種非常致命的現(xiàn)象,因?yàn)闉g覽器渲染引擎是單線程的,如果頭部引入的腳本過多會(huì)導(dǎo)致白屏,影響用戶體驗(yàn),因此雅虎的 20 軍規(guī)中就有一條提到,所有的 script 標(biāo)簽都放到 body 之后。
此外,style 標(biāo)簽 與 link 標(biāo)簽,它們?cè)诩虞d樣式文件時(shí)是不會(huì)堵塞的,但是它們一旦加載好,就會(huì)立即開始渲染已經(jīng)構(gòu)建好的節(jié)點(diǎn)元素,這可能會(huì)引起 reflow ,這也影響速度。
另外一個(gè)影響 DOM 樹構(gòu)建的是 iframe,它也會(huì)加載資源,雖然不會(huì)堵塞 DOM 構(gòu)建,但它由于發(fā)出 HTTP 請(qǐng)求,而 HTTP 請(qǐng)求時(shí)有限的,它會(huì)與父標(biāo)簽的其它需要加載外部資源的標(biāo)簽產(chǎn)生競(jìng)爭(zhēng)。我們經(jīng)??吹揭恍┬侣劸W(wǎng)上面掛了很多 iframe 廣告,這些頁面一開始加載時(shí)就很卡就是就是這個(gè)緣故。
此外還有 object 元素,用來加載 flash。document.getElementById()
?等等,這些東西會(huì)影響到 DOM 樹的構(gòu)建過程。因此在這時(shí)候,當(dāng)我們貿(mào)貿(mào)然使用?getElementById
,?getElementsByTagName
?獲取元素,然后操作它們,就會(huì)很大幾率碰到元素為 null 的異常,這時(shí)候目標(biāo)元素還有轉(zhuǎn)換為 DOM 節(jié)點(diǎn),還只是一個(gè)普通的字符串呢!
二、如何獲取這些指標(biāo)
?文章來源:http://www.zghlxwxcb.cn/news/detail-665340.html
通過?performance.timing
?獲取
performance.timing
字段 | 含義 |
---|---|
navigationStart | 加載的起始時(shí)間,如果沒有前一個(gè)頁面的 unload,則與 fetchStart 值相等 |
redirectStart | 重定向開始時(shí)間(如果發(fā)生了 HTTP 重定向,每次重定向都和當(dāng)前文檔同域的話,就返回開始重定向的 fetchStart 的值。其他情況則返回 0) |
redirectEnd | 重定向結(jié)束時(shí)間(如果發(fā)生了 HTTP 重定向,每次重定向都和當(dāng)前文檔同域的話,就返回最后一次重定向接受完數(shù)據(jù)的時(shí)間。其他情況則返回 0) |
fetchStart | fetchStart 瀏覽器發(fā)起資源請(qǐng)求時(shí),如果有緩存,則返回讀取緩存的開始時(shí)間 |
domainLookupStart | DNS 域名開始查詢的時(shí)間,如果本地有緩存或 keep-alive 等,則返回 fetchStart |
domainLookupEnd | DNS 域名結(jié)束查詢的時(shí)間,如果沒有發(fā)起 DNS 請(qǐng)求同上 |
connectStart | TCP 開始建立連接的時(shí)間,如果有本地的緩存或 keep-alive 等,則與 fetchStart 值相等 |
secureConnectionStart | https 連接開始的時(shí)間如果不是安全連接則為 0 |
connectEnd | TCP 完成握手的時(shí)間,如果有本地的緩存或 keep-alive 等,則與 connectStart 值相 |
requestStart | HTTP 請(qǐng)求讀取真實(shí)文檔的開始時(shí)間,包括從本地文件讀取 |
requestEnd | HTTP 請(qǐng)求讀取真實(shí)文檔的結(jié)束時(shí)間,包括從本地文件讀取 |
responseStart | 瀏覽器從服務(wù)器收到(或從本地緩存讀?。┑谝粋€(gè)字節(jié)的時(shí)間 |
responseEnd | 瀏覽器從服務(wù)器收到(或從本地緩存讀取,或從本地資源讀?。┳詈笠粋€(gè)字節(jié)的時(shí)間 |
unloadEventStart | 前一個(gè)?面的 unload 的時(shí)間戳如果沒有則為 0 |
unloadEventEnd | 與 unloadEventStart 相對(duì)應(yīng),返回的是 unload 函數(shù)執(zhí)行完成的時(shí)間戳 |
domLoading | 這是當(dāng)前網(wǎng)? DOM 結(jié)構(gòu)開始解析時(shí)的時(shí)間戳,是整個(gè)過程的起始時(shí)間,瀏覽器即將開始解析第一批收到的 HTML 文檔字節(jié),此時(shí) document.readyState 變成 loading,并將拋出 readyStateChange 事件 |
domInteractive | 返回當(dāng)前網(wǎng)? DOM 結(jié)構(gòu)結(jié)束解析、開始加載內(nèi)嵌資源時(shí)時(shí)間戳,document.readyState 變成 interactive,并將拋出 readyStateChange 事件(注意只是 DOM 樹解析完成,這時(shí)候并沒有開始加載網(wǎng)?內(nèi)的資源) |
domContentLoadedEventStart | 網(wǎng)? domContentLoaded 事件開始執(zhí)行時(shí)間 |
domContentLoadedEventEnd | 網(wǎng)? domContentLoaded 事件執(zhí)行結(jié)束時(shí)間,domReady 的時(shí)間 |
domComplete | DOM 樹解析完成,且資源也準(zhǔn)備就緒的時(shí)間,document.readyState 變成 complete。并將拋出 readystatechange 事件 |
loadEventStart | load 事件發(fā)送給文檔,onLoad 開始執(zhí)行的時(shí)間 |
loadEventEnd | load onLoad 開執(zhí)行結(jié)束的時(shí)間 |
相關(guān)參數(shù)計(jì)算
字段 | 描述 | 計(jì)算方式 | 意義 |
---|---|---|---|
unload | 前一個(gè)?面卸載耗時(shí) | unloadEventEnd - unloadEventStart | |
redirect | 重定向耗時(shí) | redirectEnd - redirectStart | 重定向的時(shí)間 |
appCache | 緩存耗時(shí) | domainLookupStart - fetchStart | 讀取緩存的時(shí)間 |
dns | DNS 解析耗時(shí) | domainLookupEnd - domainLookupStart | 可觀察域名解析服務(wù)是否正常 |
tcp | TCP 連接耗時(shí) | connectEnd - connectStart | 建立連接的耗時(shí) |
ssl | SSL 安全連接耗時(shí) | connectEnd - secureConnectionStart | 反映數(shù)據(jù)安全連接建立耗時(shí) |
ttfb | TimetoFirstByte(TTFB)網(wǎng)絡(luò)請(qǐng)求耗時(shí) | responseStart - requestStart | TTFB 是發(fā)出?面請(qǐng)求到接收到應(yīng)答數(shù)據(jù)第一個(gè)字節(jié)所花費(fèi)的毫秒數(shù) |
response | 響應(yīng)數(shù)據(jù)傳輸耗時(shí) | responseEnd - responseStart | 觀察網(wǎng)絡(luò)是否正常 |
dom | DOM 解析耗時(shí) | domInteractive - responseEnd | 觀察 DOM 結(jié)構(gòu)是否合理,是否有 JS 阻塞?面解析 |
dcl | DOMContentLoaded 事件耗時(shí) | domContentLoadedEventEnd - domContentLoadedEventStart | 當(dāng) HTML 文檔被完全加載和解析完成之后,DOMContentLoaded 事件被觸發(fā),無需等待樣式表、圖像和子框架的完成加載 |
resources | 資源加載耗時(shí) | domComplete - domContentLoadedEventEnd | 可觀察文檔流是否過大 |
首次渲染耗時(shí) | 首次渲染耗時(shí) | responseEnd - fetchStart | 加載文檔到看到第一幀非空?qǐng)D像的時(shí)間,也叫白屏?xí)r間 |
首次可交互時(shí)間 | 首次可交互時(shí)間 | domInteractive-fetchStart | DOM 樹解析完成時(shí)間,此時(shí) document.readyState 為 interactive |
首包時(shí)間耗時(shí) | 首包時(shí)間 | responseStart-domainLookupStart | DNS 解析到響應(yīng)返回給瀏覽器第一個(gè)字節(jié)的時(shí)間 |
?面完全加載時(shí)間 | ?面完全加載時(shí)間 | loadEventStart-fetchStart | |
onLoad | onLoad 事件耗時(shí) | loadEventEnd - loadEventStart |
使用 demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#parent {
width: 800px;
height: 800px;
background-color: antiquewhite;
}
#child {
width: 400px;
height: 400px;
background-color: aquamarine;
}
#son {
width: 200px;
height: 200px;
background-color: rgb(121, 216, 11);
}
.flex-center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: large;
}
</style>
</head>
<body>
<div id="parent" class="flex-center">
parent
<div id="child" class="flex-center">
child
<div id="son" class="flex-center">
son
<a id="link" >www.baidu.com</a>
</div>
</div>
<script>
const parent = document.getElementById('parent');
const child = document.getElementById('child');
const son = document.getElementById('son');
const link = document.getElementById('link');
link.addEventListener('click', (e) => {
e.preventDefault();
})
const observer = new PerformanceObserver((list) => {
const preEntires = list.getEntries()
preEntires.forEach((item) => {
console.log(item)
})
})
observer.observe({
// 想要監(jiān)聽的性能條目
entryTypes: [
'element',
'event',
'first-input',
'largest-contentful-paint',
'layout-shift',
'longtask',
'mark',
'measure',
'navigation',
'paint',
'resource'
]
})
</script>
</body>
</html>
常見的性能條目
- frame:幀信息,包括每秒的幀數(shù)(FPS)和每幀的時(shí)間間隔(Frame Interval)。
- navigation:頁面導(dǎo)航相關(guān)的性能數(shù)據(jù),包括重定向、DNS 查詢、TCP 握手、頁面加載的時(shí)間等。
- resource:資源加載相關(guān)的性能數(shù)據(jù),如圖片、樣式表、腳本等資源的加載時(shí)間和大小。
- paint:繪制相關(guān)的性能數(shù)據(jù),如首次繪制的時(shí)間點(diǎn)和繪制區(qū)域的尺寸等。
- mark:由 performance.mark() 創(chuàng)建的時(shí)間戳標(biāo)記,用于標(biāo)記代碼執(zhí)行的特定點(diǎn)。
- measure:由 performance.measure() 創(chuàng)建的自定義性能度量,用于測(cè)量特定代碼塊的執(zhí)行時(shí)間。
- longtask:表示執(zhí)行時(shí)間超過 50 毫秒的長(zhǎng)任務(wù)的性能數(shù)據(jù)。
- layout-shift:布局偏移相關(guān)的性能數(shù)據(jù),用于衡量頁面上元素位置發(fā)生變化的情況。
- element:與元素相關(guān)的性能數(shù)據(jù),例如測(cè)量元素大小和位置的信息。
代碼獲取首次繪制,首次內(nèi)容繪制,最大內(nèi)容繪制
new PerformanceObserver((entryList, observer) => {
let entries = entryList.getEntries()
for (let i = 0; i < entries.length; i++) {
if (entries[i].name === 'first-paint') {
console.log('FP', entries[i].startTime)
}
if (entries[i].name === 'first-contentful-paint') {
console.log('FCP', entries[i].startTime)
}
}
observer.disconnect()
}).observe({ entryTypes: ['paint', 'largest-contentful-paint'] })
new PerformanceObserver((entryList, observer) => {
let entries = entryList.getEntries()
const lastEntry = entries[entries.length - 1]
console.log('LCP', lastEntry.startTime)
observer.disconnect()
}).observe({ entryTypes: ['largest-contentful-paint'] })
代碼獲取一些常用指標(biāo)
setTimeout(() => {
const {
fetchStart,
connectStart,
connectEnd,
requestStart,
responseStart,
responseEnd,
domLoading,
domInteractive,
domContentLoadedEventStart,
domContentLoadedEventEnd,
loadEventStart,
} = performance.timing
const info = `
connectTime(TCP鏈接耗時(shí)): ${connectEnd - connectStart}
ttfbTime(獲取到第一個(gè)字節(jié)耗時(shí)): ${responseStart - requestStart}
responseTime(Response響應(yīng)耗時(shí)): ${responseEnd - responseStart}
parseDOMTime(DOM 解析渲染耗時(shí)): ${loadEventStart - domLoading}
domContentLoaded(事件耗時(shí)):${domContentLoadedEventEnd - domContentLoadedEventStart}
TimeToInteractive(首次可交互時(shí)間):${domInteractive - fetchStart}
loadTime(完整的加載時(shí)間): ${loadEventStart - fetchStart}
`
console.log(info)
}, 2000)
// 寫在 setTimeout 里面頁面完成顯示完成再打印,延遲執(zhí)行時(shí)間根據(jù)實(shí)際情況設(shè)定
三、從什么維度來剖析性能?
維度 1:I/O(network)維度
在 App cache/HTTP Cache 階段
強(qiáng)緩存(Cache-Control 和 Expires)和協(xié)商緩存(ETag 和 Last-Modified)是兩種常見的緩存機(jī)制,用于在客戶端和服務(wù)器之間優(yōu)化資源的請(qǐng)求和響應(yīng)。
強(qiáng)緩存是指客戶端直接使用本地緩存的資源,而無需向服務(wù)器發(fā)送請(qǐng)求。主要通過以下兩種方式實(shí)現(xiàn):
-
Cache-Control:在服務(wù)器的響應(yīng)頭部中,設(shè)置 Cache-Control 指令來控制資源的緩存機(jī)制。常用的指令包括:
- public:表示資源可以被任何緩存(包括客戶端和代理服務(wù)器)緩存。
- private:表示資源只能被客戶端緩存,不可被代理服務(wù)器緩存。
- max-age:指定資源的有效時(shí)間,單位為秒??蛻舳嗽谠摃r(shí)間內(nèi)可以直接使用本地緩存而無需發(fā)送請(qǐng)求。
-
Expires:在服務(wù)器的響應(yīng)頭部中,設(shè)置 Expires 字段來指定資源的過期時(shí)間。它是一個(gè)具體的時(shí)間點(diǎn),表示在該時(shí)間點(diǎn)之后,客戶端不能再使用本地緩存,而是需要向服務(wù)器發(fā)送請(qǐng)求。
協(xié)商緩存是指客戶端先向服務(wù)器發(fā)送請(qǐng)求,服務(wù)器通過對(duì)請(qǐng)求的驗(yàn)證來判斷是否返回資源的實(shí)際內(nèi)容。主要通過以下兩種方式實(shí)現(xiàn):
- ETag:在服務(wù)器的響應(yīng)頭部中,設(shè)置 ETag 字段來標(biāo)識(shí)資源的版本號(hào)??蛻舳丝梢栽诤罄m(xù)請(qǐng)求中使用 If-None-Match 頭部字段將上次響應(yīng)中的 ETag 值發(fā)送給服務(wù)器,服務(wù)器通過比較資源的 ETag 值判斷資源是否發(fā)生了變化,如果未發(fā)生變化,則返回 304 狀態(tài)碼,告知客戶端可以使用緩存的資源。
- Last-Modified:在服務(wù)器的響應(yīng)頭部中,設(shè)置 Last-Modified 字段來表示資源的最后修改時(shí)間??蛻舳丝梢栽诤罄m(xù)請(qǐng)求中使用 If-Modified-Since 頭部字段將上次響應(yīng)中的 Last-Modified 時(shí)間發(fā)送給服務(wù)器,服務(wù)器通過比較資源的最后修改時(shí)間判斷資源是否發(fā)生了變化,如果未發(fā)生變化,則返回 304 狀態(tài)碼,告知客戶端可以使用緩存的資源。
需要注意的是,強(qiáng)緩存是由客戶端自身決定是否使用緩存,而協(xié)商緩存是由服務(wù)器來決定是否返回實(shí)際內(nèi)容。通常情況下,當(dāng)資源處于強(qiáng)緩存的有效期內(nèi)時(shí),客戶端直接使用本地緩存的資源,不會(huì)發(fā)送請(qǐng)求到服務(wù)器;而當(dāng)資源過期或存在其他的緩存驗(yàn)證字段時(shí),客戶端會(huì)發(fā)送請(qǐng)求到服務(wù)器并進(jìn)行緩存的驗(yàn)證。兩種緩存機(jī)制可以結(jié)合使用,實(shí)現(xiàn)更靈活和高效的緩存策略。
有緩存
無緩存
強(qiáng)緩存
沒過期
已過期
協(xié)商緩存
可用
不可用
HTTP請(qǐng)求
是否有緩存
強(qiáng)緩存或協(xié)商緩存?
直接向后端獲取數(shù)據(jù)
頁面呈現(xiàn)
緩存是否過期?
不發(fā)送請(qǐng)求,讀取瀏覽器緩存
重新向后端獲取數(shù)據(jù)
向后端發(fā)送ETag或Last-Modified
后端判斷緩存是否可用
返回304
讀取瀏覽器緩存
返回200
后端返回新的數(shù)據(jù)
緩存中有哪些細(xì)節(jié)需要注意?
-
304 狀態(tài)碼是好還是不好?
- 如果多:意味著,我的協(xié)商緩存就比較多;如果少:意味著,我的 JS 文件可能存在頻繁更新的情況。
-
直接在瀏覽器端輸入的?
http://xxx.xxx.com/index.html
,該文件是不會(huì)被緩存的。 -
webpack 中的 hash 指紋,需要合理的利用,去讓“該緩存的內(nèi)容被緩存”
- 不經(jīng)常更新的文件改用 contentHash,例如:公共的樣式文件,工具類 js 文件。
-
協(xié)商緩存中的 Modified 是根據(jù)時(shí)間判斷,精確到秒,一秒時(shí)間內(nèi)可能引發(fā)很多問題。
- 如果同時(shí)有多個(gè)資源在同一秒內(nèi)發(fā)生了修改,服務(wù)器可能無法準(zhǔn)確識(shí)別出每個(gè)資源的變化,導(dǎo)致緩存失效。
- 如果服務(wù)器的時(shí)間與瀏覽器的時(shí)間存在偏差,可能導(dǎo)致資源的修改時(shí)間在服務(wù)器和瀏覽器之間不一致,進(jìn)而造成緩存驗(yàn)證不準(zhǔn)確。
為了避免以上問題,通常建議在修改資源的同時(shí),將"Last-Modified"時(shí)間向上取整到秒級(jí),以增加時(shí)間的準(zhǔn)確性。此外,還可以使用更精確的緩存驗(yàn)證機(jī)制,如 ETag,來解決精度不足的問題,以確保資源的緩存驗(yàn)證更為準(zhǔn)確和可靠。
-
在 CDN 下,hash 緩存是否能夠有比較好的緩存效果?
在 CDN 下,哈希緩存具有非常好的緩存效果。
哈希緩存是指在文件名中添加一個(gè)唯一的哈希值,當(dāng)文件內(nèi)容發(fā)生改變時(shí),哈希值也會(huì)改變。CDN 會(huì)根據(jù)文件名來緩存和分發(fā)資源,因此當(dāng)哈希值改變時(shí),CDN 會(huì)將最新的文件緩存并提供給用戶。
哈希緩存在 CDN 下可以實(shí)現(xiàn)以下優(yōu)點(diǎn):
- 強(qiáng)制緩存更新:哈希緩存可以強(qiáng)制瀏覽器或 CDN 節(jié)點(diǎn)在文件內(nèi)容發(fā)生變化時(shí)獲取最新的資源。由于哈希值的改變,文件名也會(huì)改變,這會(huì)觸發(fā)瀏覽器或 CDN 節(jié)點(diǎn)重新請(qǐng)求該資源,確保用戶獲得最新的版本。
- 緩解緩存一致性問題:在 CDN 集群中,緩存一致性是一個(gè)重要的問題。使用哈希緩存可以避免不同 CDN 節(jié)點(diǎn)之間的緩存不一致性問題。當(dāng)文件內(nèi)容發(fā)生變化時(shí),哈希值改變,文件名也改變,這樣 CDN 將不再提供舊版本的資源,而是提供最新的版本。
- 消除瀏覽器緩存問題:瀏覽器緩存也是需要考慮的因素。通過哈希緩存,瀏覽器會(huì)將每個(gè)版本的資源看作是一個(gè)新的文件,并緩存該版本。這避免了瀏覽器在引用更新資源時(shí)使用舊版本的緩存。
總結(jié)起來,哈希緩存在 CDN 下具有非常好的緩存效果。通過強(qiáng)制緩存更新、解決緩存一致性問題和消除瀏覽器緩存問題,哈希緩存可以確保用戶獲取到最新的資源版本,提高緩存命中率并加快內(nèi)容傳輸速度,從而提升用戶的訪問體驗(yàn)。
-
沒有了強(qiáng)緩存的必要字段值,瀏覽器還會(huì)走強(qiáng)緩存嗎?
- 答案是肯定的。(heuristic expiration time/試探性過期時(shí)間/啟發(fā)式緩存/)
- 強(qiáng)緩存有效期由 Expires 和 Cache-Control 中的 max-age 來決定的,那么如果響應(yīng)頭中不存在這兩個(gè)字段,緩存的有效期怎么計(jì)算呢?瀏覽器還會(huì)走強(qiáng)緩存嗎?答案是肯定的,這就是我們要現(xiàn)在要了解的?啟發(fā)式緩存?。
- 當(dāng)報(bào)頭中沒有用來確定強(qiáng)緩存時(shí)間的字段時(shí),瀏覽器會(huì)觸發(fā)啟發(fā)式緩存,緩存有效期計(jì)算公式:
(date - last-modified) * 10%
,取響應(yīng)報(bào)頭中 date 與 last-modified 值之差的百分之十作為緩存時(shí)間。啟發(fā)式緩存比較容易忽略,不了解啟發(fā)式緩存可能會(huì)因?yàn)檫@種默認(rèn)的緩存方式而掉入坑里,但一旦你了解了瀏覽器啟發(fā)式緩存的機(jī)制,很多問題都可以得到解決。
TCP 階段
HTTP(Hypertext Transfer Protocol)是用于在 Web 上進(jìn)行通信的協(xié)議。它的不同版本有不同的特性和改進(jìn)。以下是 HTTP 各個(gè)版本的主要區(qū)別:
-
HTTP/1.0:
- 請(qǐng)求-響應(yīng)模型:每個(gè)請(qǐng)求只能獲得一個(gè)響應(yīng)。
- 無狀態(tài):每個(gè)請(qǐng)求都是相互獨(dú)立的,服務(wù)器不會(huì)保留之前的請(qǐng)求信息。
- 每個(gè)請(qǐng)求建立新的連接:每個(gè)請(qǐng)求都需要在客戶端和服務(wù)器之間建立一個(gè)新的 TCP 連接。
- 無持久連接:每個(gè)請(qǐng)求的響應(yīng)結(jié)束后,連接會(huì)被關(guān)閉。
- 每個(gè)資源一個(gè)請(qǐng)求:每個(gè)頁面元素(如圖片、樣式表、腳本等)都需要單獨(dú)的 HTTP 請(qǐng)求。
-
HTTP/1.1:
- 持久連接:多個(gè)請(qǐng)求可以在同一個(gè)連接上進(jìn)行,提高效率。
- 管道化(Pipeline):允許在一個(gè)連接上同時(shí)發(fā)送多個(gè)請(qǐng)求,減少延遲。
- Host 頭部字段:允許在同一臺(tái)服務(wù)器上提供多個(gè)域名的不同網(wǎng)站。
- 增加了緩存機(jī)制:引入了更多的緩存控制頭部字段,可以更好地利用緩存。
- 引入了分塊傳輸編碼(Chunked Transfer Encoding):允許服務(wù)器逐塊發(fā)送響應(yīng),有利于大文件的傳輸。
-
HTTP/2:
- 多路復(fù)用(Multiplexing):多個(gè)請(qǐng)求可以在同一個(gè)連接上同時(shí)進(jìn)行,提高性能。
- 二進(jìn)制傳輸:HTTP/2 使用二進(jìn)制格式傳輸數(shù)據(jù),替代了 HTTP/1.x 的文本格式,提高了效率和解析速度。
- 頭部壓縮:使用 HPACK 算法對(duì)報(bào)文頭部進(jìn)行壓縮,減少了數(shù)據(jù)傳輸量。
- 服務(wù)器推送(Server Push):服務(wù)器可以主動(dòng)推送資源給客戶端,減少了客戶端的請(qǐng)求次數(shù)。
-
HTTP/3:
- 基于 QUIC 協(xié)議:HTTP/3 基于 QUIC(Quick UDP Internet Connections)協(xié)議,使用 UDP 替代 TCP,提供更低的延遲和更好的性能。
- 支持多路復(fù)用和頭部壓縮:HTTP/3 也繼承了 HTTP/2 的多路復(fù)用和頭部壓縮特性。
這些是 HTTP 協(xié)議各個(gè)版本的主要區(qū)別。隨著協(xié)議的發(fā)展,每個(gè)版本都試圖改進(jìn)性能、安全性和效率,以提供更好的 Web 體驗(yàn)。選擇使用哪個(gè)版本取決于服務(wù)器和客戶端的支持情況以及具體的需求。
Keep-Alive 是一種持久連接機(jī)制,旨在改善 HTTP 協(xié)議的性能表現(xiàn)。在傳統(tǒng)的 HTTP/1.0 中,每個(gè)客戶端請(qǐng)求都需要與服務(wù)器建立一個(gè)新的 TCP 連接,這樣會(huì)導(dǎo)致每個(gè)請(qǐng)求都要經(jīng)歷 TCP 連接的建立和釋放的過程,增加了延遲和資源消耗。而使用 Keep-Alive,可以重復(fù)使用已經(jīng)建立的 TCP 連接,減少了連接的建立和關(guān)閉過程。
具體而言,Keep-Alive 通過以下方式實(shí)現(xiàn):
- 持久連接:在 HTTP 頭部中添加?
Connection: keep-alive
,表示客戶端希望與服務(wù)器端保持持久連接。當(dāng)服務(wù)器端收到這個(gè)頭部后,會(huì)在響應(yīng)中回復(fù)相同的頭部,表示同意持久連接。 - 復(fù)用連接:客戶端發(fā)送請(qǐng)求后,在服務(wù)器端響應(yīng)結(jié)束后,TCP 連接并不會(huì)立即關(guān)閉,而是保持打開狀態(tài),以便進(jìn)行下一個(gè)請(qǐng)求。
- 設(shè)置超時(shí)時(shí)間:連接在一段時(shí)間內(nèi)沒有新的請(qǐng)求時(shí)會(huì)自動(dòng)關(guān)閉。
使用 Keep-Alive 可以帶來以下優(yōu)點(diǎn):
- 減少延遲:避免了 TCP 連接的建立和關(guān)閉過程,因此減少了連接的延遲。
- 減少資源消耗:連接的復(fù)用減少了服務(wù)器端的負(fù)擔(dān),并且降低了網(wǎng)絡(luò)帶寬的占用。
- 提升性能:在一個(gè)連接上可以發(fā)送多個(gè)請(qǐng)求,實(shí)現(xiàn)并行請(qǐng)求,減少了網(wǎng)絡(luò)擁塞和串行請(qǐng)求的影響。
需要注意的是,Keep-Alive 并不是默認(rèn)開啟的,需要在請(qǐng)求頭部明確指定。在 HTTP/1.1 中,Keep-Alive 是默認(rèn)開啟的,除非明確指定?Connection: close
。而在 HTTP/2 和 HTTP/3 中,持久連接是默認(rèn)開啟的,不再需要單獨(dú)指定。
使用 Keep-Alive 可以有效改善 HTTP 的性能,提高網(wǎng)絡(luò)請(qǐng)求的效率。然而,具體的實(shí)現(xiàn)和支持程度可能因服務(wù)器和客戶端的配置和版本而有所不同。
request 和 response 階段
-
首先式靜態(tài)資源包的體積,如何縮到極致?
- 例如 webpack 腳手架中,用 uglify,minify 插件對(duì)文件進(jìn)行壓縮。它們可以去除代碼中的空格、注釋、無效的代碼,并使用各種技術(shù)(如變量重命名、函數(shù)替換等)來對(duì)文件進(jìn)行壓縮。
- runtime:保證運(yùn)行時(shí),墊片(polyfill)按需加載。
- Tree shaking:搖樹優(yōu)化是指通過靜態(tài)代碼分析的方式剔除掉未被使用的代碼,以減少最終打包生成的代碼的大小。
- 圖片格式:
- 根據(jù)場(chǎng)景考慮能不能使用體積最小的 webp 格式
- 用 base64 圖片體積會(huì)變大 1/3 倍,不用 base64 多一個(gè) http 請(qǐng)求,一般情況大于 64kb 不建議用 base64
-
首屏加載的內(nèi)容,如何進(jìn)行分解?
- code splitting:以 vue 項(xiàng)目為例,首頁是單獨(dú)的 bundle
const routes = [ { path: '/', name: 'home', component: () => import(/* webpackChunkName: "home" */ '../views/HomeView.vue'), }, ]
-
如何在 TCP 請(qǐng)求數(shù)量之間權(quán)衡?
- Chrome 同源下最多 6 個(gè)并發(fā)
- 在 TCP 請(qǐng)求數(shù)量之間存在一個(gè)權(quán)衡(tradeoff)。增加 TCP 請(qǐng)求的數(shù)量可以提高并發(fā)性和響應(yīng)速度,但也會(huì)增加網(wǎng)絡(luò)開銷和資源消耗。反之,減少 TCP 請(qǐng)求的數(shù)量可以節(jié)省網(wǎng)絡(luò)資源和降低開銷,但可能會(huì)犧牲一定的并發(fā)性和響應(yīng)速度。
下面是一些可以幫助你在 TCP 請(qǐng)求數(shù)量方面做出權(quán)衡的建議:
- 批量請(qǐng)求:對(duì)于需要發(fā)送多個(gè)相似請(qǐng)求的情況,可以將其批量處理為一個(gè)更少的請(qǐng)求。例如,可以使用合適的數(shù)據(jù)格式(如 JSON)將多個(gè)數(shù)據(jù)項(xiàng)一次性發(fā)送給服務(wù)器,從而減少請(qǐng)求的數(shù)量。
- 長(zhǎng)連接和連接池:通過使用長(zhǎng)連接和連接池,可以避免頻繁建立和斷開 TCP 連接的開銷。長(zhǎng)連接可以在多個(gè)請(qǐng)求之間保持連接狀態(tài),而連接池可以重用現(xiàn)有連接,從而減少連接建立和拆除的開銷。
- 并發(fā)限制和調(diào)整:可以根據(jù)網(wǎng)絡(luò)和服務(wù)器的容量限制并發(fā)請(qǐng)求數(shù)量。過多的并發(fā)請(qǐng)求可能會(huì)導(dǎo)致網(wǎng)絡(luò)擁塞和服務(wù)器負(fù)載過重,從而影響性能。因此,需要找到一個(gè)合適的并發(fā)請(qǐng)求數(shù)量,既能滿足需求,又不會(huì)過度壓力網(wǎng)絡(luò)和服務(wù)器。
- 緩存和本地存儲(chǔ):利用緩存和本地存儲(chǔ)減少對(duì)服務(wù)器的請(qǐng)求。緩存可以在客戶端保留數(shù)據(jù)的副本,以便在需要時(shí)快速訪問。本地存儲(chǔ)(如瀏覽器的 localStorage)可以將數(shù)據(jù)存儲(chǔ)在客戶端,避免不必要的請(qǐng)求,提高響應(yīng)速度。
- 延遲加載和懶加載:對(duì)于大型應(yīng)用或頁面,可以延遲加載或懶加載一些資源,以減少初始請(qǐng)求的數(shù)量。只加載當(dāng)前所需的資源,而不是一次性加載所有資源,可以提高初始加載速度,并根據(jù)需要?jiǎng)討B(tài)加載其他資源。
以上是一些常見的權(quán)衡措施,可以根據(jù)具體的應(yīng)用場(chǎng)景和需求進(jìn)行調(diào)整。需要綜合考慮網(wǎng)絡(luò)環(huán)境、服務(wù)器性能、用戶體驗(yàn)和應(yīng)用需求,并根據(jù)實(shí)際情況進(jìn)行優(yōu)化和調(diào)整。最佳實(shí)踐是進(jìn)行基準(zhǔn)測(cè)試和性能測(cè)試,以評(píng)估不同參數(shù)和策略對(duì)應(yīng)用性能的影響,并找到最合適的配置。
Processing 階段/ DOM 加載階段
- 一般情況要將樣式文件放在 head 內(nèi),因?yàn)闃邮轿募坏┘虞d好就會(huì)立即渲染已經(jīng)構(gòu)建好的節(jié)點(diǎn)元素,如果樣式文件放在 body 標(biāo)簽中間引入很容易造成回流/重排。
- 將腳本放在?
<head>
?中可能會(huì)導(dǎo)致頁面加載阻塞,因?yàn)槟_本的下載和執(zhí)行會(huì)阻塞頁面的渲染。如果腳本較大或執(zhí)行時(shí)間較長(zhǎng),頁面加載速度可能會(huì)受到影響。 - 放在?
<body>
?元素底部:為了避免阻塞頁面加載,可以將?script
?標(biāo)簽放在?<body>
?元素的底部,即在頁面內(nèi)容后面。這樣,在頁面內(nèi)容加載完成后再加載和執(zhí)行腳本,不會(huì)阻塞頁面的初始渲染。 - 除了上述兩種常見的放置位置外,還可以使用異步加載和延遲加載來優(yōu)化腳本加載行為。這些技術(shù)可以通過?
async
?和?defer
?屬性來實(shí)現(xiàn),允許腳本的異步或延遲加載,以優(yōu)化頁面加載性能。
默認(rèn)模式:
download-execute
------parse------ ------parse------
defer 模式(推遲執(zhí)行模式):
download execute
------parse--------------------parse------
async 模式(異步下載模式):
download-execute
------parse--------------- ------parse------
維度 2:渲染維度
如何有效避免頻繁操作 DOM?
- createDocumentFragment
- 添加列表的時(shí)候不要每列單獨(dú) appendChild,而是將所有列先存起來最后在進(jìn)行 appendChild
回流與重繪
回流(reflow)和重繪(repaint)是瀏覽器在渲染網(wǎng)頁時(shí)執(zhí)行的兩個(gè)關(guān)鍵過程。
回流/重排(reflow),也稱為布局(layout),是瀏覽器根據(jù) DOM 樹中的元素和 CSS 樣式計(jì)算每個(gè)元素在頁面中的幾何位置和大小的過程。這是一個(gè)相對(duì)昂貴的操作,需要瀏覽器重新計(jì)算元素的布局,并重新繪制受影響的部分。
回流會(huì)在以下情況下被觸發(fā):
- 添加、刪除、修改 DOM 元素
- 修改元素的位置、尺寸(包括寬度、高度、邊距、填充)
- 修改元素的內(nèi)容(文字、圖片)
- 瀏覽器窗口的尺寸變化
- 觸發(fā) CSS 動(dòng)畫和過渡
- 計(jì)算某些屬性的值
- offsetTop、offsetLeft、offsetWidth、offsetHeight?:讀取這些屬性會(huì)觸發(fā)瀏覽器計(jì)算元素的布局,可能引發(fā)回流。
- clientWidth、clientHeight?:讀取這些屬性會(huì)獲取元素可見區(qū)域的寬度和高度,不會(huì)引起回流。
- scrollWidth、scrollHeight?:讀取這些屬性會(huì)獲取元素內(nèi)容區(qū)域的寬度和高度,不會(huì)引起回流。
- getComputedStyle()?:調(diào)用此方法可以獲取元素的計(jì)算樣式,由于需要獲取實(shí)時(shí)計(jì)算后的樣式,可能會(huì)觸發(fā)回流。
- getBoundingClientRect()?:調(diào)用此方法會(huì)獲取元素在視口中的位置和尺寸信息,這將觸發(fā)回流。
- offsetParent?:讀取這個(gè)屬性會(huì)觸發(fā)回流。
- clientTop、clientLeft?:讀取這些屬性會(huì)獲取元素的內(nèi)邊距的大小,不會(huì)觸發(fā)回流。
- offsetParent?:讀取這個(gè)屬性會(huì)觸發(fā)回流。
當(dāng)回流發(fā)生時(shí),瀏覽器會(huì)從渲染樹的根節(jié)點(diǎn)開始,遞歸遍歷整個(gè)渲染樹,確定每個(gè)元素的幾何位置和大小。然后,瀏覽器重新繪制受影響的部分,并更新布局。
重繪(repaint)?是指瀏覽器根據(jù)已計(jì)算的元素樣式重新繪制頁面的過程,而不會(huì)影響元素的幾何位置和大小。重繪是相對(duì)較快的操作,因?yàn)樗恍枰略氐目梢姌邮健?/p>
重繪會(huì)在以下情況下被觸發(fā):
- 修改元素的顏色、背景色、文本顏色等可見樣式
- 切換 CSS 類名
- 使用 CSS 偽類(:hover、:active)等
當(dāng)重繪發(fā)生時(shí),瀏覽器會(huì)重新繪制受影響的部分,但不會(huì)重新計(jì)算元素的布局。
回流和重繪的頻繁發(fā)生會(huì)導(dǎo)致性能問題,因?yàn)樗鼈儠?huì)占用大量的計(jì)算資源。優(yōu)化網(wǎng)頁性能的關(guān)鍵是盡量減少回流和重繪的次數(shù)。
為了減少回流和重繪的次數(shù),可以采取以下優(yōu)化策略:
- 避免多次讀取上述屬性,在使用前先緩存起來。
- 將樣式修改集中在一次操作中,可以使用 CSS 類名的切換來批量修改樣式。
- 使用 CSS3 的 transform 屬性來進(jìn)行位移、縮放和旋轉(zhuǎn),它不會(huì)引起回流。
- 使用?
requestAnimationFrame
?方法來進(jìn)行動(dòng)畫的更新,它能夠優(yōu)化性能并避免不必要的回流和重繪。 - 使用文檔片段(
DocumentFragment
)進(jìn)行 DOM 操作,以減少對(duì)實(shí)際文檔的修改。
CSS 的 transform 屬性不會(huì)造成回流的主要原因是,它對(duì)元素進(jìn)行了一種視覺上的變換,而不影響元素在布局上的位置和大小。具體原因如下:
- 硬件加速:當(dāng)應(yīng)用 transform 屬性時(shí),瀏覽器會(huì)將該元素視為一個(gè)單獨(dú)的圖層,并通過硬件加速來處理該圖層的變換操作。因?yàn)橛布铀偈窃趫D層級(jí)別進(jìn)行的,不影響其他元素的布局,所以不會(huì)觸發(fā)回流。
- 獨(dú)立圖層:在某些情況下,瀏覽器會(huì)自動(dòng)將某些元素創(chuàng)建為獨(dú)立的圖層,例如使用 3D 變換、透明度動(dòng)畫、嵌套的 CSS 動(dòng)畫等。這些獨(dú)立的圖層也能夠享受硬件加速的好處,不會(huì)引起回流。
- 位置不變:transform 屬性的變換并不改變?cè)卦谖臋n流中的位置,元素的原始位置仍然被保留。因此,當(dāng)元素應(yīng)用 transform 變換時(shí),并不需要改變布局來適應(yīng)變換后的狀態(tài)。
需要注意的是,雖然 transform 不會(huì)引起回流,但仍然會(huì)觸發(fā)重繪(repaint)。因?yàn)樵氐目梢姌邮桨l(fā)生了變化,瀏覽器需要重新繪制元素來反映變化。但相比于回流,重繪的開銷要小得多。
因此,對(duì)于需要對(duì)元素進(jìn)行平移、旋轉(zhuǎn)、縮放等視覺上的變換操作,使用 CSS 的 transform 屬性是一種推薦的做法,可以獲得更好的性能和流暢的動(dòng)畫效果,同時(shí)避免回流的影響。
?
前端面試題庫 (面試必備)?? ? ? ? ? ?推薦:★★★★★
地址:前端面試題庫
到了這里,關(guān)于【面試題】前端面試復(fù)習(xí)6---性能優(yōu)化的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!