一个好习惯,先给结论
有5种方法:
- iframe嵌套,这种最简单
- vue模板字符串创建vue实例
- 使用.vue单文件创建vue实例
- 使用vue3新方法defineCustomElement,这种最推荐
- 使用jsx
本文首发于blog.gis1024.com
预览(点击试试)
这里才是引言
在gis项目开发中,经常遇到需要在地图弹窗中显示信息的需求。几乎所有地图框架的弹窗api,都是用html字符串的形式填写内容。
以百度地图为例,可以看到BMapGL.InfoWindow
后跟的是一个字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var map = new BMapGL.Map("allmap"); var point = new BMapGL.Point(116.404, 39.925); map.centerAndZoom(point, 15);
var marker = new BMapGL.Marker(point); map.addOverlay(marker); var opts = { width : 200, height: 100, title : "故宫博物院" , message:"这里是故宫" } var infoWindow = new BMapGL.InfoWindow("<div>地址:北京市东城区王府井大街88号乐天银泰百货八层</div>", opts); marker.addEventListener("click", function(){ map.openInfoWindow(infoWindow, point); });
|
现代前端的开发,几乎没有原生js或者jq拼写字符串这种效率低下的形式了,那我们怎么可以将我们的vue组件(或react组件)传递给地图弹窗呢?
下面以vue3
+ leaflet
的组合演示几种方法,其他地图如百度地图
、高德地图
、mapbox
、openlayers
、leaflet
、maptalks
等地图框架和vue2
的组合同理。
方法1:iframe嵌套
将弹窗中的内容写成一个.vue组件,并且在路由中将这个组件指定为一个路由
router.js
文件如下
1 2 3 4 5 6 7 8 9 10
| ...其他代码省略 { path: '/1', component: () => import('../views/1_iframe/index.vue') }, { path: '/1_inner', component: () => import('../views/1_iframe/iframe.vue') } ...其他代码省略
|
地图主文件views/1_iframe/index.vue
文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <template> <div style="width: 100%;height: 100%;" id="map"></div> </template>
<script setup> import 'leaflet/dist/leaflet.css'; import * as L from 'leaflet'; import {onMounted} from "vue";
onMounted(() => { const map = L.map('map').setView([31.491064, 120.311889], 13);
const baseLayer = L.tileLayer("http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}", { subdomains: "1234" }); map.addLayer(baseLayer);
const marker = L.marker([31.491064, 120.311889]).addTo(map); marker.bindPopup(`<iframe style="border: 0;height: 100px;width: 200px;" src="/#/1_inner?id=666"></iframe>`).openPopup(); })
</script>
|
弹窗页面/views/1_iframe/iframe.vue
如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div> <span>接受传进来的值为:</span> <span>{{ route.query.id }}</span> </div> <div>这种方法真简单</div> </template>
<script setup> import {useRoute} from 'vue-router'
const route = useRoute(); </script>
<style scoped>
</style>
|
方法2:vue模板字符串创建vue实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| <template> <div style="width: 100%;height: 100%;" id="map"></div> </template>
<script setup> import 'leaflet/dist/leaflet.css'; import * as L from 'leaflet'; import {createApp, onMounted} from "vue";
onMounted(() => { const map = L.map('map').setView([31.491064, 120.311889], 13);
const baseLayer = L.tileLayer("http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}", { subdomains: "1234" }); map.addLayer(baseLayer);
const marker = L.marker([31.491064, 120.311889]).addTo(map);
marker.bindPopup(`<div id="my_popup" style="width: 200px;height: 100px;"></div>`)
const id = 666; let vm; marker.on('popupopen', e => { vm = createApp({ template: ` <div>通过vue的字符串模板创建,可以将外部的参数直接传递到data中</div> <div>接收到的参数值:{{ count }}</div> <div> <button @click="count++">+1</button> </div> `, data() { return { count: id } } }) vm.mount('#my_popup') })
marker.on('popupclose', e => { vm.unmount(); })
marker.openPopup(); })
</script>
|
方法3:使用.vue单文件创建vue实例
这种方法和方法2本质上一样,不过是将写的一堆模板提取到单文件中了
index.vue
文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <template> <div style="width: 100%;height: 100%;" id="map"></div> </template>
<script setup> import 'leaflet/dist/leaflet.css'; import * as L from 'leaflet'; import {createApp, onMounted} from "vue"; import component from './component.vue';
onMounted(() => { const map = L.map('map').setView([31.491064, 120.311889], 13);
const baseLayer = L.tileLayer("http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}", { subdomains: "1234" }); map.addLayer(baseLayer);
const marker = L.marker([31.491064, 120.311889]).addTo(map);
marker.bindPopup(`<div id="my_popup" style="width: 200px;height: 100px;"></div>`)
const id = 666; let vm; marker.on('popupopen', e => { vm = createApp(component, {id: id}) vm.mount('#my_popup') })
marker.on('popupclose', e => { vm.unmount(); })
marker.openPopup(); })
</script>
|
component.vue
文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> <div> <span>接受传进来的值为:</span> <span>{{ props.id }}</span> </div> <div>这种方法也很简单</div> </template>
<script setup> const props = defineProps(['id']); </script>
<style scoped>
</style>
|
方法4:使用vue3新方法defineCustomElement
这种方法最推荐,和方法3比较类似,但是不用手动销毁vue实例。原理是利用了vue3的新方法,创建了现代浏览器能够默认识别的html组件。
index.vue
文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <template> <div style="width: 100%;height: 100%;" id="map"></div> </template>
<script setup> import 'leaflet/dist/leaflet.css'; import * as L from 'leaflet'; import {defineCustomElement, onMounted} from "vue"; import component from './component.vue';
onMounted(() => { const map = L.map('map').setView([31.491064, 120.311889], 13);
const baseLayer = L.tileLayer("http://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}", { subdomains: "1234" }); map.addLayer(baseLayer);
const marker = L.marker([31.491064, 120.311889]).addTo(map);
const MyVueElement = defineCustomElement(component); if(!customElements.get('my-vue-element')) customElements.define('my-vue-element', MyVueElement)
marker.bindPopup(`<my-vue-element j-id="666" style="width: 200px;height: 100px;"></my-vue-element>`)
marker.openPopup(); })
</script>
|
component.vue
文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <template> <div> <span>接受传进来的值为:</span> <span>{{ props["jId"] }}</span> </div> <div>这种方法最推荐</div> </template>
<script setup> const props = defineProps(['jId']); </script>
<style scoped>
</style>
|
方法5:使用jsx
实在写不动了🤣,jsx使用也是挺方便的,大家可以自己试试。
本文首发于blog.gis1024.com