小程序以免安裝用完即走的特性自發(fā)布初就很火,即使是現(xiàn)在也是熱度不減。小程序雖然是一個 HTML5,但是通過限制開發(fā)者的寫法,提供一套自定義的組件以及寫法,并且將一部分耗費性能的組件使用客戶端渲染來極大的提高網(wǎng)頁的性能。
前段時間我們也進行了小程序的開發(fā)。由于是接手的項目,這個項目之前是沒有使用框架,直接使用原生小程序開發(fā)的,開發(fā)過程中就發(fā)現(xiàn)有很多不方便的地方。例如 NPM, Promise 組件化等。最后我對 wepy , tinajs , labrador , mpvue 等多個框架進行了調(diào)研,最后決定使用 tinajs 對項目進行重構。選擇 tinajs 的原因是它是基于小程序的語法,并沒有多大的改造,學習成本非常低。同時它的漸進式概念讓我能夠循序漸進的接入框架,而不是一下子就要把它的所有概念都要學習下來,這對當時工期比較緊張的我來說是比較重要的。當然還有一個更重要的原因是,它的源碼非常的簡單,即使是出了問題我也能快速定位出來。
不過即使是選擇了一套正確的框架,碰到坑的情況也是在所難免的,小程序更甚。下面我就我這段時間碰到的一些比較經(jīng)典的問題來說一下我碰到的一些問題。
我們是一個新聞流的項目,用戶可以無限下拉加載數(shù)據(jù),內(nèi)部會使用一個數(shù)組將列表的數(shù)據(jù)存儲起來。當我美滋滋的使用 tinajs 重構完項目后準備試試的時候,我就發(fā)現(xiàn),當我加載數(shù)據(jù)超過一定數(shù)量限制(大概200條數(shù)據(jù))之后,控制臺就會報“輸出傳輸長度超過最大長度”的錯誤。
最后查了一下發(fā)現(xiàn)是小程序為了性能限制了一次渲染傳輸?shù)臄?shù)據(jù)量大小。最開始我以為是我的數(shù)據(jù)超過限制了,所以采取了精簡不必要字段的辦法將數(shù)據(jù)體積減小。不過在我縮減到不能再縮減的時候,發(fā)現(xiàn)依舊沒什么卵用。后來就想到我的數(shù)據(jù)真的有這么大么,于是乎拿之前原生的測試了一下,發(fā)現(xiàn)刷到七百多條數(shù)據(jù)的時候也會提示這個錯誤。那么就可以證明我之前兩百條數(shù)據(jù)的時候沒有超過限制,同時我將數(shù)據(jù) JSON 化并保存到本地文件里查看了一下,發(fā)現(xiàn)才150KB左右,遠遠沒有達到上限。
最后經(jīng)過我和 tinajs 作者的逐一分析,發(fā)現(xiàn)可能是自定義組件的鍋。因為我的列表元素有不同的樣式,所以我使用了自定義組件去定義了不同的樣式類型組件,部分組件又有公共的部分所以又要抽離出來變成組件,也就是說實際上我的列表是由一個多層嵌套的自定義組件循環(huán)渲染而成的。我們猜測最后小程序渲染的時候,每一個自定義組件傳入的數(shù)據(jù)都會做一次拷貝,這樣就導致了我本來 150K 的數(shù)據(jù),瞬間就超過了它們的限制。
最后解決的辦法也非常簡單,由于我其實大多數(shù)都是純渲染的組件,所以組件內(nèi)部的自定義組件我都是用 <template> 模板去渲染,這種情況下不會觸發(fā)數(shù)據(jù)的拷貝試了下就沒有問題了。當然除了我說的減少數(shù)據(jù)體積以及是用自定義模板代替自定義組件減少數(shù)據(jù)拷貝層級之外,我們還可以對 數(shù)據(jù)進行分頁操作 來達到減少一次數(shù)據(jù)渲染的體積。
我們的小程序有一個下拉刷新的功能,小程序自己官方是有封裝 onPullDownRefresh 接口來幫助我們完成這個事。不過因為我們的下拉刷新是有自定義樣式的,所以就沒辦法使用官方的接口了。最后做出來的效果大概如下所示:
最開始我是使用了 <scroll-view> 組件來做滾動,同時使用 scrolltoupper 來觸發(fā)下拉的事件。內(nèi)部則是使用了 translate 操作來展開下拉卡片。一頓操作之后覺得甚是完美,但是之后突然發(fā)現(xiàn)官方提示:
請勿在 scroll-view 中使用 textarea 、 map 、 canvas 、 video 組件
因為這幾個組件都是使用 Native 實現(xiàn)的,只能是固定在屏幕上的存在,所以沒辦法在 scroll-view 中使用。因為之后可能會在里面加入視頻的數(shù)據(jù),所以對這個組件就有點望而卻步。同時使用了這個組件之后,外部的其它組件想要修改 scrllTop 的話會變得很麻煩,都需要自維護一套事件,增加了業(yè)務的復雜度。
最終我退回成普通的 view 監(jiān)聽 touchstart , touchmove 和 touchend 事件,根據(jù)移動的距離來判斷下拉百分比來實現(xiàn)這個功能。最終的實現(xiàn)可以說是異常艱辛的。不過這個實現(xiàn)完了之后,又出現(xiàn)了一個問題。在 iOS 中會存在阻尼效果,也就是下拉的時候滾動條會有一個回彈的特效,導致你雖然下拉了但是 touch 事件并沒辦法有效的執(zhí)行。目前這個問題還沒有比較好的解決辦法,這里也有用戶提出了需要 提供禁止頁面阻尼效果的參數(shù) ,不過目前還沒有官方回應。
除了阻尼問題之外,還有一個問題是 wx.pageScrollTo() 方法提供了 duration 參數(shù),讓滾動能夠有動畫效果。你可以在開發(fā)者工具中看到,實際上小程序的這個動畫是使用 transform 屬性來做的。正常情況來說這個是沒有問題的,但是對于頁面內(nèi)存在 position: fixed 的元素來說,這個是有問題的。為什么這么說呢,大家可以看看 MDN 上 fixed 的描述:
It is positioned relative to the initial containing block established by the viewport, except when one of its ancestors has a transform , perspective , or filter property set to something other than none (see the CSS Transforms Spec), in which case that ancestor behaves as the containing block.
via: https://developer.mozilla.org/en-US/docs/Web/CSS/position
文檔上說 fixed 默認是相對于視口的,除非說父級元素設置了 transform , perspective 或者 filter 數(shù)值的值為非 none ,那么就會相對于這個祖先元素。這樣就造成了如果我最開始相對于窗口設置了一個元素 fixed 在右下角,當我 wx.pageScrollTo() 操作的時候本來相對于窗口的元素就會突然相對于 <page> 定位,當滾動結束之后因為 transform 屬性已經(jīng)消失,所以元素又會閃現(xiàn)回來。有做一個代碼片段,大家感興趣的也可以試試:wechatide://minicode/Q7zHi6m96eYV 。滾到底部之后點擊下方的藍色色塊,會發(fā)現(xiàn)藍色色塊出現(xiàn)閃動,原因就是剛才描述的。解決辦法就是去掉 wx.pageScrollTo() 的 duration 參數(shù),或者是將滾動內(nèi)容使用 <scroll-view> 包裹與 fixed 元素分離。
除了以上兩個問題之外,還有一個就是 Canvas 畫布的問題。這個 Canvas 畫布最大的問題在于小程序內(nèi)部是使用客戶端組件實現(xiàn)的,但是在開發(fā)者工具中由于是網(wǎng)頁預覽所以這里的是 HTML 中的 <canvas> 。雖然微信將 HTML 原生的 canvas 大部分接口都實現(xiàn)了,但是我要說...還是有!很多!不!一!樣!敲黑板,劃重點!所以這就導致了開發(fā)者工具上看到的效果和客戶端實際看到的效果有可能會完全不一樣,給我們開發(fā)過程帶來了無盡的阻撓!而且我這邊不知道為什么,即使是同樣身為客戶端版本的調(diào)試版,預覽版,線上版,這三者有時候表現(xiàn)出來的行為也可能造成不一樣,我當時的內(nèi)心真的是萬馬奔騰!
由于是客戶端渲染的畫布,所以小程序的畫布有以下幾個比較明顯的特點:
另外還有一個問題在于,小程序的畫布必須可視才能繪制成功,也就是說如果你給這個畫布設置 display:none 然后等它繪制成功之后再顯示出來是不可以的。目前我的解決辦法是在頁面用戶不可視區(qū)域內(nèi)先繪制然后再獲取圖片內(nèi)容。
目前接觸到的小程序的一些問題大概是這么多,有些可能在之后的版本中會解決(例如阻尼效果),而有些真的就是特性必須去適應(例如畫布)。希望我總結的一些經(jīng)驗能幫助到大家。