Vue Expose API

212 阅读2分钟

在 Vue 3 中,可以通过 defineExpose 编译器宏显式暴露组件内部的属性或方法,使父组件能够通过模板引用(ref)直接调用子组件的 API。以下是具体实现方式:


1. 子组件暴露 API

在子组件中使用 <script setup> 语法时,直接通过 defineExpose 暴露需要对外开放的属性和方法:

<!-- ChildComponent.vue -->
<template>
  <div>当前计数: {{ count }}</div>
</template>

<script setup>
import { ref } from 'vue'
// 需要暴露出去的的内部状态
const count = ref(0)
// 需要暴露的方法
const increment = () => {
  count.value++
}
  
// 暴露给父组件的 API
defineExpose({
  count,
  increment
})
</script>


2. 父组件调用子组件 API

在父组件中通过 ref 获取子组件实例,并调用其暴露的 API:

<template>
  <ChildComponent ref="childRef" />
  <button @click="handleClick">增加</button>
</template>

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

// 获取子组件的引用
const childRef = ref(null)

// 调用子组件里的 increment() 方法
const handleClick = () => {
  childRef.value.increment()
  console.log('子组件的 count:', childRef.value.count)
}
</script>


关键注意事项

  1. 暴露内容控制
    • 只有通过 defineExpose 显式暴露的属性和方法才能被外部访问。
    • 未暴露的内容即使存在于组件实例中,父组件也无法直接调用。
  2. 类型安全
    • 建议使用 TypeScript 接口明确定义暴露的 API 类型:
interface ChildComponentAPI {
  count: number
  increment: () => void
    }

  defineExpose<ChildComponentAPI>({ count, increment })
  1. 响应式更新
    • 如果暴露的是响应式对象(如 refreactive),父组件获取的将是响应式数据的引用,会自动保持同步。
  2. 生命周期限制
    • 只能在组件挂载后(如 onMounted 钩子)调用子组件 API,确保子组件已完成初始化。

**替代方案:使用 **provide/inject

如果需要在深层嵌套的组件间共享 API,可以考虑组合式 API 的 provide/inject

<!-- 祖先组件 -->
<script setup>
import { provide } from 'vue'

const sharedMethod = () => { /* ... */ }
provide('sharedMethod', sharedMethod)
</script>
<!-- 后代组件 -->
<script setup>
import { inject } from 'vue'
const sharedMethod = inject('sharedMethod')
</script>

选择 defineExpose 还是 provide/inject 取决于具体场景:前者适合父子直接通信,后者适合跨层级通信。