在公司小程序也開發了一段時間了,中間遇到過不少問題,特此記錄幾個比較典型的問題和解決方案。javascript
此問題提供源碼demo,可導入微信開發者工具查看。css
textarea 是小程序的原生組件,它的一個表現就是優先級很高,這致使了一些困擾,好比咱們有一個表單頁面,最下面就是一個textarea和一個保存按鈕,這會致使textarea的文字會浮如今按鈕上。以下圖:html
它最大的問題時會致使保存按鈕可能點擊無效或者會彈出鍵盤,而且開發者工具模擬器和真機表現不同,這真是個坑!java
模擬器中,針對 position:fixed
定位的按鈕,咱們加一個 z-index:10
便可, z-index
等於多少合適不清楚,試了等於1是不行的,10就能夠,其他的值沒試過。git
.submit-cls { position: fixed; left: 30px; right: 30px; bottom: 300px; text-align: center; background-color: green; color: #fff; z-index: 10; }
模擬器中的表現:github
然兒,真機上(Android)依然無效!以下圖:canvas
因而我想到了 cover-view 標籤,cover-view 是微信提供的一個原生組件,它是覆蓋在原生組件之上的文本視圖,可覆蓋的原生組件包括 map、video、canvas、camera、live-player、live-pusher之上,只支持嵌套 cover-view、cover-image,可在 cover-view 中使用 button。小程序
用 cover-view 標籤包裹 button 如何呢?鬱悶的事情發生了,真機上按鈕不見了!。。。這方法貌似不行。。微信小程序
<cover-view> <button class="submit-cls" id='button' bindtap="onClick"> Button z-index: 10 </button> </cover-view>
那我直接用 cover-view 標籤做爲按鈕呢?瀏覽器
<cover-view class="cover-view-clas" id='cover-view' bindtap="onClick"> cover-view z-index: 10 </cover-view>
.cover-view-clas { position: fixed; height: 40px; line-height: 40px; left: 30px; right: 30px; bottom: 250px; text-align: center; background-color: orangered; color: #fff; }
結果在模擬器裏不行
可是真機上表現很好。因而我也加了一個 z-index: 10
,這樣模擬器和真機表現就一致。
綜上所述,要解決這個問題彷佛只有一個辦法,那就是用 cover-view
+ z-index:10
,然兒這樣會致使一個的反作用,無法使用微信的開放能力好比 open-type
。
咱們知道,與傳統的瀏覽器Web頁面最大區別在於,小程序的是基於 雙線程 模型的,在這種架構中,小程序的渲染層使用 WebView 做爲渲染載體,而邏輯層則由獨立的 JsCore 線程運行 JS 腳本,雙方並不具有數據直接共享的通道,所以渲染層和邏輯層的通訊要由 Native 的 JSBrigde 作中轉。
每當小程序視圖數據須要更新時,邏輯層會調用小程序宿主環境提供的 setData
方法將數據從邏輯層傳遞到視圖層,通過一系列渲染步驟以後完成UI視圖更新。然而當 setData
傳遞大量的新數據、頻繁的執行 setData
操做、過多的頁面節點數時會影響渲染性能。
意思是, setData 只用來通知頁面更新,只有須要通知頁面更新的時候,頁面引用了某個 data 字段時才使用,其它字段數據咱們有時候可能只是爲了讓 js 方便使用。好比以下數據
data: { form: { name: 'xxxx', ... ... }, index: 0 }
假如 頁面上根本沒用到 index 來展現,只是咱們的邏輯變量,那麼咱們在賦值的時候就直接 this.data.index = xxx
便可,不要用 setData 去賦值了。
setData 是支持使用 數據路徑 的方式對對象的局部字段進行更新,咱們可能會遇到這樣的場景: list 列表是從後臺獲取的數據,並展現在頁面上,當 list 列表的第一項數據的 src 字段須要更新時,通常狀況下咱們會從後臺獲取新的 list 列表,執行 setData 更新整個 list 列表。
// 後臺獲取列表數據 const list = requestSync(); // 更新整個列表 this.setData({ list });
實際上,只有個別字段須要更新時,咱們能夠這麼寫來避免整個 list 列表更新:
// 後臺獲取列表數據 const list = requestSync(); // 局部更新列表 this.setData({ 'list[0].src': list[0].src });
小程序自定義組件的實現是由小程序官方設計的 Exparser 框架所支持,框架實現的自定義組件的組件模型與 Web Components 標準的 Shadow DOM 類似:
在頁面引用自定義組件後,當初始化頁面時,Exparser 會在建立頁面實例的同時,也會根據自定義組件的註冊信息進行組件實例化,而後根據組件自帶的 data 數據和組件WXML,構造出獨立的 Shadow Tree ,並追加到頁面 Composed Tree 。建立出來的 Shadow Tree 擁有着本身獨立的邏輯空間、數據、樣式環境及setData調用,這是組件化帶來的好處。
基於自定義組件的 Shadow DOM 模型設計,咱們能夠將頁面中一些須要高頻執行 setData 更新的功能模塊(如倒計時、進度條等)封裝成自定義組件嵌入到頁面中。當這些自定義組件視圖須要更新時,執行的是組件本身的 setData ,新舊節點樹的對比計算和渲染樹的更新都只限於組件內有限的節點數量,有效下降渲染時間開銷。
在項目中,有一個預定模塊,字段忒多,保險業務嘛,須要用戶填寫各類數據的,爲了用戶體驗拆成了多個步驟,如圖
一開始,業務上要求切換tab的時候數據要緩存,跟Vue的 keep-alive 同樣,可是小程序沒有這樣的機制,因此利用小程序的 hidden
屬性,也就是 Vue 中的 v-show
,組件始終會被渲染,只是簡單的控制顯示與隱藏。關於wx:if 和 hidden。
這樣的致使頁面節點太多,在低性能手機上會出現卡死的現象,直接沒法渲染或者渲染太慢。
後來改成 wx:if
來切換
<view wx:if="{{current === 0}}">......</view> <view wx:if="{{current === 1}}">......</view> <view wx:if="{{current === 2}}">......</view> ... ...
這樣以來一次渲染節點太多的問題解決了,可是怎麼實現tab切換的時候輸入的內容杯緩存呢?其實咱們的笨辦法就是切換的時候把前一個表單內容保存到 localStorage 或 gloabData 中,切換回去的時候再取出來填充,這中間會有一個明顯的渲染過程,肉眼可見,沒辦法,目前只能犧牲一點點體驗了。
對於這種大型表單,數據處理和邏輯交互的時候要很是注意,很容易出現性能問題。
此次就說這麼多吧,文章若有什麼錯誤,或有什麼想法,請留言,不吝賜教!
參考文章:微信小程序渲染性能調優