在mpvue中使用map如何避坑

最近在作一個需求,當用戶放大地圖到某個級別時,自動顯示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組件相似一個特殊的表單元素

然而上面的指南跟我遇到的問題沒什麼關係,咱們仍是要繼續分析。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這個數據也一塊兒傳給小程序呢,我們繼續性能

mpvue的setData設計

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>

(完)

相關文章
相關標籤/搜索