一、定义插件的目的是为了使用函数式调用某个全局组件,方便直接在js中调用,
不需要在任何组件中声明,大概是这样的效果
this.$toast({
title: '我是toast',
closeButton: {
text: '知道了',
callback () {
console.log('用户说他知道了');
}
}
})
二、实现过程:
1.准备好需要的组件
<template>
<div>
<div>{{title}}</div>
<!-- <slot></slot> -->
</div>
</template>
<script>
export default {
data() {
return {};
},
props: {
title: {
type: String,
default: ''
},
closeButton: {
type: Object,
default () {
return {
text: '关闭',
callback: undefined
}
}
}
},
methods: {
close() {
// 删除组件方法,将真实的dom节点删掉
this.$el.remove();
// 将虚拟节点占的内存也释放掉
this.$destroy();
},
onClickClose () {
this.close();
if (this.closeButton && typeof this.closeButton.callback === 'function') {
this.closeButton.callback(this) // this === toast实例
}
}
},
};
</script>
这里:如果我们想在js调用组件的时候,拿到组件实例,并调用组件里的方法,可以把this传出去, this === toast实例,然后在调用的地方这样使用:
this.$toast({
title: '我是toast',
closeButton: {
text: '知道了',
callback (toast) {
toast.log() //调用组件内部方法
console.log('用户说他知道了');
}
}
})
- 将组件实例化,并转化成真实dom
这里:Vue.extend()其实就是用来创建组件的构造函数的,然后使用这个构造函数创建出Vue的虚拟节点Vnode
import Toast from './demoPage'
export default {
install(Vue, options) {
Vue.prototype.$toast = function (options) {
// 1.使用Vue.extend创建构造函数,MyComponent是自定义的vue组件(MyComponent.vue)
let Constructor = Vue.exend(Toast)
// 构造函数的参数,propsData相当于我们组件MyComponent.vue里需要的props,
// 这里为了和vue文件中的props冲突,所以官方取了个别名
// 2.然后实例化后,会生成一个vue组件对应的虚拟节点
let toast = new Constructor({
propsData: options
})
// toast.$slot.default = [options.message]
// 3.将vm转化成真实dom
toast.$mount();
// 4.将真实dom,挂载到body上
document.body.appendChild(toast.$el)
}
}
}
进一步可以封装成:
import Toast from './demoPage';
function createToast({ Vue, propsData }) {
// 1.使用Vue.extend创建构造函数,MyComponent是自定义的vue组件(MyComponent.vue)
let Constructor = Vue.exend(Toast)
// 构造函数的参数,propsData相当于我们组件MyComponent.vue里需要的props,
// 这里为了和vue文件中的props冲突,所以官方取了个别名
// 2.然后实例化后,会生成一个vue组件对应的虚拟节点
let toast = new Constructor({
propsData
})
// toast.$slot.default = [propsData.message]
// 3.将vm转化成真实dom
toast.$mount();
// 4.将真实dom,挂载到body上
document.body.appendChild(toast.$el)
}
export default {
install(Vue, options) {
Vue.prototype.$toast = funtion(options) {
createToast({ Vue, propsData: options })
}
}
}
优化,避免创建多次:
mport Toast from './demoPage';
function createToast({ Vue, propsData }) {
// 1.使用Vue.extend创建构造函数,MyComponent是自定义的vue组件(MyComponent.vue)
let Constructor = Vue.exend(Toast)
// 构造函数的参数,propsData相当于我们组件MyComponent.vue里需要的props,
// 这里为了和vue文件中的props冲突,所以官方取了个别名
// 2.然后实例化后,会生成一个vue组件对应的虚拟节点
let toast = new Constructor({
propsData
})
// toast.$slot.default = [propsData.message]
// 3.将vm转化成真实dom
toast.$mount();
// 4.将真实dom,挂载到body上
document.body.appendChild(toast.$el)
}
let currentToast;
export default {
install(Vue, options) {
Vue.prototype.$toast = funtion(options) {
if (currentToast) {
currentToast.close()
}
currentToast = createToast({ Vue, propsData: options })
}
}
}
继续优化:
methods: {
close() {
// 删除组件方法,将真实的dom节点删掉
this.$el.remove();
this.$emit('close')
// 将虚拟节点占的内存也释放掉
this.$destroy();
}
},
import Toast from './demoPage';
function createToast({ Vue, propsData, onClose }) {
// 1.使用Vue.extend创建构造函数,MyComponent是自定义的vue组件(MyComponent.vue)
let Constructor = Vue.exend(Toast)
// 构造函数的参数,propsData相当于我们组件MyComponent.vue里需要的props,
// 这里为了和vue文件中的props冲突,所以官方取了个别名
// 2.然后实例化后,会生成一个vue组件对应的虚拟节点
let toast = new Constructor({
propsData
})
// toast.$slot.default = [propsData.message]
// 3.将vm转化成真实dom
toast.$mount();
toast.$on('close', onClose)
// 4.将真实dom,挂载到body上
document.body.appendChild(toast.$el)
}
let currentToast
export default {
install(Vue, options) {
Vue.prototype.$toast = funtion(options) {
if (currentToast) {
currentToast.close()
}
currentToast = createToast({
Vue, propsData: options, onClose: () => {
currentToast = null
}
})
}
}
}
3.在入口文件main.js中调用插件:
// 导入插件
import noticePlugin from '@/notice/index'
// 使用插件
Vue.use(noticePlugin)