优化实战 第49期 - 责任链模式在高德地图中的应用
在项目中使用高德地图的 API
时,发现 API
的使用和业务耦合的非常严重。既不利于维护,又不利于扩展
于是乎,可以使用 责任链模式 对地图 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)
转载自:https://juejin.cn/post/7156130732627197988