优化实战 第 49 期 - 责任链模式在高德地图中的应用

·  阅读 2819

在项目中使用高德地图的 API 时,发现 API 的使用和业务耦合的非常严重。既不利于维护,又不利于扩展

gis.jpeg

于是乎,可以使用 责任链模式 对地图 API 进行封装设计,提升其可 维护性 可扩展性 易读性

核心点在于可以通过上下文既可以实现共享又可以进行业务的灵活处理

设计轮子

  • 引入第三方库
    import koaCompose from 'koa-compose'
    复制代码
  • 集成第三方库
    class Chain {
      constructor({ config = {}, gaudMap = {} }) {
        Object.assign(this, { middleware: [], config, gaudMap })
      }
      // 添加执行节点,也就是中间件
      use(fn) {
        if (typeof fn !== 'function') {
          throw new TypeError('Middleware must be composed of functions!')
        }
        this.middleware.push(fn)
        // 返回 this 让其可以链式调用
        return this
      }
      // 接收上下文,并触发责任链执行
      transmit(context = {}) {
        koaCompose(this.middleware)(Object.assign(context, { config: this.config, gaudMap: this.gaudMap }))
      }
    }
    
    export default Chain
    复制代码

编写中间件

  • 地图初始化
    export default async (ctx, next) => {
      // 创建一个地图并设置其初始状态
      ctx.map = new AMap.Map('container', ctx.config.MAP_INIT_CONFIG)
      await next()
      const { map, citySearch } = ctx
      // 地图中心点发生改变时触发
      map.on('moveend', () => {
        ctx.position = map.getCenter()
        ctx.gaudMap.getAdressByLocation(ctx)  // 通过地理坐标(经纬度)获取地址描述信息
      })
      // 地图图块加载完成
      map.on('complete', () => {
        // 根据 IP 定位获取当前所在城市信息
        citySearch.getLocalCity((status, { province, adcode, bounds }) => {
          map.setBounds(bounds)  // 设置地图显示范围
          ctx.cityInfo = { adcode, province }
        })
      })
    }
    复制代码
  • 地图插件加载
    export default (ctx, next) => {
      const { map, config, hasHeatMap = false } = ctx
      const plugins = [
        'AMap.MouseTool', 'AMap.Geocoder', 'AMap.CitySearch', 'AMap.DistrictSearch', 'AMap.PlaceSearch', 'AMap.AutoComplete'
      ]
      hasHeatMap && plugins.push('AMap.HeatMap')
    
      AMap.plugin(plugins, async () => {
        Object.assign(ctx, {
          mouseTool: new AMap.MouseTool(map),  // 鼠标绘制工具
          geocoder: new AMap.Geocoder(config.GEOCODER_CONFIG),  // 将地址描述信息和地理坐标做相互转化
          citySearch: new AMap.CitySearch(),  // 根据 IP 定位获取当前所在城市信息
          districtSearch: new AMap.DistrictSearch(config.DISTRACT_SEARCH_CONFIG),  // 行政区查询服务
          placeSearch: new AMap.PlaceSearch({ map }),  // 提供某一特定地区的位置查询服务,即 POI(兴趣点类型)搜索
          autocomplete: new AMap.AutoComplete({ citylimit: true }),  // 根据输入的关键字显示相关的匹配信息
        })
        hasHeatMap && (ctx.heatmap = new AMap.HeatMap(map, config.HEATMAP_STYLE))
        await next()
      })
    }
    复制代码
  • 绘制工具初始化
    export default async (ctx, next) => {
      const { map, mouseTool, config, drawToolContainer, gaudMap } = ctx
      drawToolContainer.addEventListener('click', ({ target }) => {
        map.clearMap()
        ctx.type = target.dataset.type
        if (ctx.type === 'marker') {
          map.setDefaultCursor('pointer')
          mouseTool[ctx.type]({ content: '<span></span>' })  // 传入空 HTML 字符串,覆盖 icon 属性
        }
        if (['circle', 'rectangle', 'polygon', 'polyline'].includes(ctx.type)) {
          map.setDefaultCursor('crosshair')
          mouseTool[ctx.type](config.DRAW_AREA_STYLE)
        }
      })
    
      await next()
      // 鼠标工具绘制覆盖物结束时触发 draw 事件
      mouseTool.on('draw', function({ obj }) {
        this.close(false)  // 关闭鼠标操作,保留所绘制的覆盖物对象
        map.setDefaultCursor('move')
        if (ctx.type === 'marker') {
          ctx.markerPosition = obj.getPosition()
          gaudMap.getAdressByLocation(ctx)
          gaudMap.markerSelectionExtension(ctx)  // 标记选点的扩展
        }
      })
    }
    复制代码

使用轮子

  • 导入中间件
    import { initialize, pluginsLoad, mouseToolDraw } from './middleware'
    复制代码
  • 创建执行链
    const chain = new Chain({ config, gaudMap })
    chain.use(initialize).use(pluginsLoad).use(mouseToolDraw)
    复制代码
  • 传入上下文,触发执行链
    chain.transmit(this.context)
    复制代码
分类:
前端
收藏成功!
已添加到「」, 点击更改