【前端除錯】- 藉助Performance分析並最佳化效能

Leise發表於2023-01-08

歡迎閱讀本系列其他文章
【前端除錯】- 更好的除錯方式 VSCode Debugger
【前端除錯】- 斷點除錯的正確開啟方式

介紹

首先簡單過一下Performance的使用,開啟網頁點選控制檯Performance,錄製5s的資料

image

其中 Main 這部分就是網頁的主執行緒,也就是執行 Event Loop 的部分:

image

灰色就代表宏任務 task(這裡帶了紅色是因為在 Performance 中寬度代表時間,超過 50ms 就被認為是 Long Task,會被標紅。)
橙色:瀏覽器內部的 JS
藍色:html parse
紫色:reflow、repaint
綠色:渲染
寬度代表了執行的時間,超過 50ms 就被任務是長任務,需要最佳化。
長度代表了呼叫棧深度,一般特別長的都是有遞迴在。

效能分析

下面這段程式碼在Performance中會顯示如下

  useEffect(() => {
    function a() {
      b();
    }
    function b() {
      let total = 0;
      for (let i = 0; i < 10 * 10000 * 10000; i++) {
        total += i;
      }
      console.log("b:", total);
    }

    a();
  });

點選可以跳轉帶程式碼位置,顯示對應耗時

這段程式碼在Performance中顯示可以看到b函式的執行導致了耗時太高了,因為渲染和 JS 執行都在主執行緒,在一個 Event Loop 中,會相互阻塞,如果 JS 有長時間執行的 Task,就會阻塞渲染,導致頁面卡頓。所以,效能分析主要的目的是找到 long task,之後消除它。

效能最佳化

我們最佳化的目標是把兩個 long task 中的耗時邏輯(迴圈累加)給去掉或者拆分成多個 task。

但明顯我們這裡的邏輯沒啥好拆分的,它就是一個大迴圈。

那麼能不能不放在主執行緒跑,放到其他執行緒跑呢?瀏覽器的 web worker 好像就是做耗時計算的效能最佳化的。
建立worker.js

// worker
const workercode = () => {
    const compute = (num) => {
        let total = 0;
        for (let i = 0; i < num; i++) {
            total += i;
        }
        return total
    }
    this.onmessage = function (e) {
        const reslut = compute(e.data)
        this.postMessage(reslut);
    }
};

let code = workercode.toString();
code = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}"));

const blob = new Blob([code], { type: "application/javascript" });
const worker_script = URL.createObjectURL(blob);

module.exports = worker_script;

需要使用的頁面引入

import worker_script from "./worker";
  useEffect(() => {
    const myWorker = new Worker(worker_script);
    myWorker.postMessage(10 * 10000 * 10000);
    myWorker.onmessage = (m) => {
      console.log("msg from worker: ", m.data);
    };
  });

image
可以看到計算被轉移到worker,雖然還是long task,但是不阻塞主執行緒,計算量也在那裡沒辦法最佳化

這樣,我們透過把計算量拆分到 worker 執行緒,充分利用了多核 cpu 的能力,解決了主執行緒的 long task 問題,介面互動會很流暢。

相關文章