Vue3 如何实现一个带遮罩的 dialog 对话框(一)

简介: Vue3 如何实现一个带遮罩的 dialog 对话框

image.png

前言: 今天在项目中遇到了很多很多需要弹出一个对话框的场景,由于之前全都是通过 v-if 来控制这个组件的显示与否,这样就造成了很多页面莫名多出了很多不相关的代码,极度不优雅。所以我尝试去实现了一个函数式调用的 dialog 组件,感觉在简单的场景下还是比较好用的,特来分享一下这个思路。🎁

b277152071bf439ab69079a008c426ce_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

一. 前期准备


你需要创建两个文件来和我一起完成这个函数式调用的 dialogDialog.vuedialogCreator.ts

image.png

二. dialog 遮罩的样式


我的组件样式是采用 UnoCss 的写法,是将样式内嵌在标签的class 属性里。和大家在 Style 标签里写是一模一样的效果,大家不用特别担心样式写法的问题,样式和本文主要内容没有任何直接的关系。

这里我们选择先写一个遮罩,关于遮罩的关键点其实就是需要设置一个带一点点透明度的背景,我选择了 rgba(0,0,0,0.4) ,也就是带 0.4 透明度的纯黑背景颜色。

image.png

在这里我们需要特别注意,由于我们的遮罩是会出现在“其它页面之上”的,所以我们需要给整个组件外部设置一个 absolute 来使它独立于其它页面,为了防止某些边界情况,需要设置 z-index:9999 来保证这个页面会在整个应用之上。整体效果如下:

image.png

三. dialog 对话框的样式


关于 dialog 对话框的样式这里我们不统一设置,但是我们组件至少需要包含三个主要元素。一个 Header 区域,一个 content 区域,最后一个取消按钮和确定按钮的区域。

image.png

  1. 在这里你可以先把文字都暂时写成固定值,到后面我会解释如何通过 props 动态的传递这些值。

四. h 函数和 render 函数的用法


让我们打开之前准备的 dialogCreator.ts 文件,引入我们刚刚编写的 Dialog 组件,一会儿我们就需要用到它了。

image.png

在此之前我们还需要引入两位老朋友 h,函数和 render 函数。在这里看过我之前《如何创建一个全局搜索框🔍》《如何创建一个 Toast》 这两篇文章的朋友一定不会陌生这两个函数的意义,但为了照顾新朋友我还是会大概讲解一下这两个函数的主要用途的。

image.png

我相信大家对 Vue 渲染组件的流程有一个大概的认知,Vue 是先构建出 虚拟dom 然后再根据 虚拟dom 去渲染出 真实dom的。

在这里我们需要清晰的知道, Vue 给我们提供的的 template 标签仅仅只是一个让我们可以用熟悉的 html 标签书写 虚拟dom 的语法糖而已。

image.png

是的,你没有听错,它仅仅只是一个语法糖而已,它底层是会被编译成用 h 函数创造出的 虚拟dom 在这里从而引出官方解释。

image.png

那么上文官方提到的渲染函数又是什么呢?其实就是刚刚我们提到的 h 函数。h() 函数更准确名字其实应该是 createVnode(),和它的英文翻译是一一对应的,创建虚拟Dom。

image.png

这个函数具体该如何使用呢?我们从实战去理解,让我们继续编写我们的 DialogCreator 类,我们创建两个函数,一个控制 dialog 的出现叫做 present 方法,另一个控制 dialog 的消失,叫做 dismiss方法。

image.png

这里马上就要用到刚刚提到的 h 函数。h 函数的第一个参数可以接收一个组件作为实参,并且返回这个组件的 虚拟dom 给我们。所以我们可以按照下面的写法拿到我们所需要的 Dialog 组件的 虚拟dom

image.png

拿到 虚拟dom 有什么用呢?这里需要引入我们的第二个关键函数 render 函数。我们需要知道,我们目前只拿到了一个游离于 真实dom节点 之外的一个“假的dom”节点,你需要告诉它该渲染到哪里。什么意思呢?打开我们的 main.ts 文件。

image.png

千万不要忘记这个 #app 是什么。

image.png

它就是我们全局唯一的 真实dom ,一个朴实无华的一个 id 叫做 app真实dom。

然后我们观察我们 render 函数可以接收的参数类型是什么,看下图我画黄色线的地方,看到什么惊喜了吗?第一个参数是一个 vnode

image.png

什么?vnode,我刚刚不才通过 h(Dialog) 函数拿到了一个 vnode 吗?没错,聪明的你应该能猜到下面的写法了。

image.png

emm 但是好像在报错,我们看一下错误信息。(这里我们忽略第三个参数,只考虑两个参数即可。)

image.png

🤔,这个 container 参数的类型是一个 element 或者 ShadomRoot,这又是什么鬼呢?我们继续点击 render 函数,进入它的定义,发现 container  原来最终是一个 HostElement 类型。看来这个搞清楚这个 HostElement 是关键。

image.png

在这里我们转变一下思路,我们反向推断 HostElement 是个什么。让我们再次打开 main.ts 文件,这次我们跳进 mount 函数的定义,就是下面黄色圈圈圈起来的这个函数。

image.png

你看到了什么?

image.png

没错很熟悉的几个单词 HostElement ,注意,你千万不要觉得这个 HostElement 是什么很神奇的元素,让我们回想一下 mount 函数的参数是什么来着?

image.png

没错,还是那个普普通通的,一个叫做 app 的全局的真实dom

image.png

由此我们可以反向推断出,render 函数需要一个 真实dom 来包裹我们的虚拟dom。生产出一个 真实dom 还不容易吗?我们直接调取 js 的方法,createElement(‘div’) 来生产一个普通的 div 元素用来包裹我们的虚拟dom。

image.png

五. 完善 DialogCreator 类


现在也告诉了虚拟Dialog 组件该放在哪里了,接下来就需要将我们的 containerEl 放在正确的位置,放在哪里呢?由于我们的 dialog 出现的情况一般都是最顶层。提醒你一下,别忘了我们所有其它页面都是被放到了 id为 appdiv 标签里。那么为了保证它绝对出现在最顶层而不被其它页面遮挡的这种情况发生,那我们延伸一下思路,如果让我们的 Dialog 成为 body 标签的第一个子元素,并且由于之前我们给 Dialog 组件设置了 absolute 属性,那么它就会正好浮现在我们所有页面之上,由于它脱离了文档流,那么它的出现就不会影响我们其它页面的布局

image.png

思路有了,这还不简单吗?如何成为 body 的第一个子元素就是基础方法了,这里就不过多解释了。

image.png

而让元素消失的方法就更简单了,合适的时机移除这个 dom 元素即可。

image.png

让我们测试一下是否可行,我们随便在哪一个页面里去调用我们的 DialogCreator 类调用 new 生成一个 Dialog 实例。然后随便写两个按钮去调用这两个方法测试一下。

image.png

效果如下:

24a3255e0ed44ca8a3d4c1d701b98f4c_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.jpg

但是由于我们的“遮罩”挡住了我们的按钮,所以目前为止我们暂时点击不了消失按钮。别着急,我们一步一步尝试优化现在的代码。

相关文章
|
9天前
vue3【实战】语义化首页布局
vue3【实战】语义化首页布局
28 2
|
9天前
|
存储 容器
vue3【实战】来回拖拽放置图片
vue3【实战】来回拖拽放置图片
19 2
|
9天前
|
JavaScript 开发工具 开发者
vue3【提效】使用 VueUse 高效开发(工具库 @vueuse/core + 新增的组件库 @vueuse/components)
vue3【提效】使用 VueUse 高效开发(工具库 @vueuse/core + 新增的组件库 @vueuse/components)
32 1
|
4天前
【vue3】Argumnt of type ‘history:RouterHistory;}is not assignable to paraeter of type ‘RouterOptions‘.
【vue3】Argumnt of type ‘history:RouterHistory;}is not assignable to paraeter of type ‘RouterOptions‘.
6 0
|
4天前
|
JavaScript
【vue3】vue3中路由hash与History的设置
【vue3】vue3中路由hash与History的设置
9 0
|
4天前
|
编解码 前端开发
【Vue3】解决电脑分辨率125%、150%及缩放导致页面变形的问题
【Vue3】解决电脑分辨率125%、150%及缩放导致页面变形的问题
11 0
|
4天前
|
JavaScript
【vue】 vue 翻页时钟制作,vue2、vue3
【vue】 vue 翻页时钟制作,vue2、vue3
11 0
|
2天前
|
JavaScript
|
4天前
|
JavaScript
【vue】el-dialog 内的tinymce弹窗被遮挡的解决办法 及 tinymce打开弹出菜单后直接关闭对话组件,导致该弹出菜单残留
【vue】el-dialog 内的tinymce弹窗被遮挡的解决办法 及 tinymce打开弹出菜单后直接关闭对话组件,导致该弹出菜单残留
16 6
|
1天前
|
存储 缓存 JavaScript
vue代码优化方案
【7月更文挑战第13天】 **Vue.js 优化要点:** 分解大组件以提高复用性和加载速度;利用计算属性与侦听器优化数据处理;使用Object.freeze()减少响应式数据;借助Vuex或Composition API管理状态;实现虚拟滚动和无限加载提升长列表性能;路由懒加载减少初始加载时间;用Vue DevTools检测性能瓶颈;定期代码审查与重构;应用缓存策略;遵循最佳实践与团队规范,提升应用整体质量。
10 2