Why develop such a component library?

The idea came from a previous project that required rendering small squares at zoom 16 level (100 * 100) using the Amap Polygon overlay, rendering 1K + on a MAC 13-inch screen. Close to 3K of overlay needed to be rendered under the external 27-inch screen.

I tried using vue-amap, a component library, which took about 5 seconds to render for 1K overlay and 30+ seconds for 3K overlay, even crashing the browser. I chose to encapsulate a component myself with the AMap SDK, but the performance is worse than vuE-AMAP, 1K overlay takes 10+ seconds, if I give this to the product, he probably beats me to death.

Here is a rendering of 2000 overlay fast-AMap versus Vue-AMap to get a feel for it.

FastAMap Codepen. IO/taoxusheng /…
VueAMap Codepen. IO/taoxusheng /…

Why are there significant performance issues with using the Autonavi SDK in Vue?

In fact, when developing with Vue, we pass data to components or data through props, and Vue deepwatches that data by default, and the Polygon instances I put on Data are bound to Vue every time, which is why the performance degrades. Finally, I packaged a Polygon rendering class by myself, and the rendering time of 1K + overlay was about 1 second. Although the performance problem was solved, it was very inconvenient to use, because there was too much code about rendering processing in the business to only care about data, and many configuration attributes needed to be written.

How does FastAMap solve this problem?

I wanted to encapsulate a component after that project, but I had problems.

  1. How to solve the problem of data being bound by Vue?
  2. Map loading may be asynchronous, but what if the map instance is loaded by the time the child component is used?
  3. There may be multiple maps on a page, and multiple map-related sub-components. How do the sub-components get map instances, and how do they ensure that their instances are correct?

The data of decoupling

Any data we pass through props will be bound to Vue, but we can clone a copy of the data. In the component watch data source, once the data changes, the corresponding instance is created and put into an array that will not be watched.

{  
  watch: {
    options: {
      immediate: true.handler: 'handleOptionsChange'
    }
  },

  created() {
    mapOptionLoader().then(AMap= > {
      AMapInstance = AMap
    })

    // Due to the need to decouple Amap from VUE, the array of instances created here cannot be watched by vue.
    if (!this.instanceList) {
      this.instanceList = []
    }
  }

  methods: {
    handleOptionsChange(options) {
      this.rendered = false
      this.getAMapPromise().then((a)= > {
        // Clear the last instance
        this.clearAll()

        // Get the corresponding map instance
        const map = this.getMapInstance(this.mid)
        options.forEach(option= > {
          // Invoke the component's create instance method
          const instance = this.createInstance(option)
          this.instanceList.push(instance)
        })
        this.$nextTick((a)= > {
          this.addPlugins()
        })
        map.add(this.instanceList)
      })
    },
  }
}
Copy the code

v-if slot

Add a Boolean value mapLoaded to the map component. Render the subcomponents after the map is loaded. The child component gets the map instance in the Mounted function.

<div ref="container" class="cpt-fast-map" :style="{ height: height + 'px' }">
  <div class="fast-map-slot-container">
    <slot v-if="mapLoaded"></slot>
  </div>
</div>// js { mounted() { this.getAMapPromise() .then(AMap => { let map = null const options = this.createMapOptions() try { map = new AMap.Map(this.$refs.container, } Catch (e) {console.error(e)} if (map) {this.$_amapMixin_setMapInstance(this.mid, This.$_amapMixin_addEvents(map, events)}}). Catch (noop)} methods: {// The complete event registered in the map instance, which is triggered to indicate that the map is loaded. handleCompleteEvent(event) { this.mapLoaded = true this.$emit(event.type, event, this.getMapInstance(this.mid)) }, } }Copy the code

Figure instance registry

AMap encapsulates AMap registry class. After the map is successfully created, the instance is added to the registry, and the instance in the registry is deleted after destruction. All map components need to add a registry ID to ensure that each component gets its map instance.

import Map from './map-shim'

/** * Amap instance registry */
export default class MapRegistry {
  constructor() {
    this.registry = null
  }

  setMap(mid, instance) {
    if(! mid) { warn('The parameter mid cannot be empty')}if (this.map) {
      if (this.map.get(mid)) {
        warn(`mid: ${mid} already exists in the map registry`)}else {
        this.map.set(mid, instance)
      }
    }
  }

  getMap(mid) {
    return this.map && this.map.get(mid)
  }

  deleteMap(mid) {
    if (this.getMap(mid)) {
      this.map.delete(mid)
    } else {
      warn(`No instance of mid: ${mid} found in the map registry`)}}static getRegistryInstance() {
    if (!this.registry) {
      this.registry = new MapRegistry()
      this.registry.map = new Map()}return this.registry
  }
}
Copy the code
Project address and Document address:

Making: link.zhihu.com/?target=htt… Documents: txs1992. Making. IO/fast – amap /