地图弹窗中使用vue组件的多种方法

一个好习惯,先给结论

有5种方法:

  1. iframe嵌套,这种最简单
  2. vue模板字符串创建vue实例
  3. 使用.vue单文件创建vue实例
  4. 使用vue3新方法defineCustomElement,这种最推荐
  5. 使用jsx

本文首发于blog.gis1024.com

线上预览地址→点我

完整代码库→ github跳转

这里才是引言

在gis项目开发中,经常遇到需要在地图弹窗中显示信息的需求。几乎所有地图框架的弹窗api,都是用html字符串的形式填写内容。

以百度地图为例,可以看到BMapGL.InfoWindow后跟的是一个字符串。

	// 百度地图API功能
	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的组合演示几种方法,其他地图如百度地图高德地图mapboxopenlayersleafletmaptalks等地图框架和vue2的组合同理。

方法1:iframe嵌套

  1. 将弹窗中的内容写成一个.vue组件,并且在路由中将这个组件指定为一个路由
    router.js文件如下
    ...其他代码省略
    {
        path: '/1',
        component: () => import('../views/1_iframe/index.vue')
    },
    {
        path: '/1_inner',
        component: () => import('../views/1_iframe/iframe.vue')
    }
    ...其他代码省略
  1. 地图主文件views/1_iframe/index.vue文件如下:
<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);

  // 创建poi点标记
  const marker = L.marker([31.491064, 120.311889]).addTo(map);
  // 通过iframe将另一个组件引用进来,可以通过路由传递参数
  marker.bindPopup(`<iframe style="border: 0;height: 100px;width: 200px;" src="/#/1_inner?id=666"></iframe>`).openPopup();
})

</script>
  1. 弹窗页面/views/1_iframe/iframe.vue如下
<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实例

<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);

  // 创建poi点标记
  const marker = L.marker([31.491064, 120.311889]).addTo(map);

  /* -----------
    该方法需要在vite.config.js中配置如下参数,是为了改变引入的vue运行时
     alias: {
      'vue': 'vue/dist/vue.esm-bundler.js' // 使用模板字符串时需要设置
    }
  ----------------------*/
    
  // 需要指定id,vue才能找到对应渲染节点
  marker.bindPopup(`<div id="my_popup" style="width: 200px;height: 100px;"></div>`)

  // 通过vue的模板字符串创建并渲染到#my_popup节点上
  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')
  })

  // 弹窗关闭时,一定需要销毁vue实例!非常重要!
  marker.on('popupclose', e => {
    vm.unmount();
  })

  // 自动打开marker,方便观察
  marker.openPopup();
})

</script>

方法3:使用.vue单文件创建vue实例

这种方法和方法2本质上一样,不过是将写的一堆模板提取到单文件中了
index.vue文件如下:

<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);

  // 创建poi点标记
  const marker = L.marker([31.491064, 120.311889]).addTo(map);

  // 需要指定id,vue才能找到对应渲染节点
  marker.bindPopup(`<div id="my_popup" style="width: 200px;height: 100px;"></div>`)

  // 使用引入的sfc文件创建vue实例
  const id = 666;
  let vm;
  marker.on('popupopen', e => {
    vm = createApp(component, {id: id})
    vm.mount('#my_popup')
  })

  // 弹窗关闭时,一定需要销毁vue实例!非常重要!
  marker.on('popupclose', e => {
    vm.unmount();
  })

  // 自动打开marker,方便观察
  marker.openPopup();
})

</script>

component.vue文件如下:

<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文件如下

<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);

  // 创建poi点标记
  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,方便观察
  marker.openPopup();
})

</script>

component.vue文件如下

<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

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值