# 前言

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

本章节我们就一起来学习几种通用的组件通信模型, 同时通过中介者模型实现组件元素之间跨级通信。

# 开箱即用的源码

源码地址:github.com/dkypooh/fro…

# 组件通信模式

本小节以 React 组件框架为例,和大家一起探讨学习几种通用的组件通信模型。

image-20210214200953207

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

# 父组件到子组件

# Props

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

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

# Instance Methods

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

class TheChild extends React.Component {
  myFunc() {
    return "hello";
  }
}
class TheParent extends React.Component {
  render() {
    return (
      <TheChild
        ref={foo => {
          this.foo = foo;
        }}
      />
    );
  }

  componentDidMount() {
    var x = this.foo.myFunc();
    // x is now 'hello'
  }
}

# 子组件到父组件

# Callback Functions

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

以属性的方式传递函数

<MyChild myFunc={this.handleChildFunc} />

子组件调用方式

this.props.myFunc();

添加属性函数声明

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

# Event Bubbling

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

class ParentComponent extends React.Component {
  render() {
    return (
      <div onKeyUp={this.handleKeyUp}>
        // Any number of child components can be added here.
      </div>
    );
  }

  handleKeyUp = (event) => {
    // This function will be called for the 'onkeyup'
    // event in any <input/> fields rendered by any of
    // my child components.
  }
}

# 临近兄弟节点

# 父组件通信

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

class ParentComponent extends React.Component {
  render() {
    return (
      <div>
        <SiblingA
          myProp={this.state.propA}
          myFunc={this.siblingAFunc}
        />
        <SiblingB
          myProp={this.state.propB}
          myFunc={this.siblingBFunc}
        />
      </div>
    );
  }
  // Define 'siblingAFunc' and 'siblingBFunc' here
}

# 任何节点

# 全局变量

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

window.x = 'global variable'

# Context

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

# 使用 Props 实例

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

# 使用 Context 实例

// 创建 Context 实例
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    return (
      // 提供 `Provider` 上下文容器
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {d
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}

# 中介者模式通信

本小节和大家一起探讨学习,通过中介者模式实现组件元素之间跨级通信。同理 Redux 状态管理也是采用中介者模式实现 数据状态扁平化管理

image-20210214201012024

不需要考虑A,B,C组件的树形关系,这样就可以实现扁平化通信,如图:A组件的行为就可以被B和C组件响应。

# EventBus实现

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

EventBus源码地址

源码地址:github.com/dkypooh/fro…

# 组件通信实例

不通过父子组件属性和回调函数通信, 通过 EventBus 中介者模式实现一个加减计数器组件

# 目录结构

├── src
│   ├── common
│   │   ├── event.js
│   │   └── eventBus
│   │       ├── event.d.ts
│   │       ├── event.js
│   │       └── event.ts
│   ├── components
│   │   ├── add
│   │   │   └── index.jsx
│   │   └── show
│   │       └── index.jsx
│   └── index.jsx

# Add 组件

import React from "react";
import event from '../../common/event';

export default class extends React.PureComponent {
  handleAdd() {
    // 触发加号事件
    event.emit('add');
  }

  handleReduce() {
    // 触发减号事件
    event.emit('reduce');
  }
  render() {
    return (
      <div className="m-opt">
        <div onClick={() => {this.handleAdd()}}>+</div>
        <div onClick={() => {this.handleReduce()}}>-</div>
      </div>
    );
  }
}

# Show 组件

import React from "react";
import event from '../../common/event';

export default class extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      number: 0
    };
    // 监听减号事件,并把 `number` 字段减一
    event.on('reduce', () => {
      this.setState({
        number: this.state.number - 1
      });
    });
    // 监听加号事件,并把 `number` 字段加一
    event.on('add', () => {
      this.setState({
        number: this.state.number + 1
      });
    });
  }
  render() {
    return (
      <div className="m-show">
        {this.state.number}
      </div>
    );
  }
}

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

# 结语

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

# 参考文档

阅读全文