Vue 3 中父组件与子组件的通信以打开 el-dialog 的最佳方式是什么?

父组件打开子组件的el-dialog的方式?

方法一、V-model

// Parent.vue
<script setup lang="ts">
import { ref, version as vueVersion } from 'vue'
import { version as epVersion } from 'element-plus'
import { ElementPlus } from '@element-plus/icons-vue'
import Child from "./Child.vue"
const show = ref(false);
</script>

<template>
  <el-button @click="show=true">show</el-button>
  <Child v-model:show="show" />
</template>
// Child.vue
<script setup>
  import {defineModel} from "vue"
  const show = defineModel("show");
</script>

<template>
  <el-dialog v-model="show">
    This is model
  </el-dialog>
</template>

方法二、ref + defineExpose

// parent.vue
<script setup lang="ts">
import { ref, version as vueVersion } from 'vue'
import { version as epVersion } from 'element-plus'
import { ElementPlus } from '@element-plus/icons-vue'
import Child from "./Child.vue"
const childRef = ref(null);
function open() {
  childRef.value.open();
}
</script>

<template>
  <el-button @click="open()">show</el-button>
  <Child ref="childRef"/>
</template>
// Child.vue
<script setup>
  import {defineModel, ref, onMounted, defineExpose} from "vue"
  const show = ref(false);
  function open() {
    show.value = true;
  }
  defineExpose({open})
</script>

<template>
  <el-dialog v-model="show">
    This is model
  </el-dialog>
</template>
  • 还有其他方式吗?
  • 哪种方法是最好的(或者说最佳实践)?
阅读 3.7k
avatarAI BotBETA

Vue 3 中父组件与子组件的通信以打开 el-dialog 的最佳方式

方法分析

方法一、V-model

在 Vue 3 的 Composition API 中,并没有直接提供 defineModel 这样的API来定义一个响应式属性作为模型(v-model 的实现)。实际上,在子组件中直接使用 v-model:propName 时,Vue 会期望子组件接收一个 propNameprops 并发出一个 update:propName 的事件。

正确的方式 是子组件应该使用 propsemits 来与父组件通信。

// Child.vue
<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  show: Boolean
})

const emit = defineEmits(['update:show'])

function toggleDialog() {
  emit('update:show', !props.show)
}
</script>

<template>
  <el-dialog v-model="props.show">
    <span slot="footer" class="dialog-footer">
      <el-button @click="toggleDialog">取 消</el-button>
      <el-button type="primary" @click="toggleDialog">确 定</el-button>
    </span>
    This is a dialog
  </el-dialog>
</template>

方法二、ref + defineExpose

使用 refdefineExpose 是 Vue 3 Composition API 中推荐的方式来暴露子组件中的方法或属性给父组件。这是父组件直接控制子组件内部状态的一个常用手段。

你的示例 Child.vue 已经展示了这种方式,但父组件中的 open 方法应该调用子组件暴露的 open 方法,而不是直接修改子组件的 show 引用(尽管这在 Vue 3 的 ref 中是可行的,但通常不是最佳实践)。

还有其他方式吗?

是的,还有其他方式可以实现父组件和子组件的通信,例如:

  • 使用 provideinject API 实现依赖注入。
  • 使用 Vuex 或 Pinia 这样的状态管理库。
  • 使用 Event Bus 或 Vue 3 的 mitt 库等发布订阅模式。
  • 使用 Vue Router 的导航守卫等(如果通信与路由相关)。

哪种方法是最好的(或者说最佳实践)?

最佳实践 取决于你的具体需求和上下文。

  • 如果 el-dialog 的显示状态完全由父组件控制,并且你希望子组件是“无状态”的,那么使用 v-model(通过 propsemits 实现)是合适的。
  • 如果子组件需要有自己的状态逻辑,并且父组件需要能够影响这些状态,那么使用 refdefineExpose 是合适的。
  • 如果你的应用状态管理很复杂,考虑使用 Vuex 或 Pinia。

在大多数简单的父子组件通信场景中,使用 v-model(通过 propsemits)或 refdefineExpose 就足够了。选择哪种方式取决于你的具体需求和代码的可读性、可维护性。

3 个回答

如果是封装的通用组件,比如说UI库。那么推荐第一种的方式(使用 props + emit 的思路)。只传入 show 属性,然后再按照具体的情况使用 emit 抛出一些 on-showon-close 等等的事件。

如果是基于UI组件库再次封装的业务弹窗组件,那么我推荐的是类似第二种的方式。子组件暴露出来 open() 方法和 close() 等方法在父级调用,并且在子组件内自己操作修改 show 属性和一些其他业务(在打开弹窗的同时很可能需要配合做一些业务处理,比如说一些表单内组件的初始化操作)。

主要看业务,我这边业务基本按第二种搞,dialog一般用于详情页展示或者表单填写。提交或者关闭多半都有刷新父组件需求。
父组件ref 与 @ 我个人觉得一眼看过去清晰。

<Child ref="childRef" @getData='xxxx'/>

然后给子组件传递参数,就我个人习惯这样

<el-button @click="open(row.id)">show</el-button>
function open(id) {
      childRef.value.open(id);
}

总之,还是看业务与个人习惯趋向哪种。

我一直用的是第一种方案,前段时间AI了下,觉得这个回答还不错,可以参考下。

61ddb437351c6e2c2dbd52be19c8fc5.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题