????Hi~ 大家好,我是小鑫同学,一位长期从事前端开发的编程爱好者,我将使用更为实用的案例输出更多的编程知识,同时我信奉分享是成长的唯一捷径,在这里也希望我的每一篇文章都能成为你技术落地的参考~

????技术&代码分享

  • 我在 IT200 总结技术学习;
  • 我在 1024Code 在线编写代码;
  • 我在 掘金 分享技术文章;
  • 我在 Github 参与开源学习;

????推荐几个好用的工具

进入正题

在各种场景的开发中Dialog组件的出现频率都是非常高的,Dialog组件作为一个容器组件受容器内业务代码复杂度的影响,代码行数、变量及函数的定义可能会很多,这样的组件就一定要考虑封装使用,以保证主流程代码的简洁。下面一起来看一下如何利用面向对象的思想来封装它吧~

技术选型

Vuejs3.x + Typescript + <script setup> 是目前编写SFC组件的推荐方式,相比直接使用setup函数编码体验会更好,AntdV组件库的使用频率相比ElementUI要高,推荐新项目直接选用AntdV,完整代码可以在1024Code直接Fork或导出到本地。

代码分析

下面的代码来自AntdV组件库Model组件的示例,Model需要一个visible属性来控制是否显示和隐藏,由buttonclick事件来触发修改visible属性显示,并且可以通过ok事件的触发来表示对Model内业务逻辑的认可。

从这个示例来看代码当然是没有拆分组件的必要的,当但代表主流程业务的button和由modal包裹的业务流程变得多起来的时候,script里面的代码可读性将大大降低。

<script setup lang="ts">
import { ref } from 'vue';

const visible = ref<boolean>(false);

const showModal = () => {
  visible.value = true;
};

const handleOk = (e: MouseEvent) => {
  console.log(e);
  visible.value = false;
};
</script>

<template>
  <a-button type="primary" @click="showModal">Open Modal</a-button>
  <a-modal v-model:visible="visible" title="Basic Modal" @ok="handleOk">
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
  </a-modal>
</template>

<style scoped>
</style>

普通的组件拆分

常规的组件拆分大家按习惯来做一定是考虑通过Props传递visible参数来控制子组件中Model组件的显示,但是你是没有办法直接将Propsvisible属性绑定到Model,因为在关闭弹窗是涉及到了修改变量,这样的做法在Vuejs中认为是危险的(父子组件单向数据流),所以必须要通过子组件重新定义变量来接收后使用;

另一个问题就是父组件在给子组件传visible参数时在父组件也有属性定义,这个属性在改为true后同样因单向数据流的限制是无法在Model关闭时动态变更为false,这样的Model就只能被打开一次,合理的解决范式就是通过emit在Model关闭时通知(回调)父组件,由父组件主动修改它定义的变量。

这样拆分组件不仅定义了多份控制显示/隐藏的变量,并且还需要在回调中专门处理,这给组件的开发和使用都造成了一定的麻烦,所以我不推荐这种拆分方式。

面向对象拆分组件

面向对象的第一大概念就是封装,所以只要涉及封装就完全可以用面向对象的思想来做,在案例中控制显示隐藏的visible属性和ok事件都是属于这个组件对象自己的,只能定义在子组件中。操作Model显示隐藏的方式不能通过Props传递,同样需要由组件对象自己提供,在使用了<script setup>后,组件默认为关闭状态,需要使用defineExpose函数将需要暴露的函数暴露给父组件使用。前面提到的emit在这里还是比不可少了,但是仅仅是作为将业务处理的结果传递给父组件。

<script setup lang="ts">
import { ref, defineExpose, defineEmits } from 'vue'

const visible = ref<boolean>(false);

const open = () => visible.value = true;
const close = () => visible.value = false;

defineExpose({
  open,
})

const emit = defineEmits<{
  (e: 'complete', code: number): void
}>()

const handleOk = (_) => {
  close();
  emit('complete', 200);
};
</script>

<template>
  <a-modal v-model:visible="visible" title="Basic Modal" @ok="handleOk">
    <p>Some contents...</p>
    <p>Some contents...</p>
    <p>Some contents...</p>
  </a-modal>
</template>

<style scoped>
</style>

面向对象使用组件

在面向对象封装时并不需要过多的考虑显示隐藏状态变化的传递,属于组件本身的属性、函数有组件自己控制包括是否对外暴露等,那么在使用的时候也是非常简洁的。在父组件中只关注Model什么时候需要打开和关闭后回调的数据处理即可。

<script setup lang="ts">
import { ref } from 'vue';
import BusinessDialog from './components/BusinessDialog.vue';

const businessDialogRef = ref<InstanceType<typeof BusinessDialog>>();

const showModal = () => {
  businessDialogRef.value?.open();
}

const onComplete = (e: any) => {
  console.log(e);
}
</script>

<template>
  <a-button type="primary" @click="showModal">Open Modal</a-button>
  <BusinessDialog ref="businessDialogRef" @complete="onComplete" />
</template>

<style scoped>
</style>

总结

文中介绍的关于DialogComponent组件的封装思想是我所推荐的,因为它不仅开发子组件是不需要过多外部的干扰,在使用子组件是也不需要过多的考虑,这也是封装的意义所在,在其中使用到了defineExpose()defineEmits(),对于很少使用<script setup>方式的小伙伴也是不熟悉的,可以通过Vuejs文档学习,最后在父组件定义子组件的ref时使用的InstanceType属于Typescript中的类型编程,感兴趣的也可以了解一下。好了,今天就说这么多,下次见~

PS:完整代码见1024Code


如果看完觉得有收获,欢迎点赞、评论、分享支持一下。你的支持和肯定,是我坚持写作的动力~