共计 5090 个字符,预计需要花费 13 分钟才能阅读完成。
前言
笔者去年写过一篇 NiceModal:从新思考 React 中的弹窗应用形式,6 月中旬换了工作,技术栈也由 React + TypeScript 切成了 Vue@2.7 + JavaScript :),然而没有发现 @ebay/nice-modal-react 在 vue 生态中的代替计划,故而有了这篇文章。
对于 React 技术栈写过一篇 2021-2022,我的前端最佳实际,有趣味的同学能够参考浏览。
如果本文能对你日常开发产生一些新的思考和效率的晋升,那就太好了!笔者在工作中接触 vue 的工夫也就短短半个月,如有设计不当的中央欢送 PR 一起改良。Vue Nice Modal – GitHub
在开始进入正题之前,先看看一些与弹窗无关的乏味场景 🤔。
一些乏味的实在场景
案例一:全局弹窗
上图是掘金的登录弹窗,未登录状态下触发该弹窗展现的机会有很多,比方:
- 点击 Header 上的登录按钮
- 文章列表页或详情页点赞以及评论文章
- 发沸点、评论沸点以及点赞沸点
- …
开发者往往会基于第三方组件库定义一个 <LoginModal />
,而后将其挂载至 Root
组件下。
这样会带来一些问题:
<LoginModal />
外部逻辑在组件渲染的时候就会执行,即便弹窗处于暗藏状态- 额定的复杂度。因为存在多个触发机会,须要将
setVisible
以及setOtherLoginData
透传至<Main />
外部的多个子组件,你能够抉择通过 props 一层一层的传递进去(鬼晓得有多少层!),也能够引入工具进行状态共享,但不论怎样,这都不是一件容易的事; - 随着相似的弹窗越来越多,根组件会保护很多弹窗组件的状态 … 天知道为什么展现一个弹窗须要在多个文件里重复横跳。
展现一个弹窗,为什么会变得如此简单?
以上案例来自 @ebay/nice-modal-react,稍作批改
除了上述全局弹窗的场景,还有一种场景也很让人头疼。
案例二:存在分支以及依赖关系的弹窗
用户头疼,开发者也头疼
弹窗 1 确认弹出弹窗 2,勾销则弹出弹窗 3,弹窗 2 以及 弹窗 3 也存在对应的分支逻辑,子孙满堂轻而易举,若依照惯例申明式弹窗组件的实现,十分恐怖!
Vue Nice Modal
vue-nice-modal 是一个工具库,能够将 Vue.js 的 modal 组件转换为基于 Promise 的 API。
灵感来源于 @ebay/nice-modal-react 和 vant。
反对 Vue 2.x,通过 vue-demi。
vue@2.7.x 以及 vue@3 曾经通过测试,vue@2.6.x 暂未测试,实践上也是反对的 (欢送 PR),倡议降级至 vue@2.7.x 享受 composition-api 的个性。
示例
你能够在 examples/* 文件夹中查看残缺的我的项目示例。
装置
npm install vue-nice-modal | |
# or | |
yarn add vue-nice-modal | |
# or | |
pnpm add vue-nice-modal |
应用
import {create} from 'vue-nice-modal'; | |
import MyModal from './MyModal.vue'; | |
const myModal = create(MyModal); | |
myModal | |
.show({ | |
title: 'Hello,world!',content: 'This is a nice modal.',}) | |
.then((result) => {console.log('Confirmed! Result:',result); | |
}) | |
.catch((error) => {console.error('Rejected! Error:',error); | |
}); |
自定义 Modal 组件
<script setup lang="ts"> | |
import {Dialog} from 'vant'; | |
import {INiceModalHandlers} from 'vue-nice-modal'; | |
// inject hide/remove/callback methods by vue-nice-modal | |
interface IProps extends INiceModalHandlers<number> { | |
visible: boolean; | |
// props you need | |
title: string; | |
content: string; | |
} | |
interface IEmits {(e: 'update:visible',visible: boolean): void; | |
} | |
const props = defineProps<IProps>(); | |
// @ts-ignore | |
const emit = defineEmits<IEmits>(); | |
const handleCancel = () => {props.hide(); // or emit('update:visible',false) | |
props.callback('cancel'); // reject the promise | |
}; | |
const handleConfirm = async () => { | |
// mock async function call | |
const sleep = (ms: number): Promise<number> => | |
new Promise((res) => | |
setTimeout(() => {res(ms); | |
},ms) | |
); | |
const payload = await sleep(1000); | |
// resolve the promise with payload | |
props.callback('confirm',payload); | |
}; | |
</script> | |
<template> | |
<dialog | |
:show="visible" | |
@update:show="$emit('update:visible',false)" | |
@cancel="handleCancel" | |
@confirm="handleConfirm" | |
@closed="remove" | |
:title="title" | |
:content="content" | |
show-cancel-button | |
class="demo-dialog" | |
> | |
<div>Hello, Vue Nice Modal</div> | |
</dialog> | |
</template> |
残缺示例请点击 example-vue3
本节提供了一个应用 vue-nice-modal 库创立自定义 modal 组件的示例。该示例应用 vant UI 库的 Dialog 组件作为示例,但你能够应用任何自定义 modal 组件,并不限度组件库,如在 example-vue2.7.x 里应用的是 ant-design-vue。
很显著,此处与日常工作中自定义一个弹窗组件没有任何别离,只不过通过 INiceModalHandlers 注入了一些通用的办法(示例中除 title 以及 content 的相干内容),你仍旧能够通过组件的模式调用该弹窗组件,只不过须要自行注入相干办法以及
visible
/update:visible
以实现 v-model 相干绑定。
在示例中,visible 属性和 update:visible 事件由 vue-nice-modal 注入到自定义 modal 组件中。这些用于管制 modal 组件的可见性。visible 属性应是一个布尔值,用于确定 modal 是可见的还是不可见,update:visible 事件应在 modal 的可见性扭转时触发。
hide()、remove() 和 callback() 办法也由 vue-nice-modal 注入到自定义 modal 组件中。这些办法用于暗藏或删除 modal 组件,以及解决用户确认或勾销 modal 操作。
modal 组件创立结束后,能够应用 vue-nice-modal 提供的 create() 函数来创立一个 Modal 对象,该对象蕴含 show()、hide() 和 remove() 办法。后续即可应用 show() 办法显示自定义 modal 组件,并应用 vue-nice-modal 提供的基于 Promise 的 API 解决用户确认或勾销 modal 操作。
API
create(Comp: Component): Modal
create 函数承受 Vue.js 组件并返回一个带有以下办法的 Modal 对象:
show(options: ExtractOptions<ComponentProps<C>>): Promise<any>
显示 modal 组件并返回一个 Promise,如果用户确认 modal 则 resolve,如果用户勾销则 reject。
options 参数是一个对象,蕴含与 modal 组件相干的属性 (除去 vue-nice-modal 注入的通用属性与办法,仅蕴含用户自定义的所需 props,以达到良好的类型提醒)。
以下是 show 办法的类型提醒实现:
type ComponentProps<C extends Component> = C extends new (...args: any) => any | |
? Omit< | |
InstanceType<C>['$props'],keyof VNodeProps | keyof AllowedComponentProps | |
> | |
: never; | |
type ExtractOptions<T extends Record<string,any>> = Omit< | |
T,keyof INiceModalHandlers | 'visible' | 'onUpdate:visible' | |
>; | |
export function create<C extends Component>(Comp: C) { | |
// ... | |
const show = (options: ExtractOptions<ComponentProps<C>>) => {// ...}; | |
return { | |
show,// ... | |
}; | |
} |
hide(): void
暗藏 modal 组件。
remove(): void
从 DOM 中删除 modal 组件。
类型定义
vue-nice-modal 提供了一些 TypeScript 类型定义:
Modal
Modal 接口定义了 create 返回的对象的办法。
interface Modal {show: (options: ExtractOptions<ComponentProps<C>>) => Promise<any>; | |
hide: () => void; | |
remove: () => void;} |
ComponentProps<C extends Component>
ComponentProps 工具泛型用于获取 Vue 组件的属性。
type ComponentProps<C extends Component> = C extends new (...args: any) => any | |
? Omit< | |
InstanceType<C>['$props'],keyof VNodeData | keyof AllowedComponentProps | |
> | |
: never; |
INiceModalHandlers
INiceModalHandlers 接口定义了用于解决用户确认或勾销 modal 的办法。
export interface INiceModalHandlers<T = any> {callback: (action: 'confirm' | 'cancel',payload?: T) => void; | |
remove: () => void; | |
hide: () => void;} |
这些办法以及 visible 和 update:visible 事件将被注入用户的自定义 modal 组件中,即便不应用基于 Promise 的函数调用,相干属性也能够通过 v-model(visible 和 update:visible) 传递从而管制组件的可见性。这容许用户按本人喜爱的形式管制 modal 组件的显示和暗藏,同时也确保了 vue-nice-modal 库的灵活性。
ExtractOptions<T extends Record<string,any>>
ExtractOptions 类型用于提取与 modal 组件相干的选项(除去 vue-nice-modal 注入的通用属性与办法)。
type ExtractOptions<T extends Record<string,any>> = Omit< | |
T,keyof INiceModalHandlers | 'visible' | 'onUpdate:visible' | |
>; |
留神
- modal 组件必须具备 visible 属性和 update:visible 事件以管制其可见性。请参阅 MyModal.vue 作为示例。
- vue-nice-modal 会在 DOM 中增加一个根元素 div.vue-nice-modal-root。请确保款式兼容。
举荐浏览
- eBay/nice-modal-react: A modal state manager for React.
- Modal.confirm 违反了 React 的模式吗?