Vue2 Dialog弹窗函数式调用实践示例

目录
  • 前言
  • Vue2 的弹窗常用的使用方式
    • 第一种:将弹窗写在上下文中
    • 第二种:原型上注入全局方法
    • 第三种:通过依赖注入的方式
  • 合理的使用方式
  • 功能实现
  • 结语

前言

Dialog 对话框组件几乎是每个前端项目必不可少的组件,通常是在保留当前页面状态并屏蔽其他用户输入的情况下,与用户交互并承载相关操作。

在 BOM 的方法中,alertprompt 都是以前用来做类似功能的方法,但是这些浏览器内置方法会完全停止网页代码执行,对于贫乏的前端线程资源来说实在是过于僵硬。于是便产生了各种各样的 Js 弹窗。

今天就来谈谈在前端框架 Vue 2 版本中的弹窗组件的相关实现以及我个人认为的最佳实践。

Vue2 的弹窗常用的使用方式

对于 Vue2 的 UI 框架,坊间比较火的有 element-ui antd-vue vant-ui 等等,不过他们在弹窗的使用方法上几乎都是一致的。下面我们以 element-ui 中的组件为例讲讲这些使用方式的缺点

第一种:将弹窗写在上下文中

这种方式不用多讲,使用起来肯定是最麻烦的一种,这意味着你每次想使用这个弹窗组件都要写一遍负责显隐的状态以及方法,尽量只在唯一场景里使用,不具备复用条件。

第二种:原型上注入全局方法

即是通过往 Vue.prototype 中注入弹窗的方法,使你可以在 vue 上下文中使用 this.$confirm 诸如此类的方法来使用他们的弹窗功能。

这种调用方式曾经几乎是所有 vue2 全局 api 的解决方法,一时间各种插件都有了各种各样的全局 api。例如 vuex 的 this.$store、 EventBus 的 this.$bus 等等。

这种方式在使用上虽然非常方便,但也有如下一些缺点

上下文污染,一个 vue 的全局 this 里面,什么东西都可以有,不论是全局方法、还是全局变量,都可以放在 Vue.prototype 里面,例如前一个例子,如果复杂的弹窗需要在各种其他前端文件内打开,大概率也是用这种方式将弹窗打开和关闭方法都注入到全局 this 中使用。

类型丢失,无论你是全局状态管理还是事件总线,在使用的时候都是各种黑盒,全局方法来自于什么插件、需要传什么参数、返回什么东西,全然无感,只能想办法寻找蛛丝马迹去溯源。

只能在 vue 上下文中使用,在 vue 文件外就无法使用了。就好比 React 无法在 React 上下文之外 setState 一样难受。

第三种:通过依赖注入的方式

这种方式和第二种几乎一样,通过顶层组件 provider 出对应的函数方法,之后在子组件中 inject 进来。 一样存在着 provider 的上下文污染,也丢失了类型,并且依旧只能在 vue 上下文中使用,灵活性一样差。

合理的使用方式

除了上述所说的问题之外

综合以上的缺点,我认为一个弹窗最佳的使用方式在 Vue2 中使用方式是这样:

    <template>
        <button @click="open">打开弹窗</button>
    </template>
    <script>
        import openDialog from 'my-dialog'
        export default {
            methods: {
                open() {
                    openDialog()
                        .then(() => {})
                        .catch(() => {})
                        .finally(() => {})
                }
            }
        }
    </script>

也可以在其他文件中,例如:

    // api.ts
    import openDialog from 'my-dialog'
    const getUserInfo = () => {
        return fetch('/xxx/api').then(() => {
            openDialog('success')
        })
    }

功能实现

废话不多说,直接上核心代码

// dialog.ts
import Vue, { ComponentInstance } from "vue";
import ConfirmDialog from "./confirm-dialog.vue";
export let index = 1000;
export const cache = new Set<string>();
export function openDialog(component: ComponentInstance) {
	const div = document.createElement("div");
	const el = document.createElement("div");
	const id = 'dialog-' + Math.random();
	div.appendChild(el);
	document.body.appendChild(div);
	const ComponentConstructor = Vue.extend(component);
	return (propsData = {}, parent = undefined) => {
		let instance = new ComponentConstructor({
			propsData,
			parent,
		}).$mount(el);
		const destroyDialog = () => {
			if (cache.has(id)) return;
			if (instance && div.parentNode) {
				cache.add(id);
				(instance as any).visible = false;
                // 延时是为了在关闭动画执行完毕后卸载组件
				setTimeout(() => {
					cache.delete(id);
					instance.$destroy();
					// @ts-ignore
					instance = null;
					div.parentNode && div.parentNode.removeChild(div);
				}, 1000);
			}
		};
		// visible控制
		if ((instance as any).visible !== undefined) {
			// 支持sync/v-model
			instance.$watch("visible", (val) => {
				!val && destroyDialog();
			});
			Vue.nextTick(() => ((instance as any).visible = true));
		}
		return new Promise((resolve, reject) => {
			// emit 一个 done 事件关闭
			instance.$once("done", (data: any) => {
				destroyDialog();
				resolve(data);
			});
			// emit 一个 cancel 事件取消
			instance.$once("cancel", (data: any) => {
				destroyDialog();
				reject(data);
			});
		});
	};
}
export function confirmDialog(content: string, editable?: boolean) {
	return openDialog(ConfirmDialog as any)({ content, editable });
}

使用方式和结果可以通过下面这个例子进行查看

stackblitz.com/edit/vitejs…

在例子中,我们可以将任意 vue 组件包装进 openDialog Api 中,只需要在组件的 data 里写入一个 visible 属性,而后续在组件方法中调用 this.$emits('done') 或者 this.$emits('cancel') 就可以对应 openDialog 方法返回 Promise 的 then 回调和 catch 回调。

可以自定义传参、自定义内容、自定义事件,自定义返回,灵活性直接拉满。

而且,方法不限于任何上下文,在任何文件内都可以使用,实现了真正的函数式调用 Vue2 的弹窗组件。

但是这个方法确实也有那么一点点不方便的地方,如果有掘友看得出来,可以在评论下方说说。

结语

这篇文章其实没多少东西,甚至代码都是我一年多前就写的,在受到 React hooks 的启发后,我就觉得函数式编程非常的爽,就尝试将项目中的全局变量都剥离 vue 上下文,包括 dialogmessage 组件,之后摒弃 vuexpinia 这种强绑上下文的状态管理库,改用 Vue.observable,就可以很方便的将业务与 UI 完全抽离。所有变量和方法都可以通过 import 追溯,更重要的是这种模式更契合 typescript,类型提示也很轻易跟上来了。

以上就是本篇文章的所有内容了,后续我会封装 v2 和 v3 的函数式弹窗组件,并发布到 npm 上,到时候再更新链接到文章里,更多关于Vue2 Dialog弹窗函数式调用的资料请关注我们其它相关文章!

(0)

相关推荐

  • VueJs中toRef与toRefs函数对比详解

    目录 正文 toRef()函数 与ref的不同 toRefs()函数 为啥需要toRef()与toRefs()函数 总结 正文 ref是处理基本数据类型响应式API函数,在setup中声明定义的变量,可以直接在模板中使用 没有被响应式API包裹处理的变量数据,是不具备响应式能力的 也就是往往在逻辑中修改了数据,但是页面不会更新,那怎么样将一个非响应式数据变成响应式数据 就需要用到toRef()与toRefs()这两个componsition API的 单纯的去看概念,往往比较抽象,是难以理解的,

  • vueJs函数readonly与shallowReadonly使用对比详解

    目录 前言 readonly shallowreadonly 总结 前言 在使用vue3开发项目时,对于一些特殊的需求,针对有些数据字段,在前端,只允许读取,不允许修改,比如:有的网站用户名,一旦注册了,就不允许修改 当然,有时候,我们从别的地方引用数据过来,用作信息的展示,但是却不允许修改,不希望影响源数据 那么readonly与shallowReadonly这两个API就很有用了的 readonly 让一个响应式数据变为只读的,接收一个响应式数据,经过readonly加工处理一下,那么新赋值

  • 一文搞懂VueJs中customRef函数使用

    目录 前言 示例-延迟显示 总结 前言 ref是Vue官方提供的componsition API,将一个非响应式数据转变为响应式数据的函数,至于底层怎么实现数据的收集与响应式 使用者无需去关注,相当于就是精装电脑,然而有时候,针对一些复杂特殊的需求,我们需要自己造轮子,自己手动原生的去实现内部结构 实现基础的功能的同时,还要进行额外的拓展,那么这时候就需要自定义ref了的,它就相当于是组装式的电脑,内部结构需要自己去组装,实现 而非直接从商城里购买,用一些现成的零部件组装一个类似精装的电脑,甚至

  • vueJs函数toRaw markRaw使用对比详解

    目录 前言 toRaw()函数 markRaw()函数 总结 前言 针对一些特殊的需求,在项目里,需要将响应式数据变为普通原始类型数据,这种情况是有的 在Vue里,能够将普通数据类型的数据变为响应式数据,同时,也能将响应式类型数据变为普通类型数据 可以用于提升数据的性能 toRaw()函数 接收一个reactive响应式数据,将一个响应式的数据变为普通类型的数据,转化为非响应式数据,相当于还原对象,reactive相当于制作,但对于ref响应式数据不起作用 将一个由reactive生成的响应式对

  • Vue 2中实现CustomRef方式防抖节流

    目录 前言 背景 撸代码 防抖(debounce) 代码 使用 节流(throttle) 代码 使用 总结 前言 今天给大家带来的是Vue 2 中的实现 CustomRef 方式防抖/节流这篇文章,前几天利用 customRef 实现了在 vue 3 中的极致防抖/节流的新方式.感兴趣的朋友可以点击 Vue 3 中的极致防抖/节流(含常见方式防抖/节流) 进行查看. 在前端的开发过程中,在涉及到与用户交互的过程中是基本上都是需要处理的,常规操作就是在对应位置加上防抖或者节流. 加上防抖或者节流的

  • vue2组件之select2调用的示例代码

    目前,项目中使用了纯前端的静态项目+RESTFul接口的模式.为了更好的对数据进行操作,前端使用了vue2的mvvm功能,但是由于不是单页面应用,所以,并没有涉及到其它的如vue-route等功能,也未使用webpack等编译功能,所以,也没有使用.vue文件功能.这时候,如果用到控件,则多数从原jquery的组件中选择. select下拉搜索选择 这次的需求调研与设计是原来做winform开发的同事,由于用惯了devexpress这个控件库,所以,对于searchlookupeditor这个控

  • vue3.2自定义弹窗组件结合函数式调用示例详解

    目录 前言 手写弹窗组件 组件调用 函数式调用 如何使用 含样式完整源码 效果图 前言 涉及的vue3知识点/API,createApp defineProps defineEmits <script setup> v-model <script setup> 就是 setup 语法糖 defineProps 和 props 用法差不多 defineEmits 声明可向其父组件触发的事件 手写弹窗组件 很简单的弹窗组件,支持设置标题 <script setup> def

  • 在vant中如何使用dialog弹窗

    目录 如何使用dialog弹窗 1.官网示例 2.第一步引入vant中的dialog组件 3.vue页面中引入 4.使用 vant dialog组件使用 vant Dialog组件引入 template中使用 如何使用dialog弹窗 1.官网示例 因为这次是在手机上用的所以就用了vant组件 2.第一步引入vant中的dialog组件 官网介绍自行选择安装方式 3.vue页面中引入 <van-dialog v-model="show" title="标题"

  • Android 自定义加载动画Dialog弹窗效果的示例代码

    效果图 首先是创建弹窗的背景 这是上面用到的 以shape_bg_5_blue.xml为例,其他的三个无非就是里面的颜色不一样而已 <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="5dp"

  • vant-ui组件调用Dialog弹窗异步关闭操作

    需求描述: 需求描述:官方文档又是组件调用方式,又是函数调用方式. 我就需要一个很简单的:点击操作弹窗显示后,我填写一个表单,表单校验通过后,再调用API接口,返回成功后,关闭弹窗. 一个很简单的东西,element-ui用的很方便,在这里就懵比了,刚开始做的,弹窗关闭了,才返回异步接口调用的结果.网速慢点,用起来真的很不好. 正确的解决方式一: <van-dialog v-model="showDialog" title="提示" show-cancel-b

  • iOS无障碍适配西瓜视频Voice Over实践示例

    目录 一.Voice Over 简介 二.Voice Over 使用指南 Voice Over 开发环境配置 Voice Over 基本使用 —— 以西瓜为例 入门手势 进阶手势 三.快速适配 Voice Over / 无障碍 设置无障碍焦点 设置无障碍文案 调整焦点顺序 西瓜首页适配实战 搜索栏 频道栏 & 频道编辑器 作者动态栏 视频列表 四.Voice Over 相关协议介绍 UIAccessibility 无障碍标签标注 UIAccessibilityAction 无障碍手势响应 UIA

  • Flutter 底部弹窗ModelBottomSheet的使用示例

    实现效果 最终实现效果如图片所示,分布演示了基础的,全屏的和自定义的底部弹窗形式. 代码结构 在消息页面 message.dart 中,使用 Column 组件构建了三个按钮,点击每个按钮调用不同的底部弹窗显示.这部分代码不展示,核心注意的方式是按钮的 onPressed 响应方法,需要使用 async 修饰,这是因为 ModalBottomSheet 的返回结果是一个 Future 对象,需要通过 await 来获取返回结果. onPressed: () async { int selecte

  • 微服务架构之服务注册与发现实践示例详解

    目录 1 服务注册中心 4种注册中心技术对比 2 Spring Cloud 框架下实现 2.1 Spring Cloud Eureka 2.1.1 创建注册中心 2.1.2 创建客户端 2.2 Spring Cloud Consul 2.2.1 Consul 的优势 2.2.2 Consul的特性 2.2.3 安装Consul注册中心 2.2.4 创建服务提供者 3 总结 微服务系列前篇 详解微服务架构及其演进史 微服务全景架构全面瓦解 微服务架构拆分策略详解 微服务架构之服务注册与发现功能详解

  • Spring Data Jpa框架最佳实践示例

    目录 前言 扩展接口用法 SPRINGDATAJPA最佳实践 一.继承SIMPLEJPAREPOSITORY实现类 二.集成QUERYDSL结构化查询 1.快速集成 2.丰富BaseJpaRepository基类 3.最终的BaseJpaRepository形态 三.集成P6SPY打印执行的SQL 结语 前言 Spring Data Jpa框架的目标是显著减少实现各种持久性存储的数据访问层所需的样板代码量. Spring Data Jpa存储库抽象中的中央接口是Repository.它需要领域实

  • jdbc中自带MySQL 连接池实践示例

    引言 在上期文章自定义 MySQL 连接池中,我提到了没找到一个特别合适的 MySQL 连接池实现,所以自己写了一个基于通用池化框架commons-pool2的 MySQL 连接池,并且模仿了 Go 语言的gorm框架设计思路,把借和还的操作不暴露给用户,只处理用户发来的 SQL 语句的思路,封装了一个com.funtester.db.mysql.MysqlPool. 可惜打脸的事情来的太快了,在录制视频的时候偶然发现了 Intellij 代码提示有个com.mysql.cj.jdbc.Mysq

随机推荐