最近參與了一個(gè)微信小程序項(xiàng)目,里面需要做一個(gè)圖表,故事就這么開始了。。。
圖表組件支持的少
搜了一圈組件,發(fā)現(xiàn)圖表類組件在小程序生態(tài)里面好殘缺。。。像 echarts 這類豐富的圖表組件庫還沒有適配小程序,能夠想得到的原因大概有:
小程序運(yùn)行機(jī)制有限制,比如:window 對象不能直接訪問等
事件監(jiān)聽機(jī)制不一樣,小程序里要使用 bindxxx 函數(shù)來實(shí)現(xiàn),和原生的事件監(jiān)聽接口有比較大的差別
最終,我們是在WX-charts這個(gè)組件上進(jìn)行了一些二次開發(fā)才完成的圖表組件。
js 執(zhí)行效率低
除了社區(qū)支持不足的問題以外,還遇到了js 執(zhí)行效率低的問題。
需要說明的是,我們要在圖表上實(shí)現(xiàn)拖拽圖表滾動(dòng)的效果,wx-charts 并不支持,團(tuán)隊(duì)里的小伙伴自己擼了一個(gè)??墒峭瑯拥拇a,在 iPhone7 上運(yùn)行流暢程度勉強(qiáng)可以接受,但是在我的 iPhone6 上神卡。小程序開發(fā)工具在真機(jī) profiling 方面的支持基本沒有,只能用 log 大法來定位問題。
要說拖拽功能的開發(fā)套路:主要就是監(jiān)聽 touch 事件,在 touchstart 事件里,記錄手指的起始位置,在 touchmove 事件里計(jì)算手指位置的偏移,根據(jù)偏移重新畫一幀。跟蹤性能的方法就是使用日志記錄下來每一幀的繪制時(shí)間。
實(shí)測發(fā)現(xiàn), iPhone6 上平均每幀要 100~120 ms,也就是說只能達(dá)到 8~10 fps。。??刹痪褪强?。。。
大概想了一下,性能問題應(yīng)該出在以下幾處:
canvas 接口本身性能問題
繪制圖表時(shí) canvas 中繪制的內(nèi)容太多
js 本身的執(zhí)行效率
期初都沒懷疑 js 的效率問題。先是懷疑問題出在第二點(diǎn)上,因?yàn)槲覀円龅氖且粋€(gè)左右滑動(dòng)的圖表,在顯示區(qū)域以外的數(shù)據(jù)點(diǎn)還是很多的,所以先是優(yōu)化了 canvas 接口的調(diào)用次數(shù),不在顯示區(qū)域里面的數(shù)據(jù)點(diǎn)全部都過濾掉。優(yōu)化之后看結(jié)果,性能確實(shí)有提升,但是也就是能提升到 12~15 fps 的水平,性能提升不明顯。
趕巧那段時(shí)間,有人在討論 Node.js v8.0 上 forEach 的性能提升。再看 wx-charts 里面用了大量的 forEach/map 方法。抱著試一試的態(tài)度,把里面最關(guān)鍵的部分全都改寫成普通的 for 循環(huán)。結(jié)果驚人地發(fā)現(xiàn),每幀的繪制時(shí)間降到了 30~40 ms。
結(jié)論,實(shí)際測試發(fā)現(xiàn):在 iPhone6上,forEach/map 方法的性能問題還是比較明顯的。平時(shí)做一些普通的功能開發(fā)看不出來,但是如果是在 canvas 圖表這類對性能要求比較高的場景下,問題就很明顯了。
touch 事件大量積壓
除了 js 執(zhí)行效率問題外,我發(fā)現(xiàn)小程序的 touch 事件有明顯的延遲現(xiàn)象。準(zhǔn)確的說是:事件觸發(fā)的時(shí)間間隔低,如果事件回調(diào)函數(shù)執(zhí)行時(shí)間長的話,touch 事件不會被丟棄而是會大量積壓。像上面說的情況,每次 touchmove 時(shí)間回調(diào)要執(zhí)行 100+ ms 可是 touchmove 事件目測不超過 20ms 就會觸發(fā)一次。所以如果快速滑動(dòng)屏幕,圖表就跟上了發(fā)條一樣,會延遲繪制很多次。
上面雖然優(yōu)化了性能,但是還不能避免事件積壓的問題。最終是加了一個(gè)控制邏輯,將 touch 事件的頻率降低到每秒 40 次,代碼大概如下:

其他問題
除了上面提到的 canvas 圖表問題,做一次小程序之后還發(fā)現(xiàn)了一些問題,希望大家開發(fā)的時(shí)候提前準(zhǔn)備好應(yīng)對措施:
很多組件要手動(dòng)引入到小程序代碼當(dāng)中。
由于微信小程序的限制,一些開源組件需要手動(dòng)修改里面的一些代碼才能在小程序當(dāng)中使用
兼容性問題,在 PC、Android、iOS 上表現(xiàn)不一致。比如想讓在 canvas 上使用 transform: rotate(90deg) ,PC 上沒有問題,但是 iOS 上不起作用。