博客 / 詳情

返回

寫給自己的前端性能優化

一直想寫篇前端性能相關的總結,個人覺得這塊的內容會比較分散,面試的時候問起來,也不容易有一個清晰的框架,但是平時的習慣是想起來什麼就寫什麼,所以攢了好久的內容只能躺在一堆筆記草稿裏面;

---🚩🚩正文分割線🚩🚩---

按頁面加載鏈路分類,從下面幾個方面開始

  • 首屏加載
  • 代碼優化
  • 構建工具

首屏加載

這部分其實就是把從獲取資源到頁面呈現中可以優化的點提取出來

1. DNS預解析

不需要用户點擊鏈接就在後台解析,在head中添加

<link rel="dns-prefetch" href="//example.com">

但是要注意會增加一定的網絡請求和帶寬消耗,非必要域名謹慎使用

2. 開啓HTTP2

首先説下相對於HTTP1的優勢

  • 多路複用,能夠在單個TCP連接上同時傳輸多個請求和響應;HTTP1.1有一個可選的Pipelining技術,但它是按照順序處理響應的,後發的請求可能被先發的請求阻塞,所以很多瀏覽器默認不開啓。
  • 首部壓縮,使用HPACK算法對請求和響應頭部進行壓縮,減少了首部大小,節省了帶寬。而在HTTP/1.x中,每次請求都需要發送完整的頭部信息,很容易造成不必要的帶寬浪費。
  • 服務器推送,服務端可以在發送頁面HTML,也就是客户端請求對應HTML頁面時主動推送其它資源,而不用等到瀏覽器解析到相應位置,發起請求再響應。
  • 二進制分幀,使用二進制協議對數據進行分幀傳輸。二進制協議更高效,減少了解析數據的開銷,並提高了傳輸速度。

    在nginx中開啓HTTP2

# 修改nginx.conf中的配置
server {
    listen 443 ssl http2;
    server_name example.com;

    # SSL證書和密鑰路徑
    ssl_certificate /path/to/ssl/cert;
    ssl_certificate_key /path/to/ssl/key;

    # 其他配置項
}

3. 資源的預加載

這個其實是安排資源以更高的優先級進行下載和緩存,更詳細的可以看看MDN文檔

<link rel="preload" href="styles.css" as="style">
<link rel="preload" href="main.js" as="script" />

4. 動態創建加載腳本

不管是js還是css,在下載過程中其實都是會阻塞頁面的,動態加載會在下載資源的同時,也不影響後續代碼的執行

const script = document.createElement("script");
script.src = "myscript.js";
document.body.appendChild(script);

5. 同構渲染

其實就是服務端渲染(SSR)+ 客户端渲染(CSR),服務端渲染的升級版,像現在的Nuxt.js或者Next.js就可以實現;

引用《vue.js設計與實現》的對比來更直觀的瞭解同構渲染的特點

CSR SSR 同構
SEO 不友好 友好 友好
白屏問題
佔用服務器資源
用户體驗

6. 可見性優化

這部分主要是針對非可視區域進行延遲加載來減少首屏執行的邏輯

非可視區域

  • 延遲接口請求,使用setTimeout或者then函數來置後加載時機;
  • 圖片懶加載,使用IntersectionObserver實現可視區域判斷;

虛擬滾動

只加載上下及當前頁的數據;可以通過滾動時分頁或者vue-virtual-scroll-listreact-virtualized一類的插件實現;

7. 針對白屏/抖動

加載過程中無法避免的會有短暫白屏,

  • 骨架屏,可以選擇固定灰色塊,或者計算頁面元素寬高生成灰色快;
  • loading,加入比較有意思的loading動畫;
  • 定義寬高,圖片或者接口數據在渲染到頁面之後,會撐開所在的元素,就造成頁面抖動,設置好盒子或者圖片的寬高或者設置個佔位;
  • 字體閃爍,加載字體且生效之前的閃爍,通過壓縮字體減小資源體積,設置font-display:block來解決加載過程中的字體樣式異常;

8. 靜態資源

  • 合理使用協商緩存和強緩存及本地存儲
  • 使用字體圖標代替圖片圖標
  • 使用webp
  • 圖片壓縮
  • 使用cdn

代碼優化

這部分只是列出來可優化點,感興趣可以去搜索相關實現,建議只有出現明確的性能問題存在時,才進行優化

1. JS

  • 使用scriptasyncdefer屬性避免阻塞;
  • service worker,攔截網絡請求,靈活的判斷是否需要緩存資源;
  • Web Worker,創建一個新的線程,在一個獨立的js環境中執行邏輯,不會阻塞後續邏輯的執行,針對耗時的計算任務或者執行時間比較久的邏輯處理;
  • 批量請求及事件任務切片;
  • 節流和防抖;
  • 事件委託;
  • 及時銷燬閉包及定時器;
  • 緩存變量及dom屬性;
  • 變量作用域的合理聲明;

2. CSS

  • 迴流屬性放在一塊集中修改;
  • 避免選擇器嵌套過深;
  • CSS動畫代替JS動畫;
  • 使用偽元素簡化html結構,如:before代替div
  • 開啓GPU加速,這個非必要不推薦開啓;
  • 減少CSS類名查找範圍,瀏覽器解析CSS遵循的是從右到左的查找規範,先找.b再找.a,將.wrap .a .b改為wrap .b

3. Vue

  • v-showv-if的合理使用,頻繁更新顯示狀態使用v-show
  • 使用keep-alivev-once減少多餘的更新渲染;
  • 通過Object.freeze移除雙向綁定,減少不必要的數據監聽;
  • 避免template中使用複雜的表達式;

4. React

  • memo,減少子組件的重複渲染,簡單組件不會有太大的效果,並且會加大內存消耗;
  • useMemo,相當於Vue中的computed函數,所設置的依賴沒有變化時,就會返回上一次的計算結果;
  • useCallback,避免重複創建函數;
  • 組件卸載清理,Class組件:componentWillUnmount,Function 組件:useEffect return
  • 使用React Fragment,減少額外節點的渲染;

構建工具

目前基本上使用vite,這裏主要針對vite優化,webpack就簡單帶過

1. webpack

  • 指定模塊解析範圍,設置解析文件類型範圍
  • webpack打包/構建緩存hard-source-webpack-plugin
  • 資源的壓縮,拆分、第三方包的提取合併(config配置optimization.splitChunks);

2. 搖樹優化

就是在保證代碼運行結果不變的前提下,去除無用的代碼;其實Rollup會默認開啓搖樹優化,但需要是ES6 module模塊,第三方包儘管可能使用esm版本,本身體積會更小,而且能有更好的壓縮效果

import { cloneDeep } from 'lodash'
// 改為
import { cloneDeep } from 'lodash-es'

const obj = cloneDeep({}) // 如果這行被註釋,vite就不會再引入lodash包

刪除線上的consoledebugger,這個根據項目需求決定是否需要配置

  {
    esbuild: {
      drop: ['console', 'debugger'],
    }
  }

3. gzip壓縮

這個就是在客户端進行文件壓縮,服務端直接調用

import viteCompression from 'vite-plugin-compression';

viteCompression({
    verbose: true,
    disable: false, // 不禁⽤壓縮
    deleteOriginFile: false, // 壓縮後是否刪除原⽂件
    threshold: 10240, // 壓縮前最⼩⽂件⼤⼩
    algorithm: 'gzip', // 壓縮算法
    ext: '.gz', // ⽂件類型
}),

nginx配置靜態gzip壓縮,會直接讀取文件夾中.gz文件

# 修改nginx.conf中的配置
http {
 gzip_static on;
}

Content-Encodinggzip就表示設置成功了

4. 圖片壓縮

import viteImagemin from 'vite-plugin-imagemin'

viteImagemin({
    gifsicle: { // gif圖片壓縮
      optimizationLevel: 3, // 選擇1到3之間的優化級別
      interlaced: false, // 隔行掃描gif進行漸進式渲染
      // colors: 2 // 將每個輸出GIF中不同顏色的數量減少到num或更少。數字必須介於2和256之間。
    },
    optipng: { // png
      optimizationLevel: 7, // 選擇0到7之間的優化級別
    },
    mozjpeg: {// jpeg
      quality: 20, // 壓縮質量,範圍從0(最差)到100(最佳)。
    },
    pngquant: {// png
      quality: [0.8, 0.9], // Min和max是介於0(最差)到1(最佳)之間的數字,類似於JPEG。達到或超過最高質量所需的最少量的顏色。如果轉換導致質量低於最低質量,圖像將不會被保存。
      speed: 4, // 壓縮速度,1(強力)到11(最快)
    },
    svgo: { // svg壓縮
      plugins: [
        {
          name: 'removeViewBox',
        },
        {
          name: 'removeEmptyAttrs',
          active: false,
        },
      ],
    },
  })

5. 依賴分析

基本上都是用這種方式來查找不必要的依賴引用來減小包體積

import { visualizer } from 'rollup-plugin-visualizer';

const command = process.env.npm_lifecycle_event

 {
  plugins: [
    command === 'report' ? 
    visualizer({ open: true, brotliSize: true, filename: 'report.html' })
    : null
  ]
 }

最後

從整體架構方面還可以做下面幾件事;

  • 組件增加權重,針對單個組件,給組件添加權重值,針對權重大的組件優先展示;
  • 按機型加載資源,根據當前系統版本、機型配置做不同的資源加載,動畫交互降級;
  • 微前端 拆分應用,剝離業務,減少業務之間的關聯影響,使用micro-app或者qiankun

最後優化是有成本的,也需要根據場景決定是否進行優化

傳送門

萬字長文:分享前端性能優化知識體系

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.