聊聊Vue3+hook怎么写弹窗组件更快更高效

聊聊Vue3+hook怎么写弹窗组件更快更高效

为什么会有这个想法

在管理后台开发过程中,涉及到太多的弹窗业务弹窗,其中最多的就是“添加XX数据”,“编辑XX数据”,“查看XX详情数据”等弹窗类型最多。【相关推荐:vuejs视频教程、vuejs视频教程】

这些弹窗组件的代码,很多都是相同的,例如组件状态,表单组件相关的方法…

于是,我简单地对Dialog组件进行的二次封装和hooks,减少了一些重复的代码

要封装什么

如果是普通弹窗使用的话,直接使用el-dialog组件已经足够了

立即学习“前端免费学习笔记(深入)”;

但我还是一个比较爱折腾的人,我们先看看官方dialog文档有什么可以添加的功能

大概看了一下,我打算封装一下功能

提供全屏操作按钮(右上角)默认提供“确认”,“关闭”按钮内部添加Loading效果

封装Dialog

确定了要封装的功能之后,先来一个简单的dialog组件。

把双向绑定处理一下,这样外部就可以直接通过v-model直接控制弹窗了。

    interface PropsType {  modelValue?: boolean;}const props = withDefaults(defineProps(), {  modelValue: false,});const emits = defineEmits();

登录后复制

header

这里使用到图标库@element-plus/icons-vue

如没有安装,请执行npm install @element-plus/icons-vue

使用el-dialog提供的header插槽,将全屏图表和关闭图标放置到右上角中。给el-dialog传递show-close属性关闭默认图标。

            
        {{ props.title }}      
      
                      
      import { FullScreen, Close } from "@element-plus/icons-vue";// 处理样式:deep(.el-dialog__header) { border-bottom: 1px solid #eee; display: flex; padding: 12px 16px; align-items: center; justify-content: space-between; margin: 0;}.dialog-title { line-height: 24px; font-size: 18px; color: #303133;}.btns { display: flex; align-items: center; i { margin-right: 8px; font-size: 16px; cursor: pointer; } i:last-child { margin-right: 0; }}

登录后复制

弹窗的标题文字内容通过props进行传递,默认为空(”)

interface PropsType {  // 忽略之前的代码  title?: string;}const props = withDefaults(defineProps(), {  title: "",});

登录后复制

我们看看现在头部的效果(这里没传入标题,默认为”)

1.png

现在这个按钮只有样式效果,还没有写上对应的功能 ~

给他们先绑定上对应的事件和指令

                    
            {{ props.title }}        
        
                                
            import { FullScreen, Close } from "@element-plus/icons-vue";interface PropsType { title?: string; modelValue?: boolean; hiddenFullBtn?: boolean;}const props = withDefaults(defineProps(), { title: "", modelValue: false, hiddenFullBtn: false,});const emits = defineEmits();// 当前是否处于全屏状态const isFullscreen = ref(false);// 是否显示全屏效果图标const isFullScreenBtn = computed(() => { if (props.hiddenFullBtn) return false; if (attrs?.fullscreen) return false; return true;});// 开启、关闭全屏效果const handleFullscreen = () => { if (attrs?.fullscreen) return; isFullscreen.value = !isFullscreen.value;};// 关闭弹窗时向外部发送close事件const handleClose = () => { emits("close");};

登录后复制

再点击下全屏图标看看效果怎么样

2.png

NICE 头部功能也就完成了

Footer

接下来,再处理下底部内容,默认提供两个按钮,分别是“确定”和“关闭”,这个名称也是可以通过props属性修改的。

两个按钮绑定点击事件,向外发送不同的事件。

  
                                    {{            props.confirmText          }}          {{ props.cancelText }}                                    
import { useSlots } from "vue";// 获取插槽const slots = useSlots();interface PropsType { title?: string; width?: string | number; isDraggable?: boolean; modelValue?: boolean; hiddenFullBtn?: boolean; confirmText?: string; cancelText?: string;}const props = withDefaults(defineProps(), { title: "", isDraggable: false, modelValue: false, hiddenFullBtn: false, confirmText: "确认", cancelText: "关闭",});const handleClose = () => { emits("close");};const handleConfirm = () => { emits("confirm");};

登录后复制

3.png

又搞定了一部分了,就剩下Content了 ~

Content

弹窗内容通过默认插槽的方式传入进来,在外层的div元素上添加v-loading标签,实现加载态。

如果你想整个弹窗实现loading效果,请把v-loading移到最外层元素即可。注意不能是el-dialog元素上,否则无法实现可能是el-dialog使用了teleport组件,导致v-loading无法正常工作。等有空研究一下 ~

  
            
                    
      
interface PropsType { loading?: boolean;}const props = withDefaults(defineProps(), { loading: false,});

登录后复制

试试看中间的loading效果

4.gif

剩下一些细节处理

在el-dialog组件提供了很多个props属性供用户选择,但我们现在封装的dialog组件只使用到了一小部分props属性。当用户想要使用其他的props属性时该怎么办?

例如使用width属性时,难道要在我们封装的组件中接收props.width再传递给组件吗?

不不不,还有另外一种方法,还记得刚刚在做全屏操作的时候使用到的useAttrs辅助函数吗

它可以获取当前组件传递进来的属性。有了这个方法之后,再配合并即可将外部传递进来的函数再传递到el-dialog组件上面啦

    

登录后复制

为了避免内部传递的props被覆盖掉,v-bind=”attrs”需要放在最前面

在使用时,可能会给before-close属性传递一个函数,但到了后面被内部的handleClose方法给覆盖掉了。

解决方案是在handleClose函数中,获取attrs.[‘before-close’]属性,如果类型是函数函数,先执行它。

const handleClose = () => {  if (    Reflect.has(attrs, "before-close") &&    typeof attrs["before-close"] === "function"  ) {    attrs["before-close"]();  }  emits("close");};

登录后复制

有关于el-dialog组件的封装就到这里了

封装hooks

利用Vue composition Api再封装一下在使用el-dialog组件状态的管理hook

useDialog

简单处理显示和加载态开关的hook

import { ref } from "vue";export default function useDialog() {  const visible = ref(false);  const loading = ref(false);  const openDialog = () => (visible.value = true);  const closeDialog = () => (visible.value = false);  const openLoading = () => (loading.value = true);  const closeLoading = () => (loading.value = false);  return {    visible,    loading,    openDialog,    closeDialog,    openLoading,    closeLoading,  };}

登录后复制

useDialog Demo

5.gif

普通弹窗  

DialogCmp1

import useDialog from "./components/useDialog";import DialogCmp from "./components/Dialog.vue";const { visible: visible1, openDialog: openDialog1, closeDialog: closeDialog1,} = useDialog();

登录后复制

useDialogState 和 useDialogWithForm

useDialogState

针对开发管理后台弹窗状态封装的一个hook,搭配下面的useDialogWithForm使用。

export enum MODE {  ADD,  EDIT,}

登录后复制

import { ref } from "vue";import { MODE } from "./types";export default function useDialogState() {  const mode = ref(MODE.ADD);  const visible = ref(false);  const updateMode = (target: MODE) => {    mode.value = target;  };  return { mode, visible, updateMode };}

登录后复制

useDialogWithForm

针对表单弹窗组件封装的hooks,接收一个formRef实例,负责控制弹窗内标题及清空表单中的校验结果,减少多余的代码 ~

import { FormInstance } from "element-plus";import { Ref, ref } from "vue";import { MODE } from "./types";import useDialogState from "./useDialogState";export default function useDialogFn(  formInstance: Ref) {  const { visible, mode, updateMode } = useDialogState();  const closeDialog = () => {    formInstance.value.resetFields();    visible.value = false;  };  const openDialog = (target: MODE) => {    updateMode(target);    visible.value = true;  };  return { visible, mode, openDialog, closeDialog };}

登录后复制

useDialogWithForm Demo

6.gif

                                                                        import { ElMessage, FormInstance } from "element-plus";import { Ref, ref } from "vue";import Dialog from "./Dialog.vue";import { MODE } from "./types";import useDialogWithForm from "./useDialogWithForm";const rules = {  name: {    type: "string",    required: true,    pattern: /^[a-z]+$/,    trigger: "change",    message: "只能是英文名称哦",    transform(value: string) {      return value.trim();    },  },  age: {    type: "string",    required: true,    pattern: /^[0-9]+$/,    trigger: "change",    message: "年龄只能是数字哦",    transform(value: string) {      return value.trim();    },  },  mobile: {    type: "string",    required: true,    pattern:      /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/,    trigger: "change",    message: "请输入正确的手机号码",    transform(value: string) {      return value.trim();    },  },};interface FromDataType {  name: string;  age: string;  mobile: string;}const formDataRef = ref(null);let formData = ref({  name: "",  age: "",  mobile: "",});const { visible, closeDialog, openDialog, mode } = useDialogWithForm(  formDataRef as Ref);const confirm = () => {  if (!formDataRef.value) return;  formDataRef.value.validate((valid) => {    if (valid) {      console.log("confirm");      ElMessage({        message: "提交成功",        type: "success",      });      closeDialog();    }  });};const customClose = () => {  ElMessage({    message: "取消提交",    type: "info",  });  closeDialog();};defineExpose({  closeDialog,  openDialog,});

登录后复制

仓库地址

vuejs视频教程

vuejs视频教程

7 (1).gif

如果您觉得本文对您有帮助,请帮帮忙点个star

您的反馈 是我更新的动力!

(学习视频分享:vuejs视频教程、vuejs视频教程)

以上就是聊聊Vue3+hook怎么写弹窗组件更快更高效的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/3230906.html

(0)
上一篇 2025年4月1日 16:46:04
下一篇 2025年4月1日 16:46:19

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • Vue3知识地图二:Vue生命周期函数与常用模板语法

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。本篇继续给大家分享关于vue生命周期函数与常用模板语法的思维导图,希望对大家有帮助!欢迎大家收藏分享学习! 系列文章:《Vue3知识地图一:学前了解与应用创…

    2025年4月5日
    200
  • Vue3知识地图三:Vue样式绑定语法与列表循环渲染

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。,本篇继续给大家分享vue样式绑定语法与列表循环渲染,希望对大家有帮助!欢迎大家收藏分享学习! 系列文章:《Vue3知识地图一:学前了解与应用创建》|《Vu…

    2025年4月5日
    100
  • Vue3知识地图四:事件绑定与双向绑定

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。本篇文章继续给大家分享vue知识点思维导图关于事件绑定与双向绑定,希望对大家有帮助!还行大家收藏学习! 系列文章:《Vue3知识地图一:学前了解与应用创建》…

    2025年4月5日
    100
  • Vue3知识地图五:组件相关语法

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。本篇文章继续给大家分享vue知识点思维导图关于组件相关语法,希望对大家有帮助!欢迎大家收藏学习! 系列文章:《Vue3知识地图一:学前了解与应用创建》|《V…

    2025年4月5日
    100
  • Vue3知识地图六:单项数据流与slot插槽

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。本篇文章继续给大家分享vue知识点思维导图关于单项数据流与slot插槽,希望对大家有帮助!欢迎大家收藏学习! 系列文章:《Vue3知识地图一:学前了解与应用…

    2025年4月5日
    100
  • Vue3知识地图七:高级语法之Mixin、directive、teleport与plugin插件

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。本篇文章继续给大家分享vue知识点思维导图关于高级语法之mixin、directive、teleport与plugin插件,希望对大家有帮助!欢迎大家收藏学…

    2025年4月5日
    200
  • Vue3知识地图八:Composition API相关函数

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。本篇文章继续给大家分享vue知识点思维导图关于高级语法之composition api相关函数,希望对大家有帮助!欢迎大家收藏学习! 系列文章:《Vue3知…

    2025年4月5日
    100
  • Vue3知识地图九:Vue配套工具之Vuecli与Router

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。本篇文章继续给大家分享vue知识点思维导图关于vue配套工具之vuecli与router,希望对大家有帮助!欢迎大家收藏学习! 系列文章:《Vue3知识地图…

    2025年4月5日
    100
  • Vue3知识地图十:VueX语法

    在之前的文章中给大家分享了vue的知识思维导图,如果没看过的话可以点击下方链接查看。本篇文章继续给大家分享vue知识点思维导图关于vuex语法,希望对大家有帮助!欢迎大家收藏学习! 系列文章:《Vue3知识地图一:学前了解与应用创建》|《V…

    2025年4月5日
    200
  • vue怎么获取dom元素

    vue获取dom元素的方法:1、给相应的dom元素加id,使用“document.getElementById(“id”)”语句获取该元素;2、给相应的dom元素加“ref=”name””,使…

    2025年4月5日 编程技术
    200

发表回复

登录后才能评论