前兩天遇到一個前端性能相關的bug,感受還挺典型的,整理了一下解決過程和思路,寫下來分享給你們。前端
場景是這樣的,有一個答題的界面,能夠播放音頻、填空、提交答案,界面是長這個樣子的:vue
看起來還挺簡單吧,可是咱們在手機上跑的時候,卻遇到了如下問題:web
1. 填完空後,提交按鈕會由灰色變爲藍色(可提交狀態),可是播放完音頻後,卻沒法變藍chrome
2. 頁面較長時,一邊播音頻一邊滾動頁面,會出現頁面閃爍(短時白屏)瀏覽器
個人第一反應就是:出渲染bug了。由於在一些低端手機上,常常會遇到動態修改頁面,渲染沒有及時生效,出現花屏或者白屏的狀況。框架
而修改這類bug並無什麼好方法,惟一管用的就是強制瀏覽器再重繪一次。經常使用的技術手段好比設置style.visibility="visible",或者是更新一下className。有時候這種「輕度重繪」起不到做用的話,還會修改背景色啦,或者先display:none而後在display:block,目的都是強制觸發瀏覽器的reflow或者repaint,指望它能給渲染正常。前端性能
所以我不假思索使出老手段。可是在嘗試各類強制重繪後,並無解決問題1。這我也是能想通的,畢竟是不穩定的hack手段,不生效也是情有可原。工具
有人會問,是否是邏輯寫的有問題啊?經我排查其實並無邏輯問題,咱們是用vue的:class綁定作的樣式更新,此時該狀態的變量已經更新了,並且class的屬性值也更新了,只是視覺上沒看到更新而已,仍是渲染的問題沒錯。佈局
此時我有一個膽大的想法:不會是vue的bug吧!猜想以下:vue內部更新class值的時候是否是有什麼機制,致使瀏覽器在某些特殊狀況下忽略了此次渲染。其實我並不肯意這樣想,畢竟vue已是一個很穩定的框架了,不至於有這麼低級的問題吧,就算真有,應該早就暴漏出來了呀。性能
可是事實就擺在我眼前,而我還必須解決這個bug,因此還得想辦法。
「要不,這裏就不用vue綁定了。」
不用vue綁定,本身手動操做更新className。這實在是下下策,我甚至感到有點羞恥。由於我是有代碼潔癖的,用vue自己就是爲了避免手動操做DOM,而如今,卻要在用vue一鼓作氣的代碼中插入一段手動操做DOM的代碼。簡直是一顆老鼠屎,破壞了整個代碼的完美度。
上線要緊啊,忍了。因而我把提交按鈕這裏,改成了手動更新className。問題解決了,提交按鈕乖乖變藍,播放音頻的動做不會影響到它了。你們誇我快速解決了問題,而個人良心隱隱做痛。
故事並無結束,其實,重點纔剛剛開始。直到遇到問題2,才讓我開始從新審視這個問題。問題2是這樣的:在頁面高度特別長的時候,會有滾動頁面的操做。當正在播放音頻的時候去滾動頁面,大概在播完音頻的那個瞬間(頁面還在滾),頁面會發生一次嚴重的抖動,直接白屏一下,而後頁面從新正常顯示。
這下好了,無法用重繪hack,也無法再用手動操做DOM的噁心方法了。此時,不得不拿出調試利器了:chrome的devtool。在Rendering工具中,勾選Paint Flashing,它可以高亮頁面被重繪的區域。
有發現了!在音頻播放完畢的時候,提交按鈕那塊區域居然發生了一次重繪。這怎麼回事呢?它倆隔着老遠,並且並無父子關係,何況提交按鈕仍是絕對定位放在下面的。我想不到什麼緣由能讓下邊的提交按鈕發生重繪。
重頭來了,這時我打開了Layers工具,看到的景象讓我大吃一驚。看下面的動圖吧:
音頻播放組件那裏的graphics layer居然如此之亂,在開始播放的時候,發生了較多的層提高和層合併。更爲奇葩的時,下方的提交按鈕區域也跟着發生了層提高。若是你細細觀察,還能看到右上角的那片綠葉子也發生了層提高,你這傢伙跟着起鬨什麼啊。。。
這下問題的癥結就比較清楚了,多餘的層變更,致使意外的重繪。恰逢頁面正在滾動,一下遇到了渲染瓶頸,就出現了閃爍。那麼,是什麼緣由致使的層變更呢?
通過審查代碼,查到了問題所在,總結以下:
音頻組件的佈局方式存在問題,左側旋轉的圓盤是右側進度條的子元素,經過絕對定位給定到左側的。而且其高度是大於父元素的,經過父元素的overflow:visible才得以完整顯示。你們知道元素間的遮擋以及裁切均可能會生成新的提高層(graphics layer)。並且左側的圓盤在播放時還會經過transform進行旋轉動畫,transform也會進行層提高,同時瀏覽器還會進行層合併的判斷,將能夠合併的合成一個graphics layer。而這個判斷是全局進行的,也就是說頁面底部的提交按鈕也被計算在內,可能正好命中了某些規則,因此它也被提高爲單獨的層。
因此我把音頻組件進行了從新的佈局(減小遮擋與裁切),不讓它產生那麼多的提高層行爲。
至於右上角那個綠葉子,我發現他的z-index爲100,感受根本不須要這麼大,改成了2,工做良好,而且不會被提高了。你們知道z-index也是層提高的一個影響因素,不少同窗常常隨手就寫一個很大的z-index,生怕本身的元素被別人蓋住。這是一個很很差的習慣,沒準哪天就給命中了層提高的規則,引起重繪了。
通過了上面的修改,再次打開Layers面板,發現此時的層已經很規整了,效果以下:
感受很清爽了有木有。而在此修改以後,問題1的根本緣由也定位到了(因爲層提高而引起了重繪),而且順勢恢復正常。那段讓我良心隱隱做痛的代碼也能夠刪掉啦!