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

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

相关文章
|
7月前
|
JavaScript 前端开发 安全
Vue 3
Vue 3以组合式API、Proxy响应式系统和全面TypeScript支持,重构前端开发范式。性能优化与生态协同并进,兼顾易用性与工程化,引领Web开发迈向高效、可维护的新纪元。(238字)
960 139
|
7月前
|
缓存 JavaScript 算法
Vue 3性能优化
Vue 3 通过 Proxy 和编译优化提升性能,但仍需遵循最佳实践。合理使用 v-if、key、computed,避免深度监听,利用懒加载与虚拟列表,结合打包优化,方可充分发挥其性能优势。(239字)
526 1
|
8月前
|
开发工具 iOS开发 MacOS
基于Vite7.1+Vue3+Pinia3+ArcoDesign网页版webos后台模板
最新版研发vite7+vue3.5+pinia3+arco-design仿macos/windows风格网页版OS系统Vite-Vue3-WebOS。
876 11
|
7月前
|
JavaScript 安全
vue3使用ts传参教程
Vue 3结合TypeScript实现组件传参,提升类型安全与开发效率。涵盖Props、Emits、v-model双向绑定及useAttrs透传属性,建议明确声明类型,保障代码质量。
595 0
|
9月前
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
918 1
|
9月前
|
缓存 JavaScript UED
除了循环引用,Vue3还有哪些常见的性能优化技巧?
除了循环引用,Vue3还有哪些常见的性能优化技巧?
478 0
|
10月前
|
JavaScript
vue3循环引用自已实现
当渲染大量数据列表时,使用虚拟列表只渲染可视区域的内容,显著减少 DOM 节点数量。
221 0
|
8月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
675 2
|
7月前
|
缓存 JavaScript
vue中的keep-alive问题(2)
vue中的keep-alive问题(2)
577 137
|
11月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
1111 0