Vue3正式发布也有一段时间了,关于Vue3的文章也是有一大把,组合式API、Typescript、源码等都介绍的非常详尽,但是关于全局api组件这块好像介绍的比较少,那么接下来就分享一下Vue3中如何开发以及使用一个全局api组件
让我们先来回顾一下vue2中的实现方式
以一个alert组件为例
<template>
<transition name="alert-fade" @after-leave="destroyElement">
<div class="alert" v-if="show">
<div class="ht-modal" @click="close"></div>
<div class="alert-box" :style="'width:'+ width">
<div class="alert-header">
<i class="iconfont icon-error alert-close" @click="close"></i>
</div>
<div class="alert-body">
{{ message }}
</div>
<div class="alert-footer">
<slot name="footer">
<div class="alert-btn" @click="close">确定</div>
</slot>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
name: 'Alert',
props: {
width: {
type: String,
required: false,
default: '30%',
},
msg: {
type: String,
required: false,
default: () => 'some message'
},
handleClose: {
type: Function,
required: false,
default: null
}
},
data () {
return {
show: true,
message: this.msg
}
},
methods: {
close($event) {
this.show = false
this.$emit('close', $event)
},
destroyElement() {
this.handleClose && this.handleClose()
}
},
}
</script>
文件命名为alert.vue,在同目录下创建alert.js文件
import Main from './alert.vue'
const Alert = {}
Alert.install = Vue => {
let instance
let show = false
let AlertConstructor = Vue.extend(Main)
const loadAlert = options => {
if ( show ) return
let { handleClose } = options
options.handleClose = () => {
handleClose && handleClose()
show = false
}
instance = new AlertConstructor({
propsData: options
})
const component = instance.$mount()
document.body.appendChild(component.$el)
}
Vue.prototype.$alert = loadAlert
}
export default Alert
此处的关键是调用Vue上的extend方法来继承alert组件属性,当然也可以直接创建一个全新的Vue示例来实现同样的效果,因为Vue2中是通过实例化Vue构造函数的方式来创建示例,所以把api方法绑定到构造函数的原型对象上即可实现继承
import Alert from 'alert.js'
import Vue from 'vue'
Vue.use(Alert)
new Vue(...).$mount(el)
完成注册后,我们便可在任意位置通过this来调用此方法来渲染组件
this.$alert({
width: '50%',
msg: '这是一个全局api组件',
handleClose: function() {
console.log('我被关闭了!!!')
}
})
以上就是对Vue2中如何开发及使用一个api组件的简单回顾,那么Vue3中的实现会有什么区别,我们继续往下
Vue3中通过createApp方法返回实例对象,也取消了extend方法,上面我们有提到可以创建一个全新的实例对象的方式来实现,那么我们依葫芦画瓢来实现一下
import { createApp } from 'vue'
import Main from './alert.vue'
const Alert = {}
Alert.install = app => {
let instance
let container = null
const loadAlert = options => {
if (container) return
let { handleClose } = options
options.handleClose = () => {
handleClose && handleClose()
document.body.removeChild(container)
instance.unmount()
container = null
}
container = document.createElement('div')
instance = createApp(Main, options)
instance.mount(container)
document.body.appendChild(container)
}
app.config.globalProperties.$alert = loadAlert
}
export default Alert
跟2的实现有几处不太相同,首先实例化操作通过createApp来实现,其次实例上不再有$mount方法,这让我们挂载dom的操作变的复杂了些,但总体的思路是一致的,最后添加方法的方式也有所不同,2是直接添加到原型对象上,3则是添加到实例应用的config对象的全局属性上,后面注册以及使用方式则完全一致
接下来再继续深入一下,Vue3最重要的一个特性就是组合式api,让我们能够非常灵活的去撰写代码,那么其实我们使用vue组件最终要的原因就是要利用其响应式渲染dom的能力来便携和高效的完成渲染操作,那么与之相关的api主要有createVnode、h以及render,我们也可用通过对这些api的使用来实现同样的效果
import { h, createVNode, render } from 'vue'
h与createVnode方法都返回一个vnode对象,两者效果相同,也可以混用
const vnode = h(Main, options)
const vnode = createApp(Main, options)
最后我们需要调用render方法传入vnode以及要挂载的父元素
render(vnode, container)
最后我们的代码可以改成
import { h, createVNode, render } from 'vue'
import Main from './alert.vue'
const Alert = {}
Alert.install = app => {
let container = null
const loadAlert = options => {
if (container) return
let { handleClose } = options
options.handleClose = () => {
handleClose && handleClose()
document.body.removeChild(container)
container = null
}
container = document.createElement('div')
const vnode = h(Main, options)
//const vnode = createVNode(Main, options) //也可以使用createVnode方法替换h
render(vnode,container)
document.body.appendChild(container)
}
app.config.globalProperties.$alert = loadAlert
}
export default Alert
其实不难发现,我们就是把createApp的行为变成了创建vnode,然后调用render方法挂载到container上,那么createApp内部也是执行了相同的操作,我们只是把其中我们需要执行的方法提取了出来,这也是组合式api的优势之一,我需要什么就引入对应api,避免代码冗余
最后我们来分析一下2和3的全局调用方式实现,2中通过原型链继承的方式,通过在Vue构造函数的的原型对象上暴露api方法,在应用单例上,供全局调用;3则是通过Proxy代理的方式改写读取行为,this.$alert访问到的实际上是app.config.globalProperties.$alert方法