组件通信

54 阅读2分钟

一、组件通信的本质与场景分类

组件通信是指不同组件间的数据传递与状态同步,常见场景包括:

  • 父子组件通信(单向数据流)
  • 兄弟组件通信(跨级传递)
  • 全局状态共享(任意组件间)

二、主流框架的通信方案对比

1. React 组件通信
方案适用场景实现方式
props/回调函数父子组件父 → 子:通过 props 传递数据
子 → 父:通过 props 传递回调函数
Context API跨级组件(祖孙)createContext + Provider + useContext
状态管理库全局状态Redux(单向数据流)、MobX(响应式)、Zustand(轻量)
事件总线(Event Bus)任意组件间(低耦合)自定义事件系统(发布-订阅模式)
useImperativeHandle父组件获取子组件引用结合 forwardRef 暴露子组件方法

示例(React Context)

// 创建 Context
const UserContext = createContext();

// 父组件
<UserContext.Provider value={{ user: 'Alice' }}>
  <ChildComponent />
</UserContext.Provider>

// 子组件
const { user } = useContext(UserContext);
2. Vue 组件通信
方案适用场景实现方式
props/$emit父子组件父 → 子:通过 props 传递数据
子 → 父:通过 $emit 触发自定义事件
parent/parent/children父子组件(直接访问实例)不推荐(强耦合),仅适用于简单场景
$refs父组件获取子组件引用通过 ref 属性访问子组件实例
Event Bus任意组件间全局事件总线(new Vue() 作为事件中心)
Vuex/Pinia全局状态集中式状态管理(单向数据流)
provide/inject跨级组件(非响应式)父组件 provide,后代组件 inject

示例(Vuex)

// Store
const store = createStore({
  state: { count: 0 },
  mutations: { increment(state) { state.count++; } }
});

// 组件
this.$store.commit('increment'); // 修改状态
this.$store.state.count; // 获取状态

三、跨框架通用方案

1. 事件总线(Event Bus)
  • 原理:全局事件中心,组件间通过发布-订阅模式通信。
  • 示例(JavaScript)
    class EventEmitter {
      constructor() {
        this.events = {};
      }
      on(event, callback) {
        this.events[event] = (this.events[event] || []).concat(callback);
      }
      emit(event, data) {
        (this.events[event] || []).forEach(fn => fn(data));
      }
    }
    
    // 使用
    const bus = new EventEmitter();
    
    // 组件A:发布事件
    bus.emit('userUpdated', { name: 'Alice' });
    
    // 组件B:订阅事件
    bus.on('userUpdated', user => console.log(user));
    
2. 全局状态管理库
  • Redux(React/Vue通用)
    // 定义 action
    const increment = () => ({ type: 'INCREMENT' });
    
    // 定义 reducer
    const counterReducer = (state = 0, action) => {
      switch (action.type) {
        case 'INCREMENT': return state + 1;
        default: return state;
      }
    };
    
    // 创建 store
    const store = createStore(counterReducer);
    
    // 组件中使用
    store.dispatch(increment()); // 触发 action
    store.subscribe(() => console.log(store.getState())); // 监听变化
    
3. 发布-订阅模式(PubSub)
  • 与 Event Bus 类似,但更强调解耦:
    // 订阅者
    const subscriberId = pubSub.subscribe('message', (data) => {
      console.log('Received:', data);
    });
    
    // 发布者
    pubSub.publish('message', { content: 'Hello' });
    
    // 取消订阅
    pubSub.unsubscribe(subscriberId);
    

四、问题

1. 问:React 中如何实现跨级组件通信?
    • 方案1:使用 Context API(简单场景):
      // 创建 Context
      const ThemeContext = createContext();
      
      // 顶层组件
      <ThemeContext.Provider value="dark">
        <Child />
      </ThemeContext.Provider>
      
      // 深层子组件
      const theme = useContext(ThemeContext);
      
    • 方案2:使用状态管理库(复杂场景):Redux、Zustand 等。
2. 问:Vue 3 中 provide/inject 和 Vuex 的区别?
  • 维度provide/injectVuex
    响应式非响应式(需手动传递 ref)完全响应式
    作用范围局部(组件树)全局(整个应用)
    复杂度低(简单场景)高(复杂状态管理)
    适用场景跨级组件简单数据传递全局状态共享、复杂数据流
3. 问:如何选择合适的组件通信方案?
    • 父子组件:优先使用 props/回调函数(React)或 props/$emit(Vue);
    • 跨级组件
      • 少量数据:Context API(React)或 provide/inject(Vue);
      • 大量数据:状态管理库(Redux、Vuex);
    • 解耦需求:事件总线或发布-订阅模式;
    • 性能敏感:考虑使用 useMemo(React)或 computed(Vue)优化。
4. 问:事件总线有什么优缺点?
    • 优点:简单易用,适合快速实现组件间通信;
    • 缺点
      • 事件名全局管理,易冲突;
      • 组件间依赖关系不明确,维护困难;
      • 大规模应用中可能导致事件混乱(推荐使用状态管理库替代)。

五、最佳实践与性能优化

  1. 单向数据流原则

    • 数据流向应清晰(如父 → 子通过 props,子 → 父通过回调);
    • 避免双向绑定(如 React 推荐受控组件,Vue 推荐 .sync 修饰符)。
  2. 状态提升

    • 将共享状态提升到最近的共同父组件,通过 props 传递数据和回调:
      // 父组件
      const [count, setCount] = useState(0);
      <Child increment={() => setCount(count + 1)} />
      
      // 子组件
      props.increment();
      
  3. 性能优化技巧

    • React:使用 React.memo 避免不必要的重渲染;
    • Vue:使用 v-once:key 稳定组件实例;
    • 状态库:使用 selector 缓存计算结果(如 Redux 的 reselect)。