vue3.0的弹窗
2020-12-01 17:55 muamaker 阅读(2606) 评论(3) 编辑 收藏 举报关于vue3.0写一个弹窗
一、官方提供的方法 teleport
1 2 3 4 5 6 7 8 9 10 11 12 13 | <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组件无法正常显示
二、自定义弹出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | import { markRaw ,defineComponent,reactive, toRefs} from 'vue' 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; } interface IState{ params: IParams | null ; visible:boolean; } export default defineComponent({ setup(){ const state = reactive<IState>({ params: null , visible: false , }); const open = (com:IParams)=>{ state.params = markRaw<IParams>(com); state.visible = true ; } const ok = (...a:Array<any>)=>{ state.params?.ok && state.params?.ok(...a); state.params?.onClose && state.params?.onClose(...a); state.visible = false ; } const cancel = (...a:Array<any>)=>{ state.params?.cancel && state.params?.cancel(...a); state.params?.onClose && state.params?.onClose(...a); state.visible = false ; } const beforeClose = (done:Function)=>{ if (state.params?.diagProps && state.params?.diagProps[ "before-close" ]){ new Promise((resolve)=>{ return state.params?.diagProps && state.params?.diagProps[ "before-close" ](resolve); }).then((res : any)=>{ ok(res); done(); }); } else { ok(); done && done(); } } return { ...toRefs(state), open, ok, cancel, beforeClose }; }, render(ctx:any){ if (!ctx.params || !ctx.params.children || !ctx.visible){ return <div></div>; } const com =ctx.params.children; const Header = ctx.params.header; const Footer = ctx.params.footer; const modal = { ok:ctx.ok, cancel:ctx.cancel } console.log(JSON.stringify(ctx.params.diagProps)) return ( <el-dialog title= "弹窗" {...ctx.params.diagProps} before-close={ctx.beforeClose} v-model={ctx.visible} v-slots={ { footer: ()=>Footer ? <Footer modal={modal} /> : null , header:()=>Header ? <Header modal={modal} /> : null } } > { com && <com modal={modal} {...ctx.params.props} /> } </el-dialog> ) } }); |
index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 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); }); } |
参考:element plus 的 message-box 实现方式
一样的会有
vnode.appContext = appContext
此处 appContext 有两个来源
1、与上面的弹窗实现一样, app._context
2、在组件的 setup 里面获取
1 2 3 4 5 6 7 8 9 | <script lang= "ts" > import { defineComponent,getCurrentInstance } from 'vue' export default defineComponent({ setup() { const { appContext } = getCurrentInstance()!; }, }) </script> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架