前端进阶能力 - 组件通信 - 图1

前言

上一章节我们一起学习了通过SDK的设计,了解了 SDK 设计中的事件监听模型,同样 EventEmitter 也可以作为中介者模型来实现组件之间的跨级通信。
本章节我们就一起来学习几种通用的组件通信模型, 同时通过中介者模型实现组件元素之间跨级通信。

开箱即用的源码

源码地址:https://github.com/dkypooh/front-end-develop-demo/tree/master/base/babel7-react

组件通信模式

本小节以 React 组件框架为例,和大家一起探讨学习几种通用的组件通信模型。
前端进阶能力 - 组件通信 - 图2

  • 父组件到子组件: props 属性 , instance methods 实例方法
  • 子组件到父组件: callback回调方法, event bubbles 事件冒泡
  • 临近兄弟节点: Parent Component 父组件
  • 任何节点: Observer Pattern 观察者模式, Global Variables 全局变量, Context React执行上下文

父组件到子组件

Props

Props 是一个父节点到一个子节点通信最常见的方式。

  1. class Welcome extends React.Component {
  2. render() {
  3. return <h1>Hello, {this.props.name}</h1>;
  4. }
  5. }
  1. ReactDOM.render(
  2. <Welcome name="Sara" />,
  3. document.getElementById('root')
  4. );

Instance Methods

父组件可以使用refs调用子组件上的实例方法

  1. class TheChild extends React.Component {
  2. myFunc() {
  3. return "hello";
  4. }
  5. }
  1. class TheParent extends React.Component {
  2. render() {
  3. return (
  4. <TheChild
  5. ref={foo => {
  6. this.foo = foo;
  7. }}
  8. />
  9. );
  10. }
  11. componentDidMount() {
  12. var x = this.foo.myFunc();
  13. // x is now 'hello'
  14. }
  15. }

子组件到父组件

Callback Functions

最简单的方法是父组件将函数传递给子组件,子组件调用父组件的方法
以属性的方式传递函数

  1. <MyChild myFunc={this.handleChildFunc} />

子组件调用方式

  1. this.props.myFunc();

添加属性函数声明

  1. MyChild.propTypes = {
  2. myFunc: React.PropTypes.func
  3. };

Event Bubbling

事件冒泡不是 React 的概念,它是DOM元素的概念,它可以像回调函数一样通过冒泡方式把数据通子组件发送到父组件。

  1. class ParentComponent extends React.Component {
  2. render() {
  3. return (
  4. <div onKeyUp={this.handleKeyUp}>
  5. // Any number of child components can be added here.
  6. </div>
  7. );
  8. }
  9. handleKeyUp = (event) => {
  10. // This function will be called for the 'onkeyup'
  11. // event in any <input/> fields rendered by any of
  12. // my child components.
  13. }
  14. }

临近兄弟节点

父组件通信

如果两个相邻组件需要通信, 我们可以找到他们相邻的父节点进行中转通信

  1. class ParentComponent extends React.Component {
  2. render() {
  3. return (
  4. <div>
  5. <SiblingA
  6. myProp={this.state.propA}
  7. myFunc={this.siblingAFunc}
  8. />
  9. <SiblingB
  10. myProp={this.state.propB}
  11. myFunc={this.siblingBFunc}
  12. />
  13. </div>
  14. );
  15. }
  16. // Define 'siblingAFunc' and 'siblingBFunc' here
  17. }

任何节点

全局变量

组件中都可以引用到全局变量,一般可以挂载到 window 对象上面,切记不要滥用他们

  1. window.x = 'global variable'

Context

Context 使用类似于 Props, 和 Props 区别在于它可以提供整个树形组件的数据(多级子组件), Context 只能向下数据传递(父组件到子组件),可以配合 callback functions 一起使用(子组件到父组件)。

使用 Props 实例

  1. class App extends React.Component {
  2. render() {
  3. return <Toolbar theme="dark" />;
  4. }
  5. }
  6. function Toolbar(props) {
  7. return (
  8. <div>
  9. <ThemedButton theme={props.theme} />
  10. </div>
  11. );
  12. }
  13. class ThemedButton extends React.Component {
  14. render() {
  15. return <Button theme={this.props.theme} />;
  16. }
  17. }

使用 Context 实例

  1. // 创建 Context 实例
  2. const ThemeContext = React.createContext('light');
  3. class App extends React.Component {
  4. render() {
  5. return (
  6. // 提供 `Provider` 上下文容器
  7. <ThemeContext.Provider value="dark">
  8. <Toolbar />
  9. </ThemeContext.Provider>
  10. );
  11. }
  12. }
  13. function Toolbar(props) {
  14. return (
  15. <div>
  16. <ThemedButton />
  17. </div>
  18. );
  19. }
  20. class ThemedButton extends React.Component {d
  21. static contextType = ThemeContext;
  22. render() {
  23. return <Button theme={this.context} />;
  24. }
  25. }

中介者模式通信

本小节和大家一起探讨学习,通过中介者模式实现组件元素之间跨级通信。同理 Redux 状态管理也是采用中介者模式实现 数据状态扁平化管理
前端进阶能力 - 组件通信 - 图3
不需要考虑A,B,C组件的树形关系,这样就可以实现扁平化通信,如图:A组件的行为就可以被B和C组件响应。

EventBus实现

EventBus 模块实现引用的是通用SDK的 event 模块。实现事件发布和监听管理。 代码可以参考上一章节(设计模式 - EventEmitter实现)
EventBus源码地址

源码地址:https://github.com/dkypooh/front-end-develop-demo/tree/master/base/babel7-react/src/common/eventBus

组件通信实例

不通过父子组件属性和回调函数通信, 通过 EventBus 中介者模式实现一个加减计数器组件。
前端进阶能力 - 组件通信 - 图4

目录结构

  1. ├── src
  2. ├── common
  3. ├── event.js
  4. └── eventBus
  5. ├── event.d.ts
  6. ├── event.js
  7. └── event.ts
  8. ├── components
  9. ├── add
  10. └── index.jsx
  11. └── show
  12. └── index.jsx
  13. └── index.jsx

Add 组件

  1. import React from "react";
  2. import event from '../../common/event';
  3. export default class extends React.PureComponent {
  4. handleAdd() {
  5. // 触发加号事件
  6. event.emit('add');
  7. }
  8. handleReduce() {
  9. // 触发减号事件
  10. event.emit('reduce');
  11. }
  12. render() {
  13. return (
  14. <div className="m-opt">
  15. <div onClick={() => {this.handleAdd()}}>+</div>
  16. <div onClick={() => {this.handleReduce()}}>-</div>
  17. </div>
  18. );
  19. }
  20. }

Show 组件

  1. import React from "react";
  2. import event from '../../common/event';
  3. export default class extends React.PureComponent {
  4. constructor(props) {
  5. super(props);
  6. this.state = {
  7. number: 0
  8. };
  9. // 监听减号事件,并把 `number` 字段减一
  10. event.on('reduce', () => {
  11. this.setState({
  12. number: this.state.number - 1
  13. });
  14. });
  15. // 监听加号事件,并把 `number` 字段加一
  16. event.on('add', () => {
  17. this.setState({
  18. number: this.state.number + 1
  19. });
  20. });
  21. }
  22. render() {
  23. return (
  24. <div className="m-show">
  25. {this.state.number}
  26. </div>
  27. );
  28. }
  29. }

Add组件出发操作加减的事件, Show组件监听事件实现状态更改

结语

这样介绍了组件通信常用的几种方式,以及通过 EventBus 实现组件之间扁平化通信。 本章以 React 框架举例,同时在其他主流的 MVVM 框架都适用(Vuejs,Angular等)。

参考文档