在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>

(完)

相关文章
相关标签/搜索