vue3.0 动态创建组件
缘起
有时候, 我们希望组件在我们任何需要的时候呼之即来,而不是固定在HTML的DOM中.
在 element-plus中 是事先通过 el-dialog 定义 然后 控制起是否可见来决定对话框的弹出还是隐藏。
这在涉及复杂逻辑可能会不太方便。
设想中的写法:
import LocMarkDialog from 'LocMarkDialog.vue'
///省略代码 ....
let {proxy} = getCurrentInstance();
proxy.$mount(LocMarkDialog).then(res)=>{
//点击了确定
}).catch(()=>{
//点击了取消
}
MyDialog 组件并不写在<template></template> 标签中,而是在任何业务需要的地方临时创建,并且在使用完后自动销毁.
组件的实现代码:
import { markRaw, ref, createVNode, defineComponent} from 'vue'
let componentMap = ref([])
let id=0;
const factory = defineComponent({
props:{
},
render(){
var children = componentMap.value.map((item, i)=>createVNode(item.c, item.p))
return createVNode('div', {class:'ui-app-component-container'}, children);
},
setup(props)
{
return {
componentMap
}
},
install(app){
app.component('CompFactory', factory);
app.config.globalProperties.$mount = (comp, props) => {
return new Promise((resolve, reject)=>{
let p = props || {};
Object.assign(p, {q:{resolve, reject}, _compId: id++, finish:(id)=>{
let ret = componentMap.value.findIndex(v=>v.p.id===id)
if(ret >=0)
componentMap.value.splice(ret, 1);
}});
console.log('mount component.', comp);
componentMap.value.push({c:markRaw(comp), p});
})
}
}
})
export default factory;
用法
第一步: 首先全局引入组件
// 引入前面写的组件
import CompFactory from '/@/uiframe/layout/components/CompFactory.vue'
const app = createApp(App);
app.use(CompFactory)
第二步: 在全局使用一次组件
这么做的目的是在document 中创建一个容器节点.
App.vue
<CompFactory></CompFactory>
第三步: 如何使用
定义一个对话框组件
<template>
<el-dialog custom-class="ui-loc-mark-dialog ui-dialog-autow" v-model="dialogVisible" destroy-on-close :modal="true" @closed="finalThings" title="地理位置标注">
<BaiduMap :location="location" width="100%" height="100%"></BaiduMap>
<template #footer>
<el-button type="primary" size="mini" @click="onSure">保存</el-button>
<el-button size="mini" @click="onCancel">取消</el-button>
</template>
</el-dialog>
</template>
<script>
import { ref } from '@vue/reactivity';
import BaiduMap from '/@/uiframe/layout/components/BaiduMap.vue'
import { onUnmounted } from '@vue/runtime-core';
import LocMarkDialog from '/@/uiframe/layout/components/LocMarkDialog.vue'
export default {
components:{
BaiduMap
},
props:{
q:Object, //这里是 resolve 和reject
_compId:Number, //这里是组件id
finish:Function, //这里是组件销毁后的回调。
location: {type:Object, default:{lng:113.97, lat:28.36300}},
},
setup(props, ctx)
{
let dialogVisible = ref(true); //对话框默认显示
const onCancel = ()=>{
dialogVisible.value = false;
props.q.reject('cancel');
}
const onSure = ()=>{
dialogVisible.value = false;
props.q.resolve({success:true});
}
//对话框销毁后, 通知CompFactory 销毁节点.
const finalThings = ()=>{
props.finish(props.id);
}
onUnmounted(()=>{
console.log('bye!');
})
return {
location:props.location,
dialogVisible,
onCancel,
onSure,
finalThings
}
}
}
</script>
<style lang="scss">
.ui-loc-mark-dialog .el-dialog__body {
height: 23rem;padding:.5rem;
}
</style>