iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
Software Development

文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念系列 第 23 篇

Day 23:效能瓶頸分析——時間複雜度(Big-O)在前端資料過濾與渲染中的實際體現

  • 分享至 

  • xImage
  •  

昨天談的測試,驗證的是「結果對不對」,但一個函式就算結果完全正確,也可能慢到使用者等不下去。今天要回頭把 Day 6 提過的 Big-O,實際套用在前端最常見的場景:資料過濾(filter)與畫面渲染(render)上,看時間複雜度怎麼從一個抽象概念,變成使用者體感上的卡頓。

一、複習:Big-O 在說什麼

Big-O 描述的是,當資料量 n 增加時,演算法需要的運算次數會如何成長。O(1) 代表運算次數不隨資料量改變;O(n) 代表運算次數跟資料量成正比;O(n²)(例如巢狀迴圈)則代表資料量每增加一倍,運算次數會變成四倍——資料量小的時候感覺不出差異,但資料量一大,O(n²) 的效能會急遽惡化。

二、前端常見的反模式:巢狀迴圈做查找

最常見的效能陷阱,是在一個迴圈裡面,對另一個陣列做 .find() 或 .filter()——這其實是把 O(n) 的查找動作,重複做了 n 次,整體變成 O(n × m):

// 反模式:對每一筆訂單,都重新在商品陣列裡線性搜尋一次 —— O(n × m)
function attachProductNames(orders, products) {
  return orders.map(order => ({
    ...order,
    productName: products.find(p => p.id === order.productId)?.name // 每次都是 O(m) 線性搜尋
  }));
}

三、代購 App 案例

延續 Day 6 提過的 { byId, allIds } 正規化狀態。如果把商品資料先轉成一個以 id 為 key 的物件(或 Map),查找就能從「線性搜尋」變成「直接取值」,從 O(m) 降到 O(1):

// 優化:先把商品陣列轉成 byId 的查找表,查找變成 O(1)
function attachProductNames(orders, products) {
  const productById = Object.fromEntries(products.map(p => [p.id, p])); // 建立一次查找表,花費 O(m)
  return orders.map(order => ({
    ...order,
    productName: productById[order.productId]?.name // 直接取值,O(1)
  }));
  // 整體時間複雜度從 O(n × m) 降到 O(n + m)
}

當代購 App 的訂單列表要同時顯示商品名稱、顯示 100 筆訂單、對應 100 項商品時,反模式的寫法要跑 100 × 100 = 10,000 次比對;優化後只需要先花 100 次建立查找表,再花 100 次直接取值,總共 200 次——資料量越大,這個差距會越明顯,這也是為什麼畫面渲染筆數一多,就容易感覺到卡頓的常見原因之一。

四、結論

Big-O 不是一個只存在於面試題裡的抽象概念——它實際對應到「資料筆數變多時,使用者會不會感覺到卡頓」這個很具體的體驗問題。前端最容易踩到的陷阱,就是在渲染或過濾資料時,不小心把簡單的查找動作,寫成了巢狀迴圈;而 Day 6 提過的正規化資料結構,正是解決這類問題最直接的工具。


上一篇
Day 22:單元測試基礎——測試金字塔理論、斷言(Assertions)與 Mock 機制
系列文
文科生的軟體工程啟蒙:用一個代購 App,看懂 30 個系統設計觀念 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言