WXS響應事件
基礎庫 2.4.4 開始支持,低版本需做兼容處理。
背景
有頻繁用戶交互的效果在小程序上表現是比較卡頓的,例如頁面有 2 個元素 A 和 B,用戶在 A 上做 touchmove 手勢,要求 B 也跟隨移動,movable-view 就是一個典型的例子。一次 touchmove 事件的響應過程為:
a、touchmove 事件從視圖層(Webview)拋到邏輯層(App Service)
b、邏輯層(App Service)處理 touchmove 事件,再通過 setData 來改變 B 的位置
一次 touchmove 的響應需要經過 2 次的邏輯層和渲染層的通信以及一次渲染,通信的耗時比較大。此外 setData 渲染也會阻塞其它腳本執行,導致了整個用戶交互的動畫過程會有延遲。
實現方案
本方案基本的思路是減少通信的次數,讓事件在視圖層(Webview)響應。小程序的框架分為視圖層(Webview)和邏輯層(App Service),這樣分層的目的是管控,開發者的代碼只能運行在邏輯層(App Service),而這個思路就必須要讓開發者的代碼運行在視圖層(Webview),如下圖所示的流程:
使用 WXS 函數用來響應小程序事件,目前只能響應內置組件的事件,不支持自定義組件事件。WXS 函數的除了純邏輯的運算,還可以通過封裝好的ComponentDescriptor
實例來訪問以及設置組件的 class 和樣式,對於交互動畫,設置 style 和 class 足夠了。WXS 函數的例子如下:
var wxsFunction = function(event, ownerInstance) { var instance = ownerInstance.selectComponent('.classSelector') // 返回組件的實例 instance.setStyle({ "font-size": "14px" // 支持rpx }) instance.getDataset() instance.setClass(className) // ... return false // 不往上冒泡,相當於調用了同時調用了stopPropagation和preventDefault }
其中入參 event
是小程序事件對象基礎上多了 event.instance
來表示觸發事件的組件的 ComponentDescriptor
實例。ownerInstance
表示的是觸發事件的組件所在的組件的 ComponentDescriptor
實例,如果觸發事件的組件是在頁面內的,ownerInstance
表示的是頁面實例。
ComponentDescriptor
的定義如下:
方法 | 參數 | 描述 |
---|---|---|
selectComponent | selector對象 | 返回組件的 ComponentDescriptor 實例。 |
selectAllComponents | selector對象數組 | 返回組件的 ComponentDescriptor 實例數組。 |
setStyle | Object/string | 設置組件樣式,支持rpx 。設置的樣式優先級比組件 wxml 里面定義的樣式高。不能設置最頂層頁面的樣式。 |
addClass/removeClass/ hasClass | string | 設置組件的 class。設置的 class 優先級比組件 wxml 里面定義的 class 高。不能設置最頂層頁面的 class。 |
getDataset | 無 | 返回當前組件/頁面的 dataset 對象 |
callMethod | (funcName:string, args:object) | 調用當前組件/頁面在邏輯層(App Service)定義的函數。funcName表示函數名稱,args表示函數的參數。 |
requestAnimationFrame | Function | 和原生 requestAnimationFrame 一樣。用於設置動畫。 |
getState | 無 | 返回一個object對象,當有局部變量需要存儲起來后續使用的時候用這個方法。 |
triggerEvent | (eventName, detail) | 和組件的triggerEvent一致。 |
WXS 運行在視圖層(Webview),里面的邏輯畢竟能做的事件比較少,需要有一個機制和邏輯層(App Service)開發者的代碼通信,上面的 callMethod
是 WXS 里面調用邏輯層(App Service)開發者的代碼的方法,而 WxsPropObserver
是邏輯層(App Service)開發者的代碼調用 WXS 邏輯的機制。
使用方法
- WXML定義事件:
<wxs module="test" src="./test.wxs"></wxs> <view change:prop="{{test.propObserver}}" prop="{{propValue}}" bindtouchmove="{{test.touchmove}}" class="movable"></view>
上面的change:prop
(屬性前面帶change:前綴)是在 prop 屬性被設置的時候觸發 WXS 函數,值必須用{{}}
括起來。類似 Component 定義的 properties 里面的 observer 屬性,在setData({propValue: newValue})
調用之后會觸發。
注意:WXS函數必須用{{}}
括起來。當 prop 的值被設置 WXS 函數就會觸發,而不只是值發生改變,所以在頁面初始化的時候會調用一次WxsPropObserver
的函數。
- WXS文件
test.wxs
里面定義並導出事件處理函數和屬性改變觸發的函數:
module.exports = {
touchmove: function(event, instance) {
console.log('log event', JSON.stringify(event))
},
propObserver: function(newValue, oldValue, ownerInstance, instance) {
console.log('prop observer', newValue, oldValue)
}
}
更多示例請查看在開發者工具中預覽效果
Tips
- 目前還不支持原生組件的事件、input和textarea組件的 bindinput 事件
- 1.02.1901170及以后版本的開發者工具上支持交互動畫,最低版本基礎庫是2.4.4
- 目前在WXS函數里面僅支持console.log方式打日志定位問題,注意連續的重復日志會被過濾掉。
.