Vue2.x将vue文件编写的组件使用API方式动态创建(vue.extend)
程序员文章站
2022-03-11 21:40:31
...
文章参考
- Vue.extend 看完这篇,你就学废了
- vue2.x挂载$mount、Vue.extend()函数的作用——由elementUI message方法联想
- Vue2.x 如何将vue文件编写的组件变为注册到全局组件?Vue.extend()
问题描述
- 在工作中使用的是Vue 脚手架搭建的,现在需要在地图上弹出一个pop 弹窗,问题是这个弹窗里面的内容全部要自己通过html写出来,因此官方给出的代码是自己写一个html字符串
let divStr = "<br/><div οnclick="myfunc()">我是弹窗显示的内容</div>",
let ele = document.createElement("div");
ele.innerHTML = divStr;
- 如果按照上面的写法,就需要
定义全局方法,污染全局,代码可读性降低不利于维护
- CSS 也需要定义全局的,不符合vue 将方法和css 放到一个组件的设计原则
解决办法
解决思路
- 能否用vue文件编写好组件代码,js逻辑和css全部写到vue文件中,然后创建vue组件的实例,将vue.$el 赋值给创建的div中?
- 如果能以这样的方式实现,就可以让js的方法作用范围在组件内部,在组件定义的css也只作用于组件,不影响全局
解决方式 Vue.extend() API
Vue.extend返回的是一个扩展实例构造器,也就是预设了部分选项的Vue实例构造器,但未曾实例化,可以理解为创建一个子类,然后让它继承Vue身上的一些功能
1. 定义好面板vue组件,将css 、js 、html 集成到一个文件中
<template>
<div class="MapRoadPanel">
<div class="MapRoadPanel--title">
<span>{{markerObj.roadName}}</span>
<span class="close h-icon-close" @click="closePanelAction"></span>
</div>
<div class="MapRoadPanel--content">
<div class="MapRoadPanel--content__item">
<div class="label">道路代码</div>
<div class="value">{{markerObj.roadCode}} | {{markerObj.roadName}}</div>
</div>
<div class="MapRoadPanel--content__item">
<div class="label">路段代码</div>
<div class="value">{{markerObj.roadSectionCode}} | {{markerObj.roadSectionName}}</div>
</div>
<div class="MapRoadPanel--content__item">
<div class="label">违禁图片</div>
<div class="value">
<img :src="markerObj.logoImageUrl">
</div>
</div>
</div>
<div class="MapRoadPanel--footer">
<el-button type="primary" class="btn" @click="closePanelAction">确定</el-button>
</div>
</div>
</template>
<script>
export default {
data () {
return {
markerObj: {
// roadName: '长沙市南二环',
// roadCode: '51000',
// logoImageUrl: 'https://baidu.com/dB9I=',
// createTime: '2021-01-29T17:11:31.849',
// roadSectionName: '南天门',
// roadSectionCode: '2102',
// gpsLocation: '112.945129,28.161757',
// dateTime: '2021-01-29 17:11:31',
// id: '202101291711310003'
}
}
},
mounted () {},
methods: {
closePanelAction () {
alert('closePanelAction')
}
}
}
</script>
<style lang="scss" scoped>
.MapRoadPanel {
width: 336px;
height: 422px;
background: #fff;
padding: 0 12px;
position: relative;
&--title {
line-height: 40px;
font-size: 12px;
color: #4d4d4d;
letter-spacing: 0;
border-bottom: 1px solid #efefef;
.close {
font-size: 24px;
position: relative;
top: 8px;
float: right;
cursor: pointer;
}
}
&--content {
&__item {
padding-top: 12px;
.label {
font-size: 12px;
color: rgba(0, 0, 0, 0.4);
letter-spacing: 0;
line-height: 20px;
}
.value {
font-size: 14px;
color: rgba(0, 0, 0, 0.7);
letter-spacing: 0;
line-height: 26px;
img {
width: 100%;
height: auto;
max-height: 160px;
}
}
}
}
&--footer {
height: 59px;
background-color: rgba(0, 0, 0, 0.04);
position: absolute;
bottom: 0;
left: 0;
right: 0;
.btn {
float: right;
position: relative;
top: 12px;
right: 12px;
background: #2080f7;
width: 96px;
height: 32px;
color: #fff;
line-height: 32px;
text-align: center;
cursor: pointer;
}
}
}
</style>
2. 目标组件,引用子组件,覆盖默认组件的data 和 方法
<script>
import Vue from 'vue'
import MapRoadPanel from '@/components/illegalMapConfig/MapRoadPanel'
import locationImg from '@/assets/images/icons/location.png'
import bigLocationImg from '@/assets/images/icons/bigLocation.png'
import { ajaxCtx } from '@/api/config.js'
export default {
provide: function () {
return {
mapContext: this // 把当前Vue对象提供给所有子组件可以访问
}
},
data () {
// 根据定义好的视图
const MyMapRoadPanel = Vue.extend(MapRoadPanel)
return {
MyMapRoadPanel, // 自定义弹窗层的组件
locationImg,
bigLocationImg,
selectRoadObj: null, // 用户选中的road对象
}
},
components: {
selectConfigPanel,
addConfigPanel
},
watch: {
// 监听用户是否有选择道路
selectRoadObj (newObj) {
// 重新渲染图标
this.addMarker()
// 如果有值,则弹出详情,没有则隐藏
if (newObj) {
this.showSimplePopup()
}
}
},
mounted () {
this.initMap()
},
methods: {
// 返回组件的HTML
getPanelCompHtml (markerObj, removePop) {
const that = this
const onePanel = new this.MyMapRoadPanel({
data: {
markerObj
},
methods: {
closePanelAction: function () {
that.selectRoadObj = null
}
}
})
// $mound() 方法执行之后,才会生成 $el,即创建dom 节点,视图和方法关联
onePanel.$mount()
return onePanel.$el
},
// 显示弹出层
showSimplePopup (pointArr) {
const ele = document.createElement('div')
const panelDom = this.getPanelCompHtml(this.selectRoadObj, removePop)
ele.appendChild(panelDom)
}
}
</script>
注意:
- 自定义组件没有注册到当前组件中,因此 自定义的组件 this ,代表的是自定义组件,而不是业务逻辑中的组件
- 自定义组件没有注册到当前组件中,因此 this.$parent 为 undefined