如何使用Vue3 tsx 和hooks开发Dialog组件
这段时间在用Vue3和tsx写项目,正好遇到了需要写Dialog的场景,因此用本文来介绍一下Vue3 和 hooks 开发Dialog的经验(不足之处和可以改进的地方求大佬们提出)。由于希望用简单的篇幅描述完开发过程 因此环境搭建等流程不做介绍
实现一个Dialog
Dialog组件需要一个外部变量去控制他是否被显示,同时也需要外部传一个函数用于关闭Dialog,因此Props中需要一个visible变量和一个onClose函数。
Dialog打开时,会显示一个阴影,在整个屏幕,然后对话框居中。因此html结构也可以确定下来
组件是挂载在body之下的,TelePort可以实现这个功能
接下来就是组件的实现
import {
defineComponent,
Teleport,
PropType,
ref,
} from 'vue';
import s from './Dialog.module.scss';
export const Dialog = defineComponent({
props: {
visible: {
type: Boolean,
},
onClose: {
type: Function as PropType<() => void>,
},
},
setup(props, context) {
return () =>
props.visible && (
<Teleport to={document.body}>
<section class={s.mask} />
<section class={s.dialog}>
<header>{context.slots.title?.()}</header>
<main>{context.slots.default?.()}</main>
<footer>{context.slots.buttons?.()}</footer>
</section>
</Teleport>
);
},
});
组件的css如下
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: var(--dialog-z-index);
}
.dialog {
display: flex;
flex-direction: column;
z-index: calc(var(--dialog-z-index) + 1);
background: white;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 20em;
> header {
padding: 12px 16px;
color: #333;
background: rgba(255, 245, 102, 1);
}
> main {
padding: 12px 16px;
flex-grow: 1;
}
> footer {
padding: 12px 16px;
text-align: right;
> button {
border: none;
background: white;
}
button + button {
margin-left: 16px;
}
}
}
组件的调用方式如下:
import {ref,defineComponent} from 'vue'
import { Dialog } from './dialog'
export const Demo = defineComponent({
setup(props,context){
const visibleRef = ref(false)
const openDialog = ()=> visibleRef.value = true;
const closeDialog = ()=> visibleRef.value = false;
const confirm = () =>{
// ... 确定所做的操作
closeDialog()
}
return ()=> <>
<button onClick={openDialog}>show dialog</buttton>
<Dialog
visible={visible}
onClose={closeDialog}
v-slots={{
title: ()=> "标题",
content: ()=> "内容",
buttons: ()=> <button onClick={confirm}>确定</button>
}}
>
</>
}
})
虽然这样也能使用一个Dialog 但是操作比较复杂
实现useDialog hooks
Vue3的hooks大量的借鉴了React 因此有React开发经验的开发者理解hooks的概念应该不会存在特别大的障碍 所以关于hooks本文不过多的介绍
下面的useDialog的实现代码
import {
ref,
VNode,
FunctionalComponent,
} from 'vue';
interface Options {
title?: VNode;
content?: VNode;
buttons?: VNode;
}
export function useDialog(options: Options = {}) {
const visible = ref(false);
const open = () => (visible.value = true);
const close = () => (visible.value = false);
const RenderDialog: FunctionalComponent = () => {
return (
<Dialog
visible={visible.value}
onClose={close}
v-slots={{
title: () => options.title,
default: () => options.content,
buttons: () => options.buttons,
}}
/>
);
};
return { open, close, RenderDialog };
}
组件的使用
import { defineComponent } from 'vue';
import { useDialog } from './components/Dialog';
export const App = defineComponent({
setup() {
const { RenderDialog, open, close } = useDialog({
title: <>登陆</>,
content: (
<>
内容
<input />
</>
),
buttons: <button onClick={() => close()}>确认</button>,
});
return () => (
<>
<button onClick={open}>+1</button>
<RenderDialog />
</>
);
},
});
效果图
Stackbiltz链接
本文简单的使用了Vue3 + hooks实现了Dialog组件 如果有什么更好的写法建议 欢迎各位留言 谢谢