• 在mpvue中使用map如何避坑


    最近在做一个需求,当用户放大地图到某个级别时,自动显示marker的callout标签,当小于这个缩放级别时,则隐藏callout。然而在我实现的过程中,却发现一个严重的问题:当我操作marker数据时,会导致地图的缩放级别发生变化(用户没有缩放的操作)。这TM是什么鬼??接下来就开始爬坑。

    官方的避坑指南

    在mpvue的文档中,官方是给出一些避坑指南的:

    列表中没有的原生事件也可以使用例如 bindregionchange 事件直接在 dom 上将bind改为@,同时这个事件也非常特殊,它的 event type 有 begin 和 end 两个,导致我们无法在handleProxy 中区分到底是什么事件,所以你在监听此类事件的时候同时监听事件名和事件类型既 <map @regionchange="functionName" @end="functionName" @begin="functionName"><map>

    如果你发现@regionchange没有触发,十有八九是掉到这个坑里面了。

    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=" 100%; height: 100vh">
    
    

    1.当我们绑定了这些属性(scale,latitude,longitude)等,2.用户进行了缩放操作,3.再去进行数据操作,就会出现上述bug。

    将上诉三个条件合起来分析可以得出,这是由于mpvue和小程序的数据没有保持一致引起的。当用户进行了缩放和移动操作时,其实scale数据已经更新,然而vm中的数据并没有相应的更新,在再次进行数据操作时,会导致旧的scale数据覆盖小程序的scale,发生改动的是marker数据,缩放级别也被更新了的bug。

    所以在用户缩放操作时,需要监听相应事件,手动更新vm中的相应的数据,来维持vm和小程序中的数据同步。不然会造成mpvue实例的数据和小程序的实际缩放数据不一致。换句话说,map组件可以类比<input>元素,在input元素中,通过:value来设置数据,通过@input来更新vm的数据,从而保证vm中的数据和DOM中元素数据的一致性。所以在map中也一样,也要在regionchange中更新vm的数据来保证数据的一致性:

    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>
    

    (完)

  • 相关阅读:
    React生命周期及事件详解
    系统重装后常见的环境变量配置
    Java 字符串格式化
    React Native实战系列教程之自定义原生UI组件和VideoView视频播放器开发
    java-json与js-json转化
    RN项目中缩进处理
    React Native 常用学习链接地址
    React-Native 常用组件学习资料链接
    Swift-继承、构造器、类型转换(嵌套)、类扩展、泛型、协议
    CallKit详解(来电提醒+骚扰拦截)
  • 原文地址:https://www.cnblogs.com/imgss/p/9700617.html
Copyright © 2020-2023  润新知