弹窗类组件的特点
弹窗类组件的特点就是他们在当前的vue实例之外独立存在,通常挂载于body。他们是通过纯JS创建的,不需要在任何组件中声明。常见的食用方式是这样:
this.$create(Notice, {
title: '标题标题',
message: '提示信息'
}).show();
分析实现思路
根据上面的实用方式,我们要想实现在vue组件中全局使用this.$create()方法,就需要将该方法赋值到vue的原型对象上。假设我们已经实现了create方法:
Vue.prototype.$create = create
分析下create函数,它接受两个参数,一个是组件配置对象,命名为Component,一个是参数属性,我们将他命名为props。要想将该组件配置对象挂载到body上,就需要构建Component实例,这样才能生成真实的dom节点。顺着这个思路,拿到真实的dom元素后,将该元素append到body上。再给其添加个remove方法,在移除的时候删除节点销毁自身,就大功告成了。
实现方式
借鸡生蛋式——借助Vue构造函数
在vue项目的main.js中,我们要想在body上挂载app组件,是这样写的:
new Vue({
render: h => h(App)
}).$mount('#app')
这里的h就是createElement,它可以返回一个vnode即虚拟dom。$mount方法则可以将render函数中返回的vnode转化为真实节点,并挂载到目标元素上。需要注意一点: $mount()是覆盖性操作。
所以我们在实现create时,不能够直接使用$mount(body),将真实节点挂载到body上。但是可以不设置挂载目标,这样依然可以转换vnode为真实节点。
所以我们也可以借助这种方式:
import Vue from 'vue'
export default function create (Component, props) {
const vm = new Vue({
render: (h) => {
return h(Component, {props})
}
}).$mount()
document.body.appendChild(vm.$el)
const comp = vm.$children[0]
comp.remove = () => {
document.body.remove(vm.$el)
vm.$destroy()
}
return comp
}
这样我们就是实现了create方法。我们写个例子来验证它:
// Notice组件
<template>
<div v-if="isShow" class="modal">
<div class="title">{{ title }}</div>
<div class="content">{{ content }}</div>
</div>
</template>
<script>
export default {
name: 'Notice',
props: {
title: {
type: String,
default: '警告'
},
content: {
type: String,
default: '我是内容我是内容'
}
},
data () {
return {
timerId: null,
isShow: false
}
},
methods: {
show () {
this.isShow = true
setTimeout(() => {
this.hide()
}, 1500)
},
hide () {
this.isShow = false
}
}
}
</script>
<style scoped>
.modal{
position: relative;
top: 50px;
left: 50%;
margin-left: -250px;
width: 500px;
border: 1px solid #f7f7f7;
border-radius: 6px;
padding: 10px;
background-color: papayawhip;
animation: disappear 2s ease-in-out;
}
.title{
text-align: left;
font-weight: 500;
font-size: 14px;
}
.content {
margin-bottom: 10px;
font-size: 12px;
}
@keyframes disappear {
0% {
top: 60px;
opacity: 1;
}
30% {
top: 10px;
opacity: 1;
}
96% {
opacity: 0.20;
}
100% {
top: 10px;
opacity: 0;
}
}
</style>
// index页面
<template>
<div>
<button @click="showNotice">提示弹窗</button>
</div>
</template>
<script>
import Notice from './Source/NoticeModal/Notice.vue'
export default {
name: 'Source',
methods: {
showNotice () {
const notice = this.$create(Notice, {
title: '测试标题',
content: '我是内容内容呀呀呀'
})
notice.show()
}
}
}
</script>
效果:
来对比看下dom的渲染:
Vue.extends()方法
Vue.extends()方法可以使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。extend 创建的是 Vue 构造器,而不是我们平时常写的组件实例。
export default function create (Component, props) {
const Ctor = Vue.extend(Component)
const vm = new Ctor({propsData: props}).$mount()
document.body.appendChild(vm.$el)
vm.remove = () => {
document.body.remove(vm.$el)
vm.$destroy()
}
return vm
}
至此,一个纯js调用的弹窗类组件就完成了。参考文档:vuejs.org。
? ? ? ? ? ?
关注我,每日都有新收获!
|