Webnovel 不用照顧 Edge 瀏覽器性能?想多了!

本文做者:任家樂html

原創聲明:本文爲閱文前端團隊 YFE 成員出品,請尊重原創,轉載請聯繫公衆號 ( id: yuewen_YFE ) 獲取受權,並註明做者、出處和連接。前端

前言

曾寫過一篇性能優化 「 長篇報告 」 「 checkbox 美化引起的蝴蝶效應 」 ,也曾感嘆 CSS 對渲染的影響是如此大,也許深化記憶點的代價就是被同一塊石頭絆倒2次 ?是的,性能優化「報告」第二彈來了,但願本篇文章能夠在優化頁面性能上給你們提供一些思路。web

看點

某些 CSS 屬性或 JS 操做,看似簡單卻易隱藏陷阱?chrome

  • visibility - 在不改變文檔佈局的狀況下展現或隱藏某個元素。
  • clientHeight / clientWidth - 獲取元素內部的寬高。

磨刀不誤砍柴工,性能工具的親密接觸老是必不可少的。瀏覽器

  • Edge 瀏覽器性能工具怎麼用
  • 如何快速的定位、解決性能問題?

同是瀏覽器,渲染層面的表現卻如此不一樣?性能優化

  • Edge 瀏覽器的渲染策略和 Chrome 有何不一樣?

性能炸彈

問題始於一個 「 紅色性能炸彈 」 ,瞄準的是 Webnovel「起點海外版」 PC 站點閱讀頁,關鍵字:Edge 瀏覽器、CPU、RAM。Webnovel 的國外用戶指出,在 Edge 瀏覽器下,閱讀頁 CPU 消耗的很是大,他的腿甚至感覺到了筆記本電腦的灼燒。異步

Webnovel 閱讀頁,爲了更趨近於完美的閱讀體驗,咱們作了:預加載上下章、支持上下鍵切換章節、無刷新跳章節等優化,在閱讀過程當中,你甚至不會看到內容加載過程當中的 loading 。然而面對此次的性能突擊,這些優化在 Edge 瀏覽器下顯得如此眇小...而對因而否值得作這次性能優化,Google Analytics 則給出了答案:chrome-devtools

「 Webnovel 瀏覽器佔比一覽」工具

Edge 瀏覽器 TOP4 (2.53%)的佔比狀況已不容忽略!看來註定免不了和 Edge 的一場針鋒相對了,立馬去 Edge 瀏覽器上一探究竟~佈局

問題的剖析

定位問題以前,心裏有個聲音在提醒我:是 Google 廣告腳本的問題!由於前不久閱讀頁引入了 Google 廣告,而且針對廣告的展現及隱藏作過一些邏輯的調整,帶着目的首先從 JS 開始分析。

性能工具中尋找問題代碼 - Google 腳本在不斷獲取 clientHeight、clientWidth?

我是用 Edge 瀏覽器自帶的性能工具來進行分析的,因爲 Edge 只能在 win10 系統上運行,我工做中用的是 iMac 因此幾乎沒有使用過 ,也只能回家 「踢走」 老公倒騰他專門打遊戲的臺式機了,其 Edge 瀏覽器版本:42.17134.1.0。

打開 Edge 開發者工具,性能工具的入口映入眼簾。

「 Edge 開發者工具 」

切入正題:「 訪問 Webnovel 閱讀頁 - 打開「 性能 」 標籤欄 - 按下運行按鈕 」。爲了還原用戶的使用場景,我也認真的滾動瀏覽了十幾章。運行的結果:

「 首次性能工具的結果圖 」

這滿屏的原諒色,看到這張圖,答應我請第一眼就確認是 「渲染」 的問題好嗎?請立馬註釋 CSS 一探究竟好嗎?但我並無作到。

繼續觀察,發現性能工具中有2個子標籤欄: 時間線、JavaScript 調用堆棧。調用堆棧裏很清楚的指明瞭 CPU 佔用率最高的2項(下圖),果真出自於 Google 廣告的 osd.js 腳本。

「 首次性能工具結果圖之 JavaScript 調用堆棧 」

不能慌,證據還不夠確鑿。Edge 的性能工具其實能夠精肯定位到 JS 中的問題代碼片斷,在紅框區域點擊鼠標右鍵,也能夠定位到問題源碼,這一點 very good!感受離事實的真相更近了一步。

「 追溯代碼問題細節圖 」

「 問題代碼片斷1 」

「 問題代碼片斷2 」

顯而易見,Google osd.js 腳本設置了計時器,不停地獲取 HTML 節點的 clientWidth 和 clientHeight,因此禍根源於此?

clientHeight、clientWidth 的一些隱患

Facebook 的工程師 Stoyan 在他的博客中曾提過, 瀏覽器是聰明的,爲了節約資源、不頻繁觸發 Repaint(重繪)、Reflow(重排),它會將一系列腳本中須要執行的 UI 改動放在一個隊列中一塊兒執行,而不是每一個改動都單獨執行從而引起無數次的 Repaint、Reflow。BUT!有一些操做會打亂它原有的 plan 和秩序:

  1. offsetWidth,offsetHeight,offsetTop,offsetLeft
  2. scrollTop, scrollLeft,scrollWidth, scrollHeight
  3. clientlTop, clientLeft,clientWidth, clientHeight
  4. getComputedStyle(),or currentStyle in IE

瀏覽器對於這些操做,會「 很是及時地 」給予最準確實時的答案,所以會觸發 Reflow,例如從新計算樣式、更新圖層等。要知道 Reflow 相比 Repaint 對渲染的影響更大、損耗更多。

demo time - 獲取 clientWidth 是否會觸發沒必要要的渲染?

功能簡述:

  1. 頁面 onload 後設置計時器每隔 4ms(瀏覽器默認最小時間間隔) 獲取 HTML 節點的clientWidth。
  2. 點擊 click me 按鈕水平位移 box1 到最右側。
  3. 點擊 no timer 按鈕清除計時器,同時水平位移 box1 到原位。

「 demo 動圖 」

過程

結果

階段一:before 「 click me 」

未觸發 Reflow 等渲染事件

階段二:after 「 click me 」

觸發了額外的渲染(recalculate style、update layers)

階段三:after 「 no timer 」

沒有觸發 Reflow 等渲染事件

Chrome Performance 工具則更直觀地描述了表格中的結果(紅框中處於階段二,清楚表現了額外的渲染):

爲了更突出差別,我將水平移動的 div 數量增長到了 20 個,Recalculate Style 的數據相差了 70ms:

「 渲染數據的差別:動畫期間獲取 clientWidth(上),動畫期間不作任何操做(下) 」

demo 的結果驗證了在 Chrome 瀏覽器中,動畫期間獲取 clientWidth / clientHeight 確實會引起瀏覽器的 Reflow,隨着動畫複雜度的增長以及元素的增長,該影響只會更大,進而增長了沒必要要的渲染消耗。

首次嘗試 - 刪除 Google 廣告

既然閱讀頁 CPU 飆升極有多是由於 Google 的 osd.js 不斷獲取 HTML 節點的 clientWidth / clientHeight 意外觸發了屢次 Reflow ,那麼解決問題的第一步便是刪除 Google 廣告。我利用 fiddler 代理 JS 腳本到本地,刪除了廣告相關的代碼。

但其結果盡和個人猜想徹底不一樣,刪除廣告並無使頁面性能變得更好,有點沒有頭緒...真的不是 Google 廣告的問題?

再次試探 - Google 廣告在其餘站點有無相似問題

繼續用 Edge 性能工具對一相似網站進行測試,由於這個網站一樣使用了 Google 廣告,而且相比於 Webnovel 閱讀頁首次初始化1枚廣告,它的首頁盡然同時加載了 4 枚廣告。然而結果卻沒有渲染上的問題(以下圖),網站 CPU 利用率一欄甚至徹底沒有飈綠的痕跡。

推翻設想,轉換方向到 CSS

太過堅信設想反而偏離正軌?前期我太堅信是 Google 廣告腳本的問題,既然以上 2 次的嘗試都證實不是 Google 廣告腳本的鍋,那麼是時候轉換方向了。

曾經寫的「 checkbox 美化引起的蝴蝶效應 」帶給了我一些靈感:CSS 在渲染性能上有着相比 JS 更不容忽視的影響力。結合前面提到的 demo 結論,不禁得猜想也許閱讀頁的性能問題是因爲某些 CSS 屬性或動畫與 Google 廣告腳本中的 clientHeight、clientWidth 產生了相互做用,引起了額外的渲染。此時嘗試註釋全部樣式,最終綠色居然消失了,這說明極有多是 CSS 的鍋。

「 CPU 佔用率中消失的渲染 」

罪魁禍首是 visibility : hidden ?

繼續從性能工具中尋找更精確的答案,意外發現 Edge 性能工具裏的 「 時間線 」 標籤欄裏能夠捕捉到不少有用的信息,甚至能夠找到樣式相關的條目?展開便可定位到目前引起渲染的 DOM,這一點太讚了!

這對問題的定位有了很大的幫助,由於我很清楚地看到了有問題的 DOM ,也就是咱們寫的 loading 組件。

Webnovel 閱讀頁確實存在幾枚隱藏的 loading,分別存在於章評彈窗中、目錄彈窗中、各章節內容之間。

「 章評彈窗、目錄彈窗 」

「 章節之間的 loading 」

隱藏 loading ,是爲了簡化邏輯,將其「 埋伏隱藏 」 在各自須要的地方,在獲取所需的異步數據以前,能夠第一時間將 loading 展現給用戶,獲取到須要的數據後再將 loading 隱藏,這樣的交互邏輯是合理的,那麼隱藏的 loading 爲何會對渲染產生影響?Webnovel 閱讀頁彈窗中的 loading 是用 visibility: hidden 來隱藏的,這樣的方式不對?

visibility: hidden 有利有弊

看不見 loading 的蹤影,它卻依然能對渲染產生這麼大的影響?你認爲正確地隱藏了其中的 loading,而 loading 卻實際地存在於文檔流中,它的每個動畫,都會影響同在文檔流中的其餘節點。

  • visibility: hidden // DOM 的渲染不會忽略其全部節點
  • display: none // DOM 的渲染忽略其全部節點

如此來看,性能更優的應該是 display: none,不在文檔流中就不會影響其餘的元素。但每一個被創造出來的 CSS 屬性必然有創造它的道理,若是 display: none 是最推薦的方式,爲何還會有 visibility : hidden 的存在?任何事物都存在兩面性,例如如下場景中,visibility: hidden 竟協助某大神解決了性能問題。

Google 的工程師 Jake Archibald 在他的博客「Solving rendering performance puzzles」中,用一個有趣的 SVG 動畫驗證了 Layout (佈局)在網頁性能中舉足輕重的地位。隨着動畫中 SVG 文字的不斷變化,動畫的幀率由原來最優的 60fps 最終下降到不足於 10fps。Chrome 的 Performance 工具揭示了其主要因素是產生了大量的 Layout 消耗(稱爲 Layout Thrashing or Reflow )。

此時 Jake 利用 visibility: hidden 解決了 Layout 消耗過多的問題,他提到,visibility: hidden 的元素,由於其在文檔流中佔據一席之地,於是將其設置爲 visibility: visible 並不會產生 Layout 上的消耗,只會產生額外的 paint,藉助此想法,他事先將 SVG 中的全部字符分別放置於 <textpath> 節點中的子節點 <tspan> 中,並利用 visibility: hidden 進行隱藏:

「 DOM 中隱藏的 SVG 文字 」

隨後逐次將 <tspan> 的 visibility 屬性值變動爲 visible ,字符就依次展現了出來,對比下,他原來的作法就真的是很是粗暴了,即:將全部字符存於單獨的一個節點中,每次截取固定長度的文字設置爲 <textPath> 的值。大量的重置 textpath 節點值最終引起了瀏覽器每一個字符的從新佈局( layout )。

所以並不能籠統地下定論 visibility: hidden 不如、或優於 display: none ,只能說在不一樣的場景下他們都有各自的優點。

問題的修復

顯然,loading 動畫用 visibility: hidden 進行隱藏確實產生了不少額外的渲染消耗。修復這個問題的方案不少,例如,能夠將 loading 的隱藏方式改成 display: none,或者是在打開彈窗的時候將 loading 實時添加到 DOM 中,最終爲避免改動彈窗組件的 CSS 產生的全局影響,咱們在打開彈窗的時候添加了 loading 的 DOM 節點,關閉彈窗時則將其移除。一週後用戶的反饋也證實了此方案是有效的(下圖)。

「 用戶的反饋 」

新方案的嘗試

Edge 瀏覽器渲染上和 Chrome 有什麼不一樣 ?

Webnovel 閱讀頁的問題雖然得以解決,但可能咱們都會感到疑惑,爲何這樣的性能問題在 Chrome 瀏覽器上並無發生,或者說表現不明顯?這裏我藉助「 visibility: hidden 有利有弊」一節中提到的 Jake 的 SVG 動畫作了一波測試。性能工具簡單探測一下,發現未優化前的動畫其性能和 Chrome 同樣沒法直視:

「 Edge 瀏覽器性能結果 」

優化後的動畫其表現和 Chrome 也相差甚遠,直觀上的對好比動圖所示,Edge 瀏覽器(第二張圖)上的動畫比 Chrome(第一張圖) 緩慢許多。

「 上 Chrome, 下 Edge 」

Chrome Performance 工具中能夠證實 Jake 的優化策略確實將 Layout 產生的消耗下降了不少,由原來的 TOP3 變爲最後一位 :

「 優化前,優化後 」

但 Edge 性能工具的結果甚至比不上優化以前,渲染上未下降,而且出現了不少 JS 上的 CPU 衝擊(圖中橘色區域):

「 優化前 」

「 優化後 」

優化後渲染層面的開銷甚至大於優化前的開銷(在動畫運行差很少時長的狀況下):

「 優化前、後 UI線程數據 」

在此 demo 的表現上,Edge 瀏覽器對 SVG 動畫的渲染狀況與 Chrome 截然相反,更重要的是,Chrome 對於 Layout(佈局)和 Paint (重繪) 在渲染性能方面的 「 權衡 」彷佛和 Edge 是不同的,下降大量的 Layout 開銷確實可使 Chrome 中的動畫更爲流暢,但這在 Edge 中卻行不通,甚至表現相反。

解決問題的新思路

Chrome 和 Edge 瀏覽器在 SVG 動畫上的表現差別提供了我一些新的思路,再次回到 Webnovel 閱讀頁,咱們的頁面中確實使用了數量很多的 SVG,我嘗試着將 SVG 的 display: inline-block 改成 display: none 後,CPU 居然迅速降了下來,性能上也獲得了緩解(以下圖),感到很是驚喜。

「 註釋 SVG 樣式以前 」

「 註釋 SVG 樣式以後 」

但目前性能問題已然解決,而且 Webnovel 全站都使用的了 SVG ,此時若全局替換掉 SVG 未免有些粗暴,加之 Jake 的 demo 還不足以說明過多的 SVG 影響了頁面的性能,所以該方案並無實施,但這確實能夠做爲 Webnovel 閱讀頁性能問題的另外一個突破點,後續還須要花更多的時間來探索這其中的奧祕。

一些碎碎念

固然,問題的修復老是須要總結一下:

  • visibility: hidden 是把雙刃劍。
  • clientWidth、clientHeight 等操做在動畫過程當中不建議使用。
  • 若是性能工具展現了大面積飆綠現象,請第一時間註釋你的 CSS 再一探究竟
  • 沒法定位問題的時候能夠對比其餘相似的網站獲得啓發,從而進一步定位
  • Edge 瀏覽器渲染層面和 Chrome 略有不一樣,這是值得重視的。

性能優化本就是積極嘗試和勇於試錯的過程,正所謂積跬步以致千里,點滴的積累和探索不是爲了產出 「 最佳性能優化方案 n 條 」 ,而是爲了能更好地提煉定位問題的思路、擴展認知範圍、同時造成一種對待性能瓶頸不害怕的態度,但願咱們均可以作到微笑面對性能瓶頸。


參考連接


查看更多分享,請關注閱文集團前端團隊公衆號:

相關文章
相關標籤/搜索