菜🐥组件化开发 | 二次封装高德地图组件(丐版) | 欢迎指正👋

941 阅读3分钟

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」。

⏳ 前言

本人是一只前端🐥,最近在学习到组件的封装时上手封装了高德地图组件供学习使用,这个组件目前有很多需要改正的地方,希望各位大佬给出指正或建议,不胜感激!

🚚 组件需求

  • 模板化

    我们需要在创建组件实例的时候渲染HTML模板,因此需要将组件模板化

  • 可扩展性

    地图中的标记点marker是根据后端数据动态传入的,也就是说我们需要将数据源传递给Map组件,然后Map组件利用这个数据源将标记点渲染到HTML中

  • api

    我们需要根据组件的业务需求,为组件设计api,例如为标记点添加信息窗体、自定义信息窗体内容

🔧 实现

在开始写代码之前,我们先来回顾一下组件的需求:

模板化

  • 当用户实例化组件时,需要传入容器的id名container和配置选项options(可选,不传则为默认配置项),注册地图实例并渲染到容器中

设计api

  • 用户可以传入标记点数据markerList,渲染到地图中
  • 为每个标记点marker注册信息窗体,并在点击标记点时弹出相应信息窗体
  • 用户可以自定义窗体内容,传入信息窗体的标题title和内容content
  • 为地图加入生命周期和点击事件,监听地图加载完成和点击地图事件

根据上面的需求,我设计了以下6个api

  • init() - 初始化地图实例
  • renderMarkers(markerList) - 渲染标记点
  • registerInfoWindow() - 注册信息窗体
  • createInfoWindow(title, content) - 创建信息窗体样式(这部分来自高德地图官方文档)
  • onMounted() - 组件生命周期
  • handleClickMap() - 地图点击事件
class GaodeMap {
  constructor(container, options) {
    this.container = container
    this.options = options
    this.map = {}
    this.markers = []
    this.init(container, options)
  }
  
  init(container, options) {
    this.map = new AMap.Map(container, options);
    this.map.on('complete', () => {
      this.onMounted()  // register lifecycle hook for component
    })
  }

  renderMarker(markerList) {
    markerList.map(item => {
      const marker = new AMap.Marker({
        position: new AMap.LngLat(item.Lng, item.Lat),
        title: item.title
      });
      marker.address = item.address   // 对应item上的属性address赋值到marker上
      this.markers.push(marker)
    })
    this.map.add(this.markers)
  }

  registerInfoWindow() {
    const content = [];
    this.markers.map(marker => {
      const title = marker.w.title
      const address = marker.address
      const infoWindow = new AMap.InfoWindow({
        isCustom: true,
        content: this.createInfoWindow(title, address, content.join("<br/>")),
        offset: new AMap.Pixel(16, -45)
      })
      AMap.event.addListener(marker, 'click', (e) => {
        // 监听标记点点击事件
        infoWindow.open(this.map, marker.getPosition())
      });
    })
  }

  createInfoWindow(title, content) {
    var info = document.createElement("div");
    info.className = "custom-info input-card content-window-card";

    //可以通过下面的方式修改自定义窗体的宽高
    //info.style.width = "400px";
    // 定义顶部标题
    var top = document.createElement("div");
    var titleD = document.createElement("div");
    var closeX = document.createElement("img");
    top.className = "info-top";
    titleD.innerHTML = title;
    closeX.src = "https://webapi.amap.com/images/close2.gif";
    closeX.onclick = this.closeInfoWindow()

    top.appendChild(titleD);
    top.appendChild(closeX);
    info.appendChild(top);

    // 定义中部内容
    var middle = document.createElement("div");
    middle.className = "info-middle";
    middle.style.backgroundColor = 'white';
    content += "地址:" + `<a href="javascript:;">${address}</a>`
    middle.innerHTML = content;
    info.appendChild(middle);

    // 定义底部内容
    var bottom = document.createElement("div");
    bottom.className = "info-bottom";
    bottom.style.position = 'relative';
    bottom.style.top = '0px';
    bottom.style.margin = '0 auto';
    var sharp = document.createElement("img");
    sharp.src = "https://webapi.amap.com/images/sharp.png";
    bottom.appendChild(sharp);
    info.appendChild(bottom);

    return info;
  }
  
  closeInfoWindow() {
    return () => {
      this.map.clearInfoWindow();
    }
  }
  
  onMounted() {
    // do something when the map is completed
    console.log('Map is mounted!');
    this.map.on('click', handleClickMarker)
  }

  handleClickMarker(e) {
    console.log('点击了地图的',[e.lnglat.lng, e.lnglat.lat]);
  }
  
}

具体使用:

;(async function() {
    const options = {
    resizeEnable: true, //是否监控地图容器尺寸变化
    zoom: 11, //初始化地图层级
    center: [113.122717, 23.008762], //初始化地图中心点
  }
  const myMap = new GaodeMap('container', options)

  const { data: markerList } = await axios.post('http://localhost:3000/api')
  myMap.renderMarker(markerList)
  myMap.registerInfoWindow()
})();

效果如下图所示:

image.png

总结

通过这个高德地图的丐版组件封装,我们可以总结出组件封装的一般步骤:

  1. 渲染HTML结构
  2. 设计组件API
  3. 扩展性

这个丐版的地图组件只是暂时实现了我的业务需求,但它仍有许多不足的地方:

  1. 需要修改信息窗体内容时需要修改createInfoWindow部分的代码,没有做到组件的封装性
  2. 不能为特定标记点选定特定的图标

最后希望大家提出建议和意见!!!