最近在学习公司的一个地图平台,正好结合vue3 composition-api将一些基础效果实现。
写过vue2的人都知道,在一个vue文件中可能有许多功能点,比如一个列表页有搜索功能、分页功能、导出功能等等。在一些人员充足的团队中,各个功能点可能有不同的人来负责开发,vue2中,只能等待一个人将一个功能点完成后另一个人才能继续开发另一个功能点,否则就会冲突(当然愿意花时间去解决冲突也可以同时开发);而使用vue3 composition-api,就可以将各个功能点拆分成单独的文件,相关人员独立开发,互不影响,只需要在开发完成后在主页面中调用即可。
网上有很多拿mixin来说明composition-api好处的例子,但个人感觉那并不是主要的作用,实际项目中很少有在各个vue文件中使用大量mixin的做法,毕竟耦合度太高,容易出问题。composition-api官网中的介绍中拿了vue2在大型应用中的不利之处,并提出composition-api就是为了解决这种不利(具体有啥不利之处可参照官网),其实通俗点将就是vue2只能把所有功能的data、methods、watch等等写到vue文件的指定位置,这种写法会导致一个复杂的vue文件尤为混乱;而composition-api就显得更为灵活,可以将各个功能点的代码写在同一区域,说了这么多,个人认为其实composition-api就是实现了各个功能点分离,从而利于项目的维护,提高了代码的可读性。接下来本人就使用composition-api结合mineDate的二维地图实现图层展示与隐藏。
最终效果
上图中的隐藏与展示、marker点加载、地图底图的展示都是放在不同的文件中,最终在一个主页面中调用各个部分的功能代码。
地图底图文件:baseMap2D.vue
<template>
<div class="base-map2D" :id="mapId"></div>
</template>
<script>
import { nextTick, reactive } from 'vue'
export default {
props: {
mapId: {
type: [String, Number]
}
},
setup (props, context) {
/**
* 全局参数设置
*/
minemap.domainUrl = 'https://minedata.cn'
minemap.dataDomainUrl = 'https://minedata.cn'
minemap.serverDomainUrl = 'https://minedata.cn'
minemap.spriteUrl = 'https://minedata.cn/minemapapi/v2.1.0/sprite/sprite'
minemap.serviceUrl = 'https://minedata.cn/service/'
/**
* key、solution设置
*/
minemap.key = '16be596e00c44c86bb1569cb53424dc9'
minemap.solution = 12877
// let map = reactive(null)
let map = null
const mapConfif = reactive({ center: [] })
const initMap = () => {
map = new minemap.Map({
container: props.mapId,
style: 'https://minedata.cn/service/solu/style/id/12877' /* 底图样式 */,
// center: [116.46, 39.92] /* 地图中心点 */,
// center: [45.7413805110411, 127.165046475283],
center: [127.165046475283, 45.7413805110411],
zoom: 5/* 地图默认缩放等级 */,
pitch: 0 /* 地图俯仰角度 */,
maxZoom: 17 /* 地图最大缩放等级 */,
minZoom: 3 /* 地图最小缩放等级 */,
projection: 'MERCATOR'
})
map.on('load', () => {
mapConfif.center = map.getCenter()
// 将mapObj(地图对象)emit给父组件,便于操作地图
context.emit('mapObj', map)
})
}
nextTick(() => {
initMap()
})
return {
mapConfif,
map
}
}
}
</script>
<style scoped lang='scss'>
.base-map2D {
height: 100%;
}
</style>
marker点加载功能抽离:userPoiMarker.js
import axios from 'axios'
import { ref, reactive } from 'vue'
export default function usePoiMarker () {
const markerList = ref([])
const poiObj = reactive({})
const layersIds = reactive([])
const getData = (map) => {
axios.get('static/json/tree.json').then((res) => {
markerList.value = res.data
setPoiObj()
addMarker(map)
})
}
const showList = (map) => {
for (const key in markerList.value) {
console.log(key, markerList.value[key])
}
}
// 将数据组合成poiObj(poiObj每一项是geoJSON类型的数据)
const setPoiObj = () => {
for (const key in markerList.value) {
const geoObj = {
type: 'FeatureCollection',
features: []
}
const pointArr = markerList.value[key]
pointArr.forEach(point => {
const obj = {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
+point.lng || +point.lon,
+point.lat
]
},
properties: {
title: point.name || point.poiName,
...point
}
}
geoObj.features.push(obj)
})
poiObj[key] = geoObj
}
console.log(poiObj, 'poiObj')
}
// 添加marker点
const addMarker = (map) => {
map.loadImage(
'static/images/marker-cur.png',
function (error, image) {
if (error) throw error
// 添加自定义图标
map.addImage('logo', image)
}
)
console.log(poiObj, 'pppppppppppp')
for (const key in poiObj) {
map.addSource(`${key}PoiSource`, {
type: 'geojson',
data: poiObj[key]
})
map.addLayer({
id: `${key}PoiLayer`,
type: 'symbol',
source: `${key}PoiSource`,
layout: {
'icon-image': 'logo',
'text-field': '{title}', // {title}
'text-offset': [0, 0.6],
'text-anchor': 'top',
'icon-allow-overlap': true, // 图标允许压盖
'text-allow-overlap': true // 图标覆盖文字允许压盖
},
paint: {
'text-color': '#ff0000',
'text-halo-color': '#000000',
'text-halo-width': 0.5
},
minzoom: 5,
maxzoom: 17.5
})
layersIds.push(`${key}PoiLayer`)
}
console.log(layersIds,'layerIds')
}
return {
markerList,
getData,
showList,
addMarker,
layersIds
}
}
图层隐藏展示功能抽离:useLayerToggle.js
export default function useLayerToggle () {
const showlayers = (map, layers) => {
layers.forEach((layer) => {
map.setLayoutProperty(layer, 'visibility', 'visible')
})
}
const hideLayers = (map, layers) => {
layers.forEach((layer) => {
map.setLayoutProperty(layer, 'visibility', 'none')
})
}
return {
showlayers,
hideLayers
}
}
地图主页面
<template>
<div class="comMap2">
<div class="map-tackle">
<el-button @click="hideLayers(mapObj, layersIds)">隐藏图层</el-button>
<el-button @click="showlayers(mapObj, layersIds)">显示图层</el-button>
</div>
<com-map-2 mapId="comMap2Id" @mapObj="getMap"></com-map-2>
</div>
</template>
<script>
import comMap2 from '../../components/baseMap2D/index.vue'
import usePoiMarker from './js/userPoiMarker'
import { ref } from 'vue'
import useLayerToggle from './js/userLayerToggle'
export default {
components: {
comMap2
},
setup () {
// 地图基础
const mapObj = ref()
const getMap = (val) => {
mapObj.value = val
getData(mapObj.value)
}
// marker标记
const { markerList, showList, getData, addMarker, layersIds } =
usePoiMarker()
// 图层切换
const { showlayers, hideLayers } = useLayerToggle()
return {
mapObj,
getMap,
markerList,
showList,
getData,
addMarker,
showlayers,
hideLayers,
layersIds
}
}
}
</script>
<style scoped lang='scss'>
.comMap2 {
height: 100%;
position: relative;
.map-tackle {
position: absolute;
left: 20px;
top: 20px;
z-index: 999;
}
}
</style>
可以看出使用composition-api使整个vue文件的js部分显得更为清晰,以往实现这种功能需要在一个vue文件的data中定义各个功能点需要的数据,在methods中定义各个功能点需要的函数,这种做法使各个功能点混在一起,显得混乱。使用composition-api则将各个功能的抽离出来,后期维护某个功能点只需要关注该功能点所在模块即可。
至于composition-api的具体语法,这里不在赘述,网上教程多的是,有机会可以搞个专栏大家一起学习。本篇主要作为本人在接下来项目中复制、粘贴的来源~~