# 实现一个全局挂载插件
一般,我们在写公共组件时都是使用 component 进行局部或者全局注册,这种写法比较固定,如果想通过 Vue 实例直接调用组件,比如,全局消息弹窗、加载框等直接通过$api 调用,就需要把组件挂载到 Vue 实例,我们可以通过插件+Vue.extend()方法来实现,并且通过此方法可以直接把元素挂载到对应的 DOM 元素上。
# 1、插件使用
创建一个插件,我们只需要提供一个 install 方法,该方法接收两个参数,一个是 Vue 构造器,第二个是插件的参数,我们可以通过这两个参数在 Vue 实例上添加全局方法、属性、组件、指令、实例方法。然后通过 Vue.use(MyPlugin)使用,我们定义的全局属性就挂载到 Vue 实例上了。
除了通过插件挂载,我们也可以直接在 main.js 中直接挂载到 Vue 的原型属性上也行。
// plugin.js
const myPlugin = {
install = function (Vue, options) {
// 注册全局组件
Vue.component('xxx', xxx);
// 添加全局方法或 property
Vue.name = 'xxx'
Vue.myGlobalMethod = function () {
// 逻辑...
}
// 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
// 逻辑...
}
}
}
export default MyPlugin
# 2、Vue.extend()方法
Vue.extend()方法使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象,可以动态的去修改组件中的值,不像 component 就直接把组件目标定义死了。并且可以通过它把组件挂载到其他 DOM 元素上。
// 创建构造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')
# 3、创建一个全局 toast 弹窗
创建一个 toast 弹窗模板,通过传入 props 参数来显示内容,弹窗显示后会自动关闭。关闭前把当前 DOM 移除掉。
// toast.vue
<template>
<transition name="fade" @after-leave="handleAfterLeave">
<div class="component-toast-cls" v-show="visible" :style="{ zIndex }">{{ content }}</div>
</transition>
</template>
<script>
export default {
name: 'Toast',
props: {
content: {
type: String
},
zIndex: {
type: [Number, String],
default: 999
},
duration: {
type: Number,
default: 3000
}
},
data() {
return {
visible: false,
timer: null
}
},
mounted() {
this.visible = true
this.startTimer()
},
methods: {
startTimer() {
this.timer = setTimeout(() => {
this.visible = false
clearTimeout(this.timer)
this.timer = null
}, this.duration)
}, // 清除DOM
handleAfterLeave() {
this.$destroy(true)
this.$el.parentNode.removeChild(this.$el)
}
},
beforeDestroy() {
if (this.timer) clearTimeout(this.timer)
}
}
</script>
<style lang="scss" scoped>
.component-toast-cls {
position: fixed;
top: 50%;
left: 50%;
font-size: 14px;
color: #fff;
line-height: 24px;
padding: 8px 20px;
background-color: rgba(0, 0, 0, .8);
border-radius: 4px;
transform: translateX(-50%) translateY(-50%);
}
// 添加动画
.fade-enter-active, .fade-leave-active {
transition: all .3s ease;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
</style>
自定义一个插件,创建一个全局$Toast方法挂载到Vue上,然后引入我们创建的toast模板,通过Vue.extend()方法构建一个组件,把$Toast 方法接收的参数传入来动态替换 toast 模板 props 的值,在传入之前你可以修改 options 的值来实现自己的逻辑。
// 自定义一个插件
import Vue from 'vue'
// 引入模板
import toast from './toast.vue'
const MyPlugin = {
// 提供install方法,传入Vue构造器
install(Vue) {
// 挂载全局属性
// 注册全局组件
Vue.component('Toast', toast) // 挂载全局方法
Vue.prototype.$Toast = function (options) {
// 服务端渲染退出
if (Vue.prototype.$isServer) return
const Toast = Vue.extend(toast) // 构造函数可以接传值,会替换模板中data、props、methods相同的值
const ToastInstance = new Toast({
// data: options 替换目标data数据
propsData: options // 替换props传参
}) // $mount()不带参数,会把组件在内存中渲染完毕
const vm = ToastInstance.$mount() // 挂载到body上
document.body.appendChild(ToastInstance.$el) // 可以通过ToastInstance.$el获取模板DOM元素
console.log('插件注册成功', options) // 返回一个主动关闭的方法,在调用后通过一个变量保存来执行
// 渲染后的实例vm
const close = () => {
vm.visible = false
}
return close
}
}
}
export default MyPlugin
在 main.js 中引入该插件
// main.js
import MyPlugin from '@/components/MyPlugin'
Vue.use(MyPlugin)
最后,就可以在 vue 中使用了
this.$Toast({
content: '弹窗内容'
})
// 手动关闭
const closeCallback = this.$Toast({
content: '弹窗内容',
duration: 10000
})
setTimeout(() => {
closeCallback()
}, 2000)