最近在作一個需求,當用戶放大地圖到某個級別時,自動顯示marker
的callout標籤,當小於這個縮放級別時,則隱藏callout。然而在我實現的過程當中,卻發現一個嚴重的問題:當我操做marker數據時,會致使地圖的縮放級別發生變化(用戶沒有縮放的操做)。這TM是什麼鬼??接下來就開始爬坑。html
在mpvue的文檔中,官方是給出一些避坑指南的:vue
列表中沒有的原生事件也可使用例如 bindregionchange 事件直接在 dom 上將bind改成@,同時這個事件也很是特殊,它的 event type 有 begin 和 end 兩個,致使咱們沒法在handleProxy 中區分究竟是什麼事件,因此你在監聽此類事件的時候同時監聽事件名和事件類型既 <map @regionchange="functionName" @end="functionName" @begin="functionName"><map>
git
若是你發現@regionchange
沒有觸發,十有八九是掉到這個坑裏面了。github
然而上面的指南跟我遇到的問題沒什麼關係,咱們仍是要繼續分析。map組件的操做不少,好比拖動,縮放,點擊等。當用戶進行拖動和縮放操做時,都會觸發regionchange
事件,若是咱們綁定了scale
,latitude
或者longitude
屬性,像下面這樣:小程序
<map id="map" :markers="markers" :scale="scale" :latitude="latitude" :longitude="longitude" @callouttap="goToClass" @end="regionchange" @begin="regionchange" @regionchange="regionchange" show-location style="width: 100%; height: 100vh">
1.當咱們綁定了這些屬性(scale,latitude,longitude)等,2.用戶進行了縮放操做,3.再去進行數據操做,就會出現上述bug。api
將上訴三個條件合起來分析能夠得出,這是因爲mpvue和小程序的數據沒有保持一致引發的。當用戶進行了縮放和移動操做時,其實scale數據已經更新,然而vm中的數據並無相應的更新,在再次進行數據操做時,會致使舊的scale數據覆蓋小程序的scale,發生改動的是marker數據,縮放級別也被更新了的bug。性能優化
因此在用戶縮放操做時,須要監聽相應事件,手動更新vm中的相應的數據,來維持vm和小程序中的數據同步。否則會形成mpvue實例的數據和小程序的實際縮放數據不一致。換句話說,map組件能夠類比<input>
元素,在input元素中,經過:value
來設置數據,經過@input
來更新vm的數據,從而保證vm中的數據和DOM中元素數據的一致性。因此在map中也同樣,也要在regionchange中更新vm的數據來保證數據的一致性:dom
methods: { regionchange: (e) => { this.ctx.getScale({// this.ctx是MapContext對象的引用 https://developers.weixin.qq.com/miniprogram/dev/api/map/MapContext.html success: (res) => { this.scale = res.scale } }) this.ctx.getCenterLocation({ success: (res) => { this.latitude = res.latitude this.longitude = res.longitude } }) } }
經過上面的代碼,保證了vm數據和小程序的數據同步,避免了操做marker數據時,將舊的longitude,scale,latitude數據傳給小程序,形成在用戶沒有縮放操做的狀況下地圖被縮放。問題是解決了,可是爲何我改變的是markers的數據,mpvue會將longitude這個數據也一塊兒傳給小程序呢,我們繼續性能
Taro 1.0發佈時,提到了小程序 setState 性能優化:優化
在 setData 以前進行了一次數據 Diff,找到數據的最小更新路徑,而後再使用此路徑來進行更新
這個其實也是mpvue的一大痛點,咱們來看一下mpvue的相關源碼:
export function updateDataToMP () { const page = getPage(this) if (!page) { return } const data = formatVmData(this) throttleSetData(page.setData.bind(page), data) }
上面的代碼中,經過page.setData
,更新vm的data數據,並用throttleSetData
進行50ms一次的節流。然而,第二個參數data依然是將vm中的所有數據傳給setData,而沒有檢測究竟data中的哪些字段真正發生了變化。
綜合起來說,vm和小程序數據沒有保持統一,和mpvue setData前沒有diff一次找出真正須要更新的數據,這兩個因素共同形成了上面的bug。
在實踐中發現,雙向綁定雖然不會形成地圖縮放級別重置,可是依然會形成抖動,因此另外一種解決辦法是再也不對scale,latitude,longitude
這3個參數進行響應式綁定,改成使用原生小程序的api來操做。代碼以下:
this.$mp.page.setData({ '$root[0].latitude': data[0].latitude, '$root[0].longitude': data[0].longitude, '$root[0].scale': INIT_SCALE })
經過this.$mp.page來拿到小程序的page實例,本身在須要的時候手動進行setData。須要注意的地方時在模板中依然須要綁定,保證編譯出來的wxml知道這裏是能夠setData的,可是在data中沒有相關的值,從而使scale這些數據不是響應式的
<map id="map" :scale="scale" :latitude="latitude" :longitude="longitude"> //在js中 <script> export default{ data(){ return { 這裏不寫 scale,latitude,longitude這些屬性 } } } </script>
(完)