Skip to content

组件通信与刷新实现方式对比

实现方式对比表

方式实现原理优点缺点适用场景代码复杂度
1. 父组件调用子组件方法<br/>(ref + defineExpose)父组件通过 ref 获取子组件实例,调用子组件暴露的方法• 直接明确<br/>• 类型安全(TypeScript)<br/>• 调用时机可控• 强耦合(父组件需要知道子组件方法名)<br/>• 需要维护 ref<br/>• 违反单向数据流原则• 需要精确控制调用时机<br/>• 子组件方法需要被外部调用⭐⭐⭐
2. 子组件监听父组件状态<br/>(watch props)父组件通过 props 传递状态变化,子组件 watch 监听• 符合单向数据流<br/>• 解耦(父组件只需更新状态)<br/>• 响应式自动触发<br/>• 易于测试• 需要设计合适的状态标识<br/>• 可能触发不必要的刷新• 状态驱动的刷新<br/>• 多个子组件需要同步刷新⭐⭐
3. 事件总线/全局状态<br/>(EventBus/Pinia)通过全局事件或状态管理触发刷新• 完全解耦<br/>• 跨组件通信<br/>• 支持一对多• 难以追踪数据流<br/>• 可能造成性能问题<br/>• 调试困难• 跨层级通信<br/>• 多个不相关组件需要同步⭐⭐⭐⭐
4. Provide/Inject<br/>(依赖注入)父组件 provide 刷新函数,子组件 inject 使用• 跨层级传递<br/>• 避免 prop drilling<br/>• 类型安全• 依赖关系不明确<br/>• 可能滥用<br/>• 调试困难• 深层嵌套组件<br/>• 多个子组件需要同一功能⭐⭐⭐
5. Props 传递回调函数<br/>(callback props)父组件传递回调函数,子组件在适当时机调用• 简单直接<br/>• 符合 React 模式<br/>• 易于理解• 需要传递函数引用<br/>• 可能造成不必要的重渲染• 简单的父子通信<br/>• 需要双向通信⭐⭐
6. 组合式函数返回刷新函数<br/>(composable pattern)通过 composable 返回刷新函数,父子组件共享• 逻辑复用<br/>• 统一管理<br/>• 易于测试• 需要合理设计 composable<br/>• 可能过度设计• 复杂的状态管理<br/>• 需要逻辑复用⭐⭐⭐

详细代码示例

方式 1: 父组件调用子组件方法 (ref + defineExpose)

父组件 (OrderTabs.vue)

vue
<template>
  <ClosedOrder ref="closeOrderRef" ... />
</template>

<script setup>
const closeOrderRef = ref();

const { refresh } = useClosePosition();

// 刷新时调用子组件方法
const handleRefresh = () => {
  refresh();
  closeOrderRef.value?.fetchCloseOrdersSum(); // 手动调用
};
</script>

子组件 (CloseOrder.vue)

vue
<script setup>
const fetchCloseOrdersSum = () => {
  fetchSum({ ... })
}

defineExpose({
  fetchCloseOrdersSum, // 暴露方法供父组件调用
})
</script>

方式 2: 子组件监听父组件状态 (当前实现) ✅

父组件 (OrderTabs.vue)

vue
<template>
  <ClosedOrder :refresh-key="closedOrderRefreshKey" ... />
</template>

<script setup>
const { refresh, refreshKey } = useClosePosition();

// 只需更新状态,子组件自动响应
const handleRefresh = () => {
  refresh(); // refreshKey 自动递增
};
</script>

子组件 (CloseOrder.vue)

vue
<script setup>
const props = defineProps<{
  refreshKey?: number
}>()

watch(
  () => props.refreshKey,
  (newKey, oldKey) => {
    if (newKey !== undefined && oldKey !== undefined && newKey !== oldKey) {
      fetchCloseOrdersSum() // 自动响应状态变化
    }
  },
)
</script>

方式 3: 事件总线/全局状态

使用 Pinia Store

typescript
// store/refresh.ts
export const useRefreshStore = defineStore("refresh", () => {
  const refreshKey = ref(0);

  const triggerRefresh = () => {
    refreshKey.value++;
  };

  return { refreshKey, triggerRefresh };
});

父组件

vue
<script setup>
const refreshStore = useRefreshStore();

const handleRefresh = () => {
  refreshStore.triggerRefresh();
};
</script>

子组件

vue
<script setup>
const refreshStore = useRefreshStore();

watch(
  () => refreshStore.refreshKey,
  () => {
    fetchCloseOrdersSum();
  }
);
</script>

方式 4: Provide/Inject

父组件

vue
<script setup>
const refreshKey = ref(0);

const triggerRefresh = () => {
  refreshKey.value++;
};

provide("refreshCloseOrder", {
  refreshKey: readonly(refreshKey),
  triggerRefresh,
});
</script>

子组件

vue
<script setup>
const { refreshKey, triggerRefresh } = inject('refreshCloseOrder')!

watch(
  () => refreshKey.value,
  () => {
    fetchCloseOrdersSum()
  },
)
</script>

方式 5: Props 传递回调函数

父组件

vue
<template>
  <ClosedOrder :on-refresh="handleRefresh" ... />
</template>

<script setup>
const handleRefresh = () => {
  // 父组件的刷新逻辑
  refresh();
};
</script>

子组件

vue
<script setup>
const props = defineProps<{
  onRefresh?: () => void
}>()

// 在需要时调用
const someAction = () => {
  props.onRefresh?.()
}
</script>

方式 6: 组合式函数返回刷新函数

Composable (useClosePosition.ts)

typescript
export function useClosePosition() {
  const refreshKey = ref(0);

  // 统一的刷新函数
  const refreshAll = () => {
    refreshKey.value++;
    // 可以在这里统一处理所有刷新逻辑
  };

  return {
    refreshKey,
    refreshAll,
    // ... 其他
  };
}

父组件

vue
<script setup>
const { refreshAll } = useClosePosition();

const handleRefresh = () => {
  refreshAll(); // 统一刷新
};
</script>

子组件

vue
<script setup>
// 可以共享同一个 composable 实例
// 或者通过 props 传递 refreshKey
</script>

推荐选择指南

🎯 当前场景推荐:方式 2 (子组件监听状态)

理由:

  • ✅ 符合 Vue 3 响应式设计理念
  • ✅ 单向数据流,易于维护
  • ✅ 父组件只需关注状态管理,无需关心子组件实现
  • ✅ 代码简洁,易于理解

📊 其他场景推荐

场景推荐方式理由
需要精确控制调用时机方式 1 (ref)直接调用,时机可控
跨层级、跨组件通信方式 3 (Store)全局状态,解耦彻底
深层嵌套组件方式 4 (Provide/Inject)避免 prop drilling
简单父子交互方式 5 (Callback)简单直接
复杂状态管理方式 6 (Composable)逻辑复用,统一管理

性能对比

方式性能开销重渲染次数内存占用
ref + defineExpose1 次
watch props1-2 次
EventBus/Store可能多次
Provide/Inject1 次
Callback Props可能多次
Composable1 次

总结

最佳实践建议:

  1. 优先使用方式 2(状态驱动):符合 Vue 3 设计理念,代码清晰
  2. 简单场景用方式 5(回调函数):直接明了
  3. 复杂场景用方式 6(Composable):逻辑复用,易于维护
  4. 跨组件用方式 3(Store):全局状态管理
  5. 避免过度使用方式 1(ref):除非确实需要精确控制

当前实现的优势:

  • 🎯 符合单向数据流原则
  • 🎯 父组件无需关心子组件内部实现
  • 🎯 响应式自动触发,无需手动调用
  • 🎯 易于测试和维护
  • 🎯 支持多个子组件同时响应

本站总访问