一、组件通信的本质与场景分类
组件通信是指不同组件间的数据传递与状态同步,常见场景包括:
- 父子组件通信(单向数据流)
- 兄弟组件通信(跨级传递)
- 全局状态共享(任意组件间)
二、主流框架的通信方案对比
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 触发自定义事件 |
| 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 等。
- 方案1:使用 Context API(简单场景):
2. 问:Vue 3 中 provide/inject 和 Vuex 的区别?
- 答:
维度 provide/inject Vuex 响应式 非响应式(需手动传递 ref) 完全响应式 作用范围 局部(组件树) 全局(整个应用) 复杂度 低(简单场景) 高(复杂状态管理) 适用场景 跨级组件简单数据传递 全局状态共享、复杂数据流
3. 问:如何选择合适的组件通信方案?
- 答:
- 父子组件:优先使用 props/回调函数(React)或 props/$emit(Vue);
- 跨级组件:
- 少量数据:Context API(React)或 provide/inject(Vue);
- 大量数据:状态管理库(Redux、Vuex);
- 解耦需求:事件总线或发布-订阅模式;
- 性能敏感:考虑使用
useMemo(React)或computed(Vue)优化。
4. 问:事件总线有什么优缺点?
- 答:
- 优点:简单易用,适合快速实现组件间通信;
- 缺点:
- 事件名全局管理,易冲突;
- 组件间依赖关系不明确,维护困难;
- 大规模应用中可能导致事件混乱(推荐使用状态管理库替代)。
五、最佳实践与性能优化
-
单向数据流原则
- 数据流向应清晰(如父 → 子通过 props,子 → 父通过回调);
- 避免双向绑定(如 React 推荐受控组件,Vue 推荐
.sync修饰符)。
-
状态提升
- 将共享状态提升到最近的共同父组件,通过 props 传递数据和回调:
// 父组件 const [count, setCount] = useState(0); <Child increment={() => setCount(count + 1)} /> // 子组件 props.increment();
- 将共享状态提升到最近的共同父组件,通过 props 传递数据和回调:
-
性能优化技巧
- React:使用
React.memo避免不必要的重渲染; - Vue:使用
v-once或:key稳定组件实例; - 状态库:使用 selector 缓存计算结果(如 Redux 的
reselect)。
- React:使用