在 Hacker News 首頁上看到「300ms Faster: Reducing Wikipedia’s Total Blocking Time」這篇,作者 Nicholas Ray 是 Wikimedia Foundation 的工程師,雖然是貼在自己的 blog 上,但算是半官方性質了… 文章裡面提到了兩個改善都是跟前端 JavaScript 有關的。
作者是透過瀏覽器端的 profiling 產生火焰圖,判讀裡面哪塊是大塊的問題,然後看看有沒有機會改善。
先看最後的成果,可以看到第一個 fix 讓 blocking time 少了 200ms 左右,第二個 fix 則是少了 80ms 左右:
第一個改善是從火焰圖發現 l._enable
吃掉很多 blocking time:
作者發現是因為 find()
找出所有的連結後 (a
元素),跑去每一個連結上面綁定事件造成的效能問題:
The .on(“click”) call attached a click event listener to nearly every link in the content so that the corresponding section would open if the clicked link contained a hash fragment. For short articles with few links, the performance impact was negligible. But long articles like ”United States” included over 4,000 links, leading to over 200ms of execution time on low-end devices.
但這其實是 redundant code,在其他地方已經有處理了,所以解法就比較簡單,拔掉後直接少了 200ms:
Worse yet, this behavior was unnecessary. The downstream code that listened to the hashchange event already called the same method that the click event listener called. Unless the window’s location already pointed at the link’s destination, clicking a link called the checkHash method twice — once for the link click event handler and once more for the hashchange handler.
第二個改善是 initMediaViewer
吃掉的 blocking time,從 code 也可以看到問題也類似,跑一個 loop 把事件掛到所有符合條件的元素上面:
這邊的解法是 event delegation,把事件掛到上層的元素,就只需要掛一個,然後多加上檢查事件觸發的起點是不是符合條件就可以了,這樣可以大幅降低「掛」事件的成本。
這點算是常用技巧,像是 table
裡面有事件要掛到很多個 td
的時候,會改成把一個事件掛到 table
上面,另外加上判斷條件。
算是蠻標準的 profiling 過程,直接拉出真實數據來看,然後調有重大影響的部分。