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