vue3专题1------父组件中更改子组件的属性

理解 Vue 3 中父组件如何引用子组件的属性是一个很重要的概念。 这里涉及到 defineExpose 和 ref 这两个关键点。

方法:使用 defineExpose 在子组件中暴露属性,然后在父组件中使用 ref 获取子组件实例并访问暴露的属性。

下面我将详细解释这个过程:

1. 子组件 (Child Component):

  • 创建 Ref: 首先,在子组件中使用 ref 创建你想要暴露的属性。
  • 使用 defineExpose 暴露: 使用 defineExpose 显式声明你想让父组件访问的属性。只有通过 defineExpose 暴露的属性才能被父组件访问。
python 复制代码
// ChildComponent.vue

<template>
  <div>
    <p>子组件 Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>


<script setup>
import { ref } from 'vue';

const count = ref(0);

const increment = () => {
    count.value++;
};

// 暴露 count 和 increment 方法给父组件

defineExpose({
    count,
    increment
})

</script>

说明:

  • defineExpose 接受一个对象,该对象包含你想暴露给父组件访问的属性和方法。
  • 在上面的例子中,我们通过defineExpose 暴露了 count (ref 对象) 和 increment 方法,否则父组件也无法访问它。

2. 父组件 (Parent Component):

  • Template Ref: 在父组件的模板中,使用 ref 特殊 attribute 给子组件命名,以便稍后获取子组件的实例。
  • 获取子组件实例: 在父组件中,创建一个与模板中 ref 特殊 attribute 同名的 ref。 当组件挂载后,这个 ref 将会持有子组件的实例。
  • 访问暴露的属性:onMounted 生命周期钩子中,访问子组件实例上的暴露属性。 需要在组件挂载后才能访问子组件的实例。
python 复制代码
// ParentComponent.vue
<template>
  <div>
    <ChildComponent ref="childComponentRef" />
    <p>父组件 Count: {{ childCount }}</p>
    <button @click="incrementChild">Increment Child</button>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';

const childComponentRef = ref(null); //  初始值必须为 null
const childCount = ref(0);

onMounted(() => {
  // 确保 childComponentRef.value 存在
  if (childComponentRef.value) {
    // 访问子组件暴露的 count 属性
    childCount.value = childComponentRef.value.count.value;

    // 可以调用子组件暴露的方法
  } else {
    console.warn('Child component ref is not yet available.');
  }
});

const incrementChild = () => {
  if (childComponentRef.value) {
    childComponentRef.value.increment(); // 调用子组件的方法
    childCount.value = childComponentRef.value.count.value; // 更新父组件的 count
  }
};

</script>

说明:

  • childComponentRef 的初始值必须为 null
  • onMounted 钩子是至关重要的。在这个钩子被调用时,子组件已经被挂载,childComponentRef.value 将指向子组件的实例。
  • 通过 childComponentRef.value.count.value 访问子组件暴露的 count 属性。 注意,由于 count 本身是一个 ref,所以需要使用 .value 两次才能获取其值。
  • 通过 childComponentRef.value.increment() 调用子组件暴露的 increment 方法。
  • 在访问子组件的属性或方法之前,最好检查 childComponentRef.value 是否存在,以避免错误。

总结:

  1. 子组件: 使用 ref 创建响应式属性,并使用 defineExpose 暴露要给父组件访问的属性和方法。
  2. 父组件: 在模板中使用 ref 特殊 attribute 引用子组件,并在 setup 中创建同名的 ref。 在 onMounted 钩子中,通过 ref.value 访问子组件的实例,并访问暴露的属性和方法。

重要提示:

  • 单向数据流: 虽然可以从父组件访问子组件的属性,但应该尽量避免直接修改子组件的状态。 更好的做法是让子组件通过 emit 触发事件,父组件监听这些事件并做出相应的更新。 这有助于维护清晰的单向数据流。
  • 避免过度暴露: 只暴露父组件真正需要的属性和方法。 避免暴露不必要的内部状态,以保持子组件的封装性。

注意:在父组件修改子组件的属性时,通常推荐暴露方法,而不是直接暴露属性

理由:

1. 维护单向数据流:

  • 暴露属性 (不推荐): 直接暴露子组件的属性给父组件修改,容易打破单向数据流。 父组件可以随意更改子组件的状态,导致子组件的状态变化不可预测,增加了调试和维护的难度。 这就像父组件直接"篡改"了子组件的数据,子组件可能不了解发生了什么变化。

  • 暴露方法 (推荐): 通过暴露方法,子组件可以控制如何修改其内部状态。 父组件调用这些方法来请求子组件进行更新。 这种方式保持了清晰的数据流方向:父组件 "请求" 子组件修改数据,子组件 "响应" 请求并执行修改。 子组件可以验证传入的数据、执行一些副作用操作 (例如,在数据更改后更新本地存储) 等,从而更好地控制自身的状态。

2. 更好的封装性:

  • 暴露属性: 直接暴露属性会破坏子组件的封装性。 父组件需要了解子组件内部的属性结构和类型,这增加了父组件和子组件之间的耦合度。 如果子组件内部的属性发生变化,可能需要修改父组件的代码。

  • 暴露方法: 通过暴露方法,子组件可以隐藏其内部的实现细节。 父组件只需要知道如何调用这些方法,而不需要了解子组件内部是如何存储和处理数据的。 这样可以更容易地修改子组件的内部实现,而不会影响到父组件。

3. 更好的控制和验证:

  • 暴露属性: 父组件可以直接设置属性值,没有机会进行验证或控制。

  • 暴露方法: 子组件可以在方法内部对传入的参数进行验证,确保数据的有效性。 此外,子组件还可以在方法内部执行一些额外的逻辑,例如触发自定义事件或更新其他相关的状态。

示例:

假设子组件有一个 count 属性,父组件想要增加它的值。

推荐 (暴露方法):

python 复制代码
// ChildComponent.vue
<script setup>
import { ref } from 'vue';

const count = ref(0);

const increment = (amount = 1) => {
  count.value += amount;
};

defineExpose({
  increment, // 暴露 increment 方法
});

</script>

// ParentComponent.vue
<script setup>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';

const child = ref(null);
onMounted(() => {
  child.value.increment(5); // 调用子组件的 increment 方法
});

</script>

总结:

在设计组件时,应该尽量遵循单向数据流的原则,提高组件的封装性和可维护性。 因此,在父组件需要修改子组件状态的情况下,推荐暴露方法 (actions),而不是直接暴露属性。 这使得子组件可以更好地控制其内部状态,并提供更大的灵活性。

当然,在非常简单的情况下,如果父组件只需要读取子组件的状态,并且不需要进行任何修改,那么暴露属性是可以接受的。 但总的来说,暴露方法是更安全、更灵活、更推荐的做法

相关推荐
BillKu1 小时前
Vue3 + TypeScript中provide和inject的用法示例
javascript·vue.js·typescript
培根芝士1 小时前
Electron打包支持多语言
前端·javascript·electron
Baoing_2 小时前
Next.js项目生成sitemap.xml站点地图
xml·开发语言·javascript
mr_cmx2 小时前
Nodejs数据库单一连接模式和连接池模式的概述及写法
前端·数据库·node.js
a东方青2 小时前
vue3学习笔记之属性绑定
vue.js·笔记·学习
东部欧安时2 小时前
研一自救指南 - 07. CSS面向面试学习
前端·css
沉默是金~2 小时前
Vue+Notification 自定义消息通知组件 支持数据分页 实时更新
javascript·vue.js·elementui
涵信2 小时前
第十二节:原理深挖-React Fiber架构核心思想
前端·react.js·架构
在下千玦2 小时前
#去除知乎中“盐选”付费故事
javascript
ohMyGod_1232 小时前
React-useRef
前端·javascript·react.js