前端组件封装思路分享(以地图组件mapbox为例)

摘要

本文的核心思想是针对日常开发过程中的实际场景,以mapbox地图组件为例,结合实际封装过程,由浅入深,提升代码的可读以及可维护性,利民利己。

两句废话

说在前面

眨眼间,软件工程毕业五年了。适应工作的变化,尝试了很多角色,也有段时间不怎么实际去写代码,实际参与开发时更多的也是东拼西凑,拿来主义。

对于编程我不是天分选手,属于勤能补拙型,即便如此,除了编码时感觉自己除了能考虑的情况更全面了,很少能在主场找到成就感。趁着国庆假期,既是自我要求,也是顺应项目需求,尝试对mapbox地图应用组件进行封装,做点有意义的事。

厉兵粟马

开始前,说一下自己主动想做这件事情的初衷。开发地图相关功能,大致功能无非地图展示以及图标、线路绘制。最初阶段还好,但随着加入功能逐步增多,代码维护已然举步维艰(即便是我自己写的)。因为之前接手过别人杂乱无章的代码,知道有多痛苦,为了不把这份痛苦传递给“下一代”,便开始想着是否可以把一些公共的功能以及配置信息提取出来,让代码看起来更有逻辑,更容易理解和维护(一个5年老码农才有这份意识会不会太迟了?)。

剥茧抽丝

提取配置信息

首先能想到的,就是把展示地图所需的tokenkey等信息提取至config.js中,因为这部分信息几乎独立,当任何地方需要直接引入即可。

const tdtToken = '08f1xxxxxxx'
const pk = "pk.eyJ1IjoiY2FvxxxxxxxxxxxxxxxxxxxxxxxxxxxNxbThnYnB1bHY4In0.eyNoC2s-HIK8YNd-tAfB2g"
export const config = {
    pk: pk,
    tdTVecUrl: "https://t0.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=" + tdtToken,
    tdTFlagUrl: "https://t0.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=" + tdtToken
}

封装工具类

mapbox提供了丰富的功能调用,对于绘制(点线面)功能,可以单独提取一个组件,如果一味的把繁杂的配置信息添加到主业务文件,会使得代码行数急剧增多,不便于维护,所以新建/utils/DrawTools.js同理还可以有RenderTool等等。由于mapbox只是匆匆过客,这里并不准备展开介绍他,所以说一下提取过程中自己的一些想法。

  • 使用es6class包装工具类

小tips:

 js本身是没有类概念的,但是在es6版本中他贴心的为我们提供了class语法糖,我们不需要关注工具方法是如何放入原型对象中的。

最初我打算将map对象绑定到window全局对象中,类似这样:

// 赋值
var map = new mapboxgl(...)
window.mapbox = map
// 其他文件调用
function getMap() {
	return window[mapbox]
}
getMap().drawLabel(..)

但是这样一来,如果其他地方使用可能会出现冲突,而且直接将map对象暴露到全局对象中亦不恰当。所以决定使用类的方式,利用构造函数接受map对象然后使用。

  • 尽量少的方法

这里用到了尽量,因为任何操作都会是一把双刃剑,有舍有得,根据实际情况选择即可。

比如封装绘制Marker时,可预见的需求是绘制独立Marker或者将多个Marker一次绘制,但是共享图层id(方便同步显隐)。

方法大致相同,对于节点我便选择二重数组来接收,可以传多个值[[],[]],也可以传单个值[[]]

  • 合适的参数

当封装绘制线路的方法时,因为线路有很多的配置信息,比如type、color、layout等等信息,所以不适合单独的接收各个参数,因为会使得参数列表过长,使得调用极不方便,所以最后决定将需要根据需求变动的信息封装到对象中来接收。

/**
     * 
     * @param {站点列表} stations 
     * @param {绘制线路图层ID} id 
     * @param {绘制线路选项{layout:{},paint{}}} options 
     */
drwaLine(stations, id, options) {}
  • 莫强求

当我回看这段代码的时候陷入了沉思,封装了,但好像没什么卵用。

flyTo(position,zoom) {
    this.map.flyTo({
        center: position,
        zoom: zoom
    })
}
  • 针对参数写注释

这段是后加的,因为封装时有意使用了英文来表示含义,认为无需对参数进行注释,但是过了2天我发现最初是有失偏颇的…

/**
     *  根据提供坐标点,以及图片id,将图片放置到指定位置
     * @param {绘制坐标点,支持多个} stations 
     * @param {绘制图层ID} id 
     * @param {使用的图片ID} image 
     * @param {图片尺寸} iconSize 
     */
drwaMarker(stations, id, image, iconSize){}

经历了上述过程,代码的可维护性和可读性更强了,但是一些代码依然会显得冗长,比如加载地图,需要先加载资源,然后添加图层,此时难免有违封装的思想,如果可以调用addBaseMap类似方法,应该能更符合我们的初衷。

封装自己

将整个map作为一个组件输出,对外暴露addBaseMap方法,调用方只需要着眼于业务逻辑即可。当我们完成了组件的封装,界面就变成了这个样子(见下方-封装组件),是不是异常清晰,真正做到了各司其职。

由于绘制线路需要传递坐标等信息,所以无法直接封装进map组件,使用ref调用一下即可。

封装组件

地图开发过程中,有一些组件比如测距或者切换地图。如果把这些加到主代码中,我们可能还是会觉得主函数冗长,以测距为例,他至少包含了开始、结束、以及UI样式等等功能,此时更好的做法是将他封装成一个组件,让代码更简洁的同时,更方便维护,这里是结合Vue slot(其他框架均可实现)的方式实现。

前端组件封装思路分享(以地图组件mapbox为例)_第1张图片

代码结构

篇幅有限,为了方便理解,这里是源代码。

mapbox
-- component
	-- map.vue  #封装自己
-- config
	-- config.js  #提取配置信息
-- tool
	-- DistanceTool.vue  #封装组件
-- utils
	-- DistanceTool.js  #封装工具类
	-- DrawTool.js    #封装工具类
-- index.vue   #应用文件,处理主业务逻辑

说到最后

欲言者甚多,然不知何言也。

你可能感兴趣的:(Some,meaningful,gis应用,前端,javascript,开发语言)