前端面試題(六)前端性能優化篇

網絡相關

DNS 預解析

DNS 解析也是須要時間的,能夠經過預解析的方式來預先得到域名所對應的 IP。javascript

<link rel="dns-prefetch" href="//yuchengkai.cn">
複製代碼

緩存

緩存對於前端性能優化來講是個很重要的點,良好的緩存策略能夠下降資源的重複加載提升網頁的總體加載速度。html

一般瀏覽器緩存策略分爲兩種:強緩存和協商緩存。前端

強緩存

實現強緩存能夠經過兩種響應頭實現:ExpiresCache-Control 。強緩存表示在緩存期間不須要請求,state code 爲 200java

Expires: Wed, 22 Oct 2018 08:41:00 GMT
複製代碼

Expires 是 HTTP / 1.0 的產物,表示資源會在 Wed, 22 Oct 2018 08:41:00 GMT 後過時,須要再次請求。而且 Expires 受限於本地時間,若是修改了本地時間,可能會形成緩存失效。面試

Cache-control: max-age=30
複製代碼

Cache-Control 出現於 HTTP / 1.1,優先級高於 Expires 。該屬性表示資源會在 30 秒後過時,須要再次請求。算法

協商緩存

若是緩存過時了,咱們就可使用協商緩存來解決問題。協商緩存須要請求,若是緩存有效會返回 304。跨域

協商緩存須要客戶端和服務端共同實現,和強緩存同樣,也有兩種實現方式。瀏覽器

Last-Modified 和 If-Modified-Since

Last-Modified 表示本地文件最後修改日期,If-Modified-Since 會將 Last-Modified 的值發送給服務器,詢問服務器在該日期後資源是否有更新,有更新的話就會將新的資源發送回來。緩存

可是若是在本地打開緩存文件,就會形成 Last-Modified 被修改,因此在 HTTP / 1.1 出現了 ETag性能優化

ETag 和 If-None-Match

ETag 相似於文件指紋,If-None-Match 會將當前 ETag 發送給服務器,詢問該資源 ETag 是否變更,有變更的話就將新的資源發送回來。而且 ETag 優先級比 Last-Modified 高。

選擇合適的緩存策略

對於大部分的場景均可以使用強緩存配合協商緩存解決,可是在一些特殊的地方可能須要選擇特殊的緩存策略

  • 對於某些不須要緩存的資源,可使用 Cache-control: no-store ,表示該資源不須要緩存
  • 對於頻繁變更的資源,可使用 Cache-Control: no-cache 並配合 ETag 使用,表示該資源已被緩存,可是每次都會發送請求詢問資源是否更新。
  • 對於代碼文件來講,一般使用 Cache-Control: max-age=31536000 並配合策略緩存使用,而後對文件進行指紋處理,一旦文件名變更就會馬上下載新的文件。

使用 HTTP / 2.0

由於瀏覽器會有併發請求限制,在 HTTP / 1.1 時代,每一個請求都須要創建和斷開,消耗了好幾個 RTT 時間,而且因爲 TCP 慢啓動的緣由,加載體積大的文件會須要更多的時間。

在 HTTP / 2.0 中引入了多路複用,可以讓多個請求使用同一個 TCP 連接,極大的加快了網頁的加載速度。而且還支持 Header 壓縮,進一步的減小了請求的數據大小。

更詳細的內容你能夠查看 該小節

預加載

在開發中,可能會遇到這樣的狀況。有些資源不須要立刻用到,可是但願儘早獲取,這時候就可使用預加載。

預加載實際上是聲明式的 fetch ,強制瀏覽器請求資源,而且不會阻塞 onload 事件,可使用如下代碼開啓預加載

<link rel="preload" href="http://example.com">
複製代碼

預加載能夠必定程度上下降首屏的加載時間,由於能夠將一些不影響首屏但重要的文件延後加載,惟一缺點就是兼容性很差。

預渲染

能夠經過預渲染將下載的文件預先在後臺渲染,可使用如下代碼開啓預渲染

<link rel="prerender" href="http://example.com"> 
複製代碼

預渲染雖然能夠提升頁面的加載速度,可是要確保該頁面百分百會被用戶在以後打開,不然就白白浪費資源去渲染

優化渲染過程

對於代碼層面的優化,你能夠查閱瀏覽器系列中的 相關內容

懶執行

懶執行就是將某些邏輯延遲到使用時再計算。該技術能夠用於首屏優化,對於某些耗時邏輯並不須要在首屏就使用的,就可使用懶執行。懶執行須要喚醒,通常能夠經過定時器或者事件的調用來喚醒。

懶加載

懶加載就是將不關鍵的資源延後加載。

懶加載的原理就是隻加載自定義區域(一般是可視區域,但也能夠是即將進入可視區域)內須要加載的東西。對於圖片來講,先設置圖片標籤的 src 屬性爲一張佔位圖,將真實的圖片資源放入一個自定義屬性中,當進入自定義區域時,就將自定義屬性替換爲 src 屬性,這樣圖片就會去下載資源,實現了圖片懶加載。

懶加載不只能夠用於圖片,也可使用在別的資源上。好比進入可視區域纔開始播放視頻等等。

文件優化

圖片優化

計算圖片大小

對於一張 100 * 100 像素的圖片來講,圖像上有 10000 個像素點,若是每一個像素的值是 RGBA 存儲的話,那麼也就是說每一個像素有 4 個通道,每一個通道 1 個字節(8 位 = 1個字節),因此該圖片大小大概爲 39KB(10000 * 1 * 4 / 1024)。

可是在實際項目中,一張圖片可能並不須要使用那麼多顏色去顯示,咱們能夠經過減小每一個像素的調色板來相應縮小圖片的大小。

瞭解瞭如何計算圖片大小的知識,那麼對於如何優化圖片,想必你們已經有 2 個思路了:

  • 減小像素點
  • 減小每一個像素點可以顯示的顏色

圖片加載優化

  1. 不用圖片。不少時候會使用到不少修飾類圖片,其實這類修飾圖片徹底能夠用 CSS 去代替。
  2. 對於移動端來講,屏幕寬度就那麼點,徹底沒有必要去加載原圖浪費帶寬。通常圖片都用 CDN 加載,能夠計算出適配屏幕的寬度,而後去請求相應裁剪好的圖片。
  3. 小圖使用 base64 格式
  4. 將多個圖標文件整合到一張圖片中(雪碧圖)
  5. 選擇正確的圖片格式:
    • 對於可以顯示 WebP 格式的瀏覽器儘可能使用 WebP 格式。由於 WebP 格式具備更好的圖像數據壓縮算法,能帶來更小的圖片體積,並且擁有肉眼識別無差別的圖像質量,缺點就是兼容性並很差
    • 小圖使用 PNG,其實對於大部分圖標這類圖片,徹底可使用 SVG 代替
    • 照片使用 JPEG

其餘文件優化

  • CSS 文件放在 head
  • 服務端開啓文件壓縮功能
  • script 標籤放在 body 底部,由於 JS 文件執行會阻塞渲染。固然也能夠把 script 標籤放在任意位置而後加上 defer ,表示該文件會並行下載,可是會放到 HTML 解析完成後順序執行。對於沒有任何依賴的 JS 文件能夠加上 async ,表示加載和渲染後續文檔元素的過程將和 JS 文件的加載與執行並行無序進行。
  • 執行 JS 代碼過長會卡住渲染,對於須要不少時間計算的代碼能夠考慮使用 WebworkerWebworker 可讓咱們另開一個線程執行腳本而不影響渲染。

CDN

靜態資源儘可能使用 CDN 加載,因爲瀏覽器對於單個域名有併發請求上限,能夠考慮使用多個 CDN 域名。對於 CDN 加載靜態資源須要注意 CDN 域名要與主站不一樣,不然每次請求都會帶上主站的 Cookie。

其餘

使用 Webpack 優化項目

  • 對於 Webpack4,打包項目使用 production 模式,這樣會自動開啓代碼壓縮
  • 使用 ES6 模塊來開啓 tree shaking,這個技術能夠移除沒有使用的代碼
  • 優化圖片,對於小圖可使用 base64 的方式寫入文件中
  • 按照路由拆分代碼,實現按需加載
  • 給打包出來的文件名添加哈希,實現瀏覽器緩存文件

監控

對於代碼運行錯誤,一般的辦法是使用 window.onerror 攔截報錯。該方法能攔截到大部分的詳細報錯信息,可是也有例外

  • 對於跨域的代碼運行錯誤會顯示 Script error. 對於這種狀況咱們須要給 script 標籤添加 crossorigin 屬性
  • 對於某些瀏覽器可能不會顯示調用棧信息,這種狀況能夠經過 arguments.callee.caller 來作棧遞歸

對於異步代碼來講,可使用 catch 的方式捕獲錯誤。好比 Promise 能夠直接使用 catch 函數,async await 可使用 try catch

可是要注意線上運行的代碼都是壓縮過的,須要在打包時生成 sourceMap 文件便於 debug。

對於捕獲的錯誤須要上傳給服務器,一般能夠經過 img 標籤的 src 發起一個請求。

面試題

如何渲染幾萬條數據並不卡住界面

這道題考察瞭如何在不卡住頁面的狀況下渲染數據,也就是說不能一次性將幾萬條都渲染出來,而應該一次渲染部分 DOM,那麼就能夠經過 requestAnimationFrame 來每 16 ms 刷新一次。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <ul>控件</ul>
  <script> setTimeout(() => { // 插入十萬條數據 const total = 100000 // 一次插入 20 條,若是以爲性能很差就減小 const once = 20 // 渲染數據總共須要幾回 const loopCount = total / once let countOfRender = 0 let ul = document.querySelector("ul"); function add() { // 優化性能,插入不會形成迴流 const fragment = document.createDocumentFragment(); for (let i = 0; i < once; i++) { const li = document.createElement("li"); li.innerText = Math.floor(Math.random() * total); fragment.appendChild(li); } ul.appendChild(fragment); countOfRender += 1; loop(); } function loop() { if (countOfRender < loopCount) { window.requestAnimationFrame(add); } } loop(); }, 0); </script>
</body>
</html>
複製代碼
相關文章
相關標籤/搜索