Vue学习笔记集--defineExpose
defineExpose
在 Vue 3 的 <script setup>
语法中,defineExpose
是一个用于显式暴露组件内部属性或方法的编译器宏。它的核心作用是控制子组件对外暴露的内容,使父组件能够通过 ref
访问这些暴露的属性和方法。以下是详细解析:
为什么需要 defineExpose
?
在 Vue 3 的 Composition API 中,使用 <script setup>
的组件默认不会暴露任何内部状态给父组件。
这意味着父组件通过 ref
获取子组件实例时,无法直接访问子组件内部的变量或方法。
defineExpose
的作用就是明确告诉 Vue:哪些属性和方法允许被外部访问。
基本用法
1. 子组件暴露内容
<!-- Child.vue -->
<script setup>
import { ref } from 'vue';
// 内部数据(默认对外不可见)
const privateData = ref('内部秘密');
// 对外暴露的数据和方法
const publicData = ref('公开数据');
const publicMethod = () => {
console.log('公共方法被调用');
};
// 显式暴露 publicData 和 publicMethod
defineExpose({
publicData,
publicMethod
});
</script>
2. 父组件访问暴露的内容
<!-- Parent.vue -->
<template>
<Child ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const childRef = ref(null);
const callChildMethod = () => {
// 访问子组件暴露的属性和方法
console.log(childRef.value.publicData); // '公开数据'
childRef.value.publicMethod(); // 输出 '公共方法被调用'
// 以下代码会报错(未暴露的 privateData)
// console.log(childRef.value.privateData);
};
</script>
核心特性
1. 选择性暴露
- 只有通过
defineExpose
显式暴露的内容,父组件才能访问。 - 未暴露的属性和方法会被 Vue 自动屏蔽,避免意外访问。
2. 支持响应式数据
-
如果暴露的是
ref
或reactive
对象,父组件访问时会保持响应性。// 子组件 const count = ref(0); defineExpose({ count }); // 父组件 childRef.value.count++; // 修改会影响子组件内部状态
3. 暴露函数
-
可以暴露方法供父组件调用(如触发子组件的特定操作)。
// 子组件 const resetForm = () => { /* 重置表单逻辑 */ }; defineExpose({ resetForm }); // 父组件 childRef.value.resetForm(); // 调用子组件方法
使用场景
1. 表单组件方法调用
子组件封装表单逻辑,暴露 submit
或 validate
方法供父组件调用:
<!-- ChildForm.vue -->
<script setup>
const validate = () => { /* 表单验证逻辑 */ };
defineExpose({ validate });
</script>
<!-- Parent.vue -->
<template>
<ChildForm ref="formRef" />
<button @click="submit">提交</button>
</template>
<script setup>
const submit = () => {
formRef.value.validate(); // 调用子组件的验证方法
};
</script>
2. 控制子组件状态
父组件需要直接修改子组件的数据(如重置计数器):
<!-- ChildCounter.vue -->
<script setup>
const count = ref(0);
const reset = () => { count.value = 0; };
defineExpose({ count, reset });
</script>
<!-- Parent.vue -->
<template>
<ChildCounter ref="counterRef" />
<button @click="counterRef.count = 0">重置</button>
</template>
注意事项
1. 避免过度暴露
- 只暴露必要的属性和方法,保持组件封装性。
- 不要暴露内部实现细节(如私有状态
privateData
)。
2. 类型安全(TypeScript)
-
为暴露的内容定义类型,增强代码可维护性:
// 子组件 const publicData = ref<string>('Hello'); defineExpose({ publicData: publicData as Ref<string> }); // 父组件 const childRef = ref<{ publicData: Ref<string> }>();
3. 组合式函数中不可用
-
defineExpose
只能在<script setup>
中使用,普通<script>
或组合式函数需改用expose
选项:// 非 <script setup> 写法 export default { setup(props, { expose }) { const data = ref(0); expose({ data }); // 替代 defineExpose return { data }; } };