背景
在做GIS地图功能时候有一个需求,每个点的popup中展示数据内容,一般情况下以拼字符串的形式往popup中拼HTML标签实现数据内容的展示,但是这样太麻烦也不容易维护。就想着能不能实现让popup中弹出一个组件的内容?经过一番对vue子组件加载渲染过程的研究和查阅leaflet中popup的源码终于实现了在Popup中放入vue组件的功能。源码github地址放在最后。
功能逻辑
首先。我们在查看leaflet的bindpopup的源码(因为实现popup功能主要依赖于.bindpopup()),之前先看看官方文档怎么描述这个方法。
bindPopup(<String|HTMLElement|Function|Popup> content, options?)
可以看出,bingPopup主要分为两个参数,分别是content和options,顾名思义,分别是给popup设定内容和一些参数。
从需求上说,让popup可以展示vue组件内容,主要需要思考的在于content这个参数上,所以,关注的重点应该是content的这个参数上。
这个content可以接受String,HTMLElement,Function和Popup四种类型的传入值。HTMLElement就是为什么它可以用拼字符串拼HTML元素方法展示数据。这就为实现加载VUE组件提供了契机(VUE组件最终渲染出来也是一个HTML元素嘛,具体组件加载渲染流程就不叙述了,网上一搜有很多的相关的知识)。
popup源码
L.Layer.include({
// @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
// Binds a popup to the layer with the passed `content` and sets up the
// neccessary event listeners. If a `Function` is passed it will receive
// the layer as the first argument and should return a `String` or `HTMLElement`.
bindPopup: function (content, options) {
if (content instanceof L.Popup) {
L.setOptions(content, options);
this._popup = content;
content._source = this;
} else {
if (!this._popup || options) {
this._popup = new L.Popup(options, this);
}
this._popup.setContent(content);
}
if (!this._popupHandlersAdded) {
this.on({
click: this._openPopup,
remove: this.closePopup,
move: this._movePopup
});
this._popupHandlersAdded = true;
}
return this;
},
源码就很简单明了。首先
if (content instanceof L.Popup) {
L.setOptions(content, options);
this._popup = content;
content._source = this;
}else {
if (!this._popup || options) {
this._popup = new L.Popup(options, this);
}
这里主要考虑的是传入popup类型的content时使用走的逻辑,从需求上只会走到else中的逻辑,
因为我们传的是组件,所以if后的第一个代码块可以忽略不看。只看else代码块中的逻辑,这里的逻辑也很简单,说白了就是说当你传入的content不是一个popup类型时候给你new一个popup并设定popup的参数,而这个this则是你的“宿主”。举个列子如果你是个marker加popup,则这个this就是你的marker。
接下来
this._popup.setContent(content);
}
这里才是涉及到content相关的代码,
setContent(<String|HTMLElement|Function> htmlContent)
这个方法可以接收HTMLElment的参数。所以,到此我们的思路就很清晰了,即在执行到this._popup.setContent()这个方法的时候。我们给他传入渲染好的vue组件的el就行。
手动渲染组件
一般我们使用子组件方法是import xxx from ‘’./xxxx.vue’然后components:{xxx}最后写入
我开始设想的是传入一个字符串然后通过require.context对目录扫描获取到该组件。这样确实也可以,但是我后来发现。。。可以直接地图页面import xxx from ‘’./xxxx.vue’然后components:{xxx},然后把xxx直接传进去进行组件的解析。所以这里采用了后者这种方法。通过打印大概是这么个东西。
总之,通过import组件这种方法可以把组件传递到我们自定义的popup里面。然后就是对这个组件进行解析渲染。
//调用vue,component方法进行组件的创建第一个参数是组件名字,第二个则是我们要渲染的组件
let popComponent = Vue.component(`POPUP-${
marker._leaflet_id}`,targetCopm)
//然后新建一个组件实例,并挂载
let mypop = new popComponent({
propsData:myProps}).$mount()
//最后拿到这个组件的el任务就完成了
popEl =mypop.$el
这个propsData:这个参数则是实现组件传值的功能。
最后我们将组件的el传入setContent中,即可完成popup展示组件内容功能。
功能代码
所以我们定义的这个方法内容应该是这样的,逻辑上说只关心的是传入的内容,而具体的怎么创建popup以及他的参数怎么设置都不是我们关心的,所以我们这个功能的逻辑就是在给setContent传入展示内容之前,对要展示的子组件进行创建和渲染得到该组件的html元素el,最后把el传给setContent。
L.Marker.include({
popupPlus:function(targetCopm,options){
return new popupplus(targetCopm,options,this