歡迎閱讀本系列其他文章
【前端除錯】- 更好的除錯方式 VSCode Debugger
【前端除錯】- 斷點除錯的正確開啟方式
介紹
首先簡單過一下Performance的使用,開啟網頁點選控制檯Performance,錄製5s的資料
其中 Main 這部分就是網頁的主執行緒,也就是執行 Event Loop 的部分:
灰色就代表宏任務 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);
};
});
可以看到計算被轉移到worker,雖然還是long task,但是不阻塞主執行緒,計算量也在那裡沒辦法最佳化
這樣,我們透過把計算量拆分到 worker 執行緒,充分利用了多核 cpu 的能力,解決了主執行緒的 long task 問題,介面互動會很流暢。