在用 elementUI、antdUI 等 ui 框架的时候,都会用到 Message 全局的提示方法,而平时常用的 Vue 组件都是写成 tag 来使用,而 Message 等组件的使用方式则不同,不用引入 Message 的 vue 组件,用 js 就能
接下来将上面组件改写成用 js 调用的方式:
在改写之前,还需要了解 vue3 的几个全局 API:
- defineComponent: 将函数或者对象转换成对象返回
- h: 将组件对象转换成 vnode
- render:将 vnode 挂载到节点或者移除节点
另建一个 ts / js 文件,然后将 TestComp 组件进行转换:
首先实现一个 vnode 挂载节点函数和一个移除节点函数:
import { defineComponent, h, render } from 'vue'
// 生成一个唯一的key
const COMPONENT_CONTAINER_SYMBOL = Symbol('component_container')
/**
* 创建组件实例对象
* 返回的实例和调用 getCurrentComponent() 返回的一致
* @param {*} Component
*/
function createComponent(Component: any, props: any, children: any) {
// 创建vnode
const vnode = h(Component, { ...props }, children)
// 创建组件容器
const container = document.createElement('div')
// @ts-ignore 将组件容器挂载到vnode上,方便后续移除
vnode[COMPONENT_CONTAINER_SYMBOL] = container
// 将vnode渲染到组件容器内, 在 vue2 的版本中,父级元素是可以传 null 的,但是 vue3 不支持
render(vnode, container)
// 返回组件实例
return vnode.component
}
/**
* 销毁组件实例对象
* @param {*} ComponnetInstance 通过createComponent方法得到的组件实例对象
*/
export function unmountComponent(ComponnetInstance: any) {
// 移除组件节点,render函数的第一个传null,表示为移除动作,会执行unmount方法
render(null, ComponnetInstance.vnode[COMPONENT_CONTAINER_SYMBOL])
}
引入组件进行挂载节点
import TestComp from './testComp.vue'
// ......
// ......
// 当前场景下是可以省略这一步转换,但是,就类型而言,defineComponent 返回的值有一个合成类型的构造函数
const componentConstructor = defineComponent(TestComp)
// 创建一个变量接收创建的组件实例
let instance: any;
// 创建节点
const showTestComponent = (options: any) => {
// 创建组件实例对象
instance = createComponent(componentConstructor, options, null)
// 添加到body
document.body.appendChild(instance.vnode.el)
}
// options为组件的props
export const testComp = function (options: any) {
const close = options.onClose
// 重新封装close,添加移除元素操作
options.onClose = () => {
close && close.call()
unmountComponent(instance)
}
showTestComponent(options)
}
最后在父组件调用
<template>
<div>js 调用 vue 组件</div>
<button @click="show">调用测试组件</button>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { testComp } from '../components/testComp'
export default defineComponent({
setup() {
const show = () => {
testComp({
title: '这是传进来的title',
onClose() {
console.log('close')
}
})
}
return {
show
}
},
})
</script>