[]).forEach(cb => cb(...data));
}
}
class Child extends EventEimtter {
constructor() {
super();
// 通过消息接口发布消息
setTimeout(() => { this.pub('update') }, 2000);
}
}
class Parent {
constructor() {
// 初始化阶段,传入回调函数
this.child = new Child();
// 订阅子组件的消息
this.child.sub('update', function () {
console.log('child update')
});
}
}
Backbone.js就同时支持回调函数和消息接口方式,但React中选择了比较简单的回调函数模式,下面来看一下React的例子
class Child extends Component {
constructor(props) {
setTimeout(() => { this.props.cb() }, 2000);
}
render() {
return <p></p>
}
}
class Parent extends Component {
render() {
return <Child cb={() => {console.log('update')}} />
}
}爷孙组件
父子组件其实可以算是爷孙组件的一种特例,这里的爷孙组件不光指爷爷和孙子,而是泛指祖先与后代组件通信,可能隔着很多层级,我们已经解决了父子组件通信的问题,根据化归法,很容易得出爷孙组件的答案,那就是层层传递属性么,把爷孙组件通信分解为多个父子组件通信的问题
层层传递的优点是非常简单,用已有知识就能解决,问题是会浪费很多代码,非常繁琐,中间作为桥梁的组件会引入很多不属于自己的属性
在React中,通过context可以让祖先组件直接把属性传递到后代组件,有点类似星际旅行中的虫洞一样,通过context这个特殊的桥梁,可以跨越任意层次向后代组件传递消息
怎么在需要通信的组件之间开启这个虫洞呢?需要双向声明,也就是在祖先组件声明属性,并在后代组件上再次声明属性,然后在祖先组件上放上属性就可以了,就可以在后代组件读取属性了,下面看一个例子
import PropTypes from 'prop-types';
class Child extends Component {
// 后代组件声明需要读取context上的数据
static contextTypes = {
text: PropTypes.string
}
render() {
// 通过this.context 读取context上的数据
return <p>{this.context.text}</p>
}
}
class Ancestor extends Component {
// 祖先组件声明需要放入context上的数据
static childContextTypes = {
text: PropTypes.string
}
// 祖先组件往context放入数据
getChildContext() {
return {text: 'yanhaijing'}
}
}context的优点是可以省去层层传递的麻烦,并且通过双向声明控制了数据的可见性,对于层数很多时,不失为一种方案;但缺点也很明显,就像全局变量一样,如果不加节制很容易造成混乱,而且也容易出现重名覆盖的问题
个人的建议是对一些所有组件共享的只读信息可以采用context来传递,比如登录的用户信息等
小贴士:React Router路由就是通过context来传递路由属性的
兄弟组件
如果两个组件是兄弟关系,可以通过父组件作为桥梁,来让两个组件之间通信,这其实就是主模块模式
下面的例子中,两个子组件通过父组件来实现显示数字同步的功能
class Parent extends Component {
constructor() {
this.onChange = function (num) {
this.setState({num})
}.bind(this);
}
render() {
return (
<p>
<Child1 num={this.state.num} onChange={this.onChange}>
<Child2 num={this.state.num} onChange={this.onChange}>
</p>
);
}
}主模块模式的优点就是解耦,把两个子组件之间的耦合关系,解耦成子组件和父组件之间的耦合,把分散的东西收集在一起好处非常明显,能带来更好的可维护性和可扩展性
任意组件
任意组件包括上面的三种关系组件,上面三种关系应该优先使用上面介绍的方法,对于任意的两个组件间通信,总共有三种办法,分别是共同祖先法,消息中间件和状态管理
基于我们上面介绍的爷孙组件和兄弟组件,只要找到两个组件的共同祖先,就可以将任意组件之间的通信,转化为任意组件和共同祖先之间的通信,这个方法的好处就是非常简单,已知知识就能搞定,缺点就是上面两种模式缺点的叠加,除了临时方案,不建议使用这种方法
另一种比较常用的方法是消息中间件,就是引入一个全局消息工具,两个组件通过这个全局工具进行通信,这样两个组件间的通信,就通过全局消息媒介完成了
还记得上面介绍的消息基类吗?下面的例子中,组件1和组件2通过全局event进行通信
class EventEimtter {
constructor() {
this.eventMap = {};
}
sub(name, cb) {
const eventList = this.eventMap[name] = this.eventMap[name] 关键词:传统组件间通信与React组件间通信的区分比较(代码示例)