關於vue3.0寫一個彈窗
一、官方提供的方法 teleport
<template>
<teleport to="#modal-container">
<div class="test">
<el-button type="primary">這是一個測試</el-button>
</div>
</teleport>
</template>
<script>
export default {
name:"Test"
}
</script>
to 指向的是一個 dom元素 id為 modal-container
缺點,只能引入后使用,不能通過js直接調用。
於是: 很自然想到 vue2.0 的 vue.extend 方法。 很可惜,沒有。。。
只能通過 createApp 自己再創建一個上下文、但是問題來了,上下文是不共享的。會出現element-plus組件無法正常顯示
二、自定義彈出
import { createVNode ,render} from 'vue'
const body = document.body;
const root = document.createElement("div");
body.appendChild(root);
root.className = "custom-root";
export default {
install(app){
let div = document.createElement("div");
root.appendChild(div);
// com 為自己寫的組件, SoltChild 可以是自己的子組件 ,也可以不傳
let vm = createVNode(com,{},{
});
vm.appContext = app._context; // 這句很關鍵,關聯起了數據
render(vm,div);
}
}
其中
vm.appContext = app._context; 非常關鍵 ,共享上下文
另外采用 jsx 語法:
Diag.vue
<script>
import {markRaw} from "vue"
export default {
data(){
return {
visible:false,
com:""
}
},
methods:{
open(com){
this.com = markRaw(com);
this.visible = true;
}
},
render(){
const com = this.com;
return (
<el-dialog
title="提示"
v-model={this.visible}
width="30%"
>
<span>這是一段信息6666666</span>
<el-button type="primary" >el-button</el-button>
{
com && <com />
}
</el-dialog>
)
}
}
</script>
index.js
import { createVNode ,render} from'vue'
const body = document.body;
const root = document.createElement("div");
body.appendChild(root);
root.className ="custom-root";
import Diag from "./Diag.vue";
let app;
export default {
install(a){
app = a;
}
}
export const Create = (com)=>{
let div = document.createElement("div");
root.appendChild(div);
// com 為自己寫的組件, SoltChild 可以是自己的子組件 ,也可以不傳
let vm = createVNode(Diag,{ref:"diag"},{
// slots
// default:()=>createVNode(SoltChild)
});
vm.appContext = app._context;// 這句很關鍵,關聯起了數據
render(vm,div);
vm.component.proxy.open && vm.component.proxy.open(com);
console.log(vm);
}
jsx 的中級 ts 版本
Diaglog.tsx
import { markRaw } from 'vue'
import { Vue } from 'vue-class-component';
interface IAny{
[x:string]:any
}
export interface IProps {
diagProps?: IAny;
props?:IAny;
ok?:Function;
cancel?:Function;
}
export interface IParams extends IProps{
children?: any;
footer?: any;
header?:any;
onClose?: Function;
}
export default class Home extends Vue{
visible=false;
params:IParams | null = null;
open(com:IParams){
this.params = markRaw<IParams>(com);
this.visible =true;
}
ok(...a:Array<any>){
this.params?.ok && this.params?.ok(...a);
this.params?.onClose && this.params?.onClose(...a);
this.visible =false;
}
cancel(...a:Array<any>){
this.params?.cancel && this.params?.cancel(...a);
this.params?.onClose && this.params?.onClose(...a);
this.visible =false;
}
beforeClose(done:Function){
done && done();
if(this.params?.diagProps && this.params?.diagProps["before-close"]){
new Promise((resolve)=>{
this.params?.diagProps && this.params?.diagProps["before-close"](resolve);
}).then(()=>{
this.cancel();
done();
});
}
}
render(){
if(!this.params || !this.params.children || !this.visible){
return <div></div>;
}
const com =this.params.children;
const Header = this.params.header;
const Footer = this.params.footer;
const modal = {
ok:this.ok,
cancel:this.cancel
}
return (
<el-dialog
title="彈窗"
{...this.params.diagProps}
before-close={this.beforeClose}
v-model={this.visible}
v-slots={
{
footer: ()=>Footer ? <Footer modal={modal} /> : null,
title:()=>Header ? <Header modal={modal} /> : null
}
}
>
{
com && <com modal={modal} {...this.params.props} />
}
</el-dialog>
)
}
}
index.ts
import { createVNode ,render}from'vue'
import type {VNode,App} from "vue"
const body = document.body;
const root = document.createElement("div");
body.appendChild(root);
root.className ="custom-root";
import Diag from "./Diaglog";
import type {IParams} from "./Diaglog"
export type {IProps} from "./Diaglog"
let app:App|null = null;
export default {
install(a:App){
app = a;
}
}
let list : Array<VNode> = [];
const getIstance = (params:IParams)=>{
let instance : VNode ;
if(list.length > 0){
instance = list.shift() as VNode;
}else{
let div = document.createElement("div");
root.appendChild(div);
// com 為自己寫的組件, SoltChild 可以是自己的子組件 ,也可以不傳
let vm = createVNode(Diag,{
// ref:xx
// 做用域插槽的格式為
// { name: props => VNode | Array<VNode> }
// scopedSlots: {
// default: props => createElement('span', props.text)
// },
// // 若是組件是其它組件的子組件,需為插槽指定名稱
// slot: 'name-of-slot',
},{
});
vm.appContext = app!._context;// 這句很關鍵,關聯起了數據
render(vm,div);
instance = vm;
}
const vvm = (instance.component as any)
new Promise((resolve)=>{
vvm.proxy.open && vvm.proxy.open({...params,onclose:resolve});
}).then(()=>{
// 關閉了彈窗,就回收
list.push(instance);
})
return {
destroy(){
vvm.proxy.destroy && vvm.proxy.destroy();
},
ok(...a:Array<any>){
vvm.proxy.ok && vvm.proxy.ok(...a);
},
cancel(...a:Array<any>){
vvm.proxy.cancel && vvm.proxy.cancel(...a);
}
}
}
export const Create = (params:IParams)=>{
return getIstance(params);
}
export const CreateAsync = (params:IParams)=>{
return new Promise((resolve,reject)=>{
let obj:IParams = {
...params,
ok(...a:Array<any>){
resolve(a[0]);
params.ok && params.ok(...a)
},
cancel(...a:Array<any>){
reject(a[0]);
params.cancel && params.cancel(...a)
}
}
getIstance(obj);
});
}
