React作为前端的新一代主流框架,因其组件化的思想,彻底革新了前端停留在DOM操作的古老开发方式。使用React的组件,不再需要模板,也不用再过分担心渲染和更新带来的性能问题。开发一个个组件,就像一个个模块一般,在需要的地方放在那里就好。但是这么多的组件之间,如何进行信息交流和信息传递,就引发了一个新的问题——组件通信。
我是目录
- 事件通信
- 观察者模式
- 关于Redux与Flux
首先,我们先假设我们的组件结构如下:
Parent
____|____
| |
ChildA ChildB
1. 事件通信
1.1 父传子
在 React
中,父组件可以向子组件通过传 props
的方式,向子组件进行通讯。
class Parent extends Component {
state = { msg: 'start' };
componentDidMount() {
setTimeout(() => { this.setState({ msg: 'end' }); }, 1000);
}
render() {
// 将传递值当做组件的props属性传递给子组件
// return ;
// 使用...运算符将父组件信息以更简洁的方式传递给子组件
return ;
}
}
class ChildA extends Component {
render() {
// 获取父组件传递过来的数据中的msg
return {this.props.msg}
;
}
}
export default Parent;
1.2 子传父
子组件向父组件通讯,同样也需要父组件向子组件传递 props
进行通讯,只是父组件传递的,是作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到父组件的作用域中。
class Parent extends Component {
state = { msg: 'start' };
transferMsg(childMsg) {
this.setState({ msg: childMsg });
}
render() {
return (
{this.state.msg}
// 将事件传递给子组件,子组件通过事件传递参数与父组件通信
this.transferMsg(childMsg)} />
);
}
};
class ChildA extends Component {
componentDidMount() {
setTimeout(() => { this.props.transferMsg('end'); }, 1000);
}
render() {
return ;
}
}
1.3 兄弟组件通信
对于没有直接关联关系的两个节点,就如 ChildA
与 ChildB
之间的关系,他们唯一的关联点,就是拥有相同的父组件。参考之前介绍的两种关系的通讯方式,如果我们向由 ChildA
向 ChildB
进行通讯,我们可以先通过 ChildA
向 Parent
组件进行通讯,再由 Parent
向 ChildB
组件进行通讯。
class Parent extends Component {
state = { msg: 'parent' };
transferMsg(childAMsg) {
this.setState({ msg: childAMsg });
}
componentDidUpdate() {
console.log('Parent is update'); // 测试更新State后哪些组件也被更新了生命周期
}
render() {
return (
this.transferMsg(childAMsg)} />
);
}
}
class ChildA extends Component {
componentDidMount() {
setTimeout(() => {
this.props.transferMsg('ChildA');
}, 1000);
}
componentDidUpdate() {
console.log('ChildA is update'); // 测试更新State后哪些组件也被更新了生命周期
}
render() {
return ;
}
}
class ChildB extends Component {
componentDidUpdate() {
console.log('ChildB update'); // 测试更新State后哪些组件也被更新了生命周期
}
render() {
return (
I am ChildB, this is ChildA to parent and then to ChildB: {this.props.msg}
);
}
}
class ChildBchild extends Component {
componentDidUpdate() {
console.log('ChildBchild is update'); // 测试更新State后哪些组件也被更新了生命周期
}
render() {
return ;
}
}
当我们在浏览器运行时,可以从控制台发现,各个组件的 componentDidUpdate
方法均被触发。所以,有没有更好的解决方式呢?
2. 观察者模式
观察者模式也叫发布-订阅者模式,发布者发布事件,订阅者监听事件并做出反应。我们通过这种模式,在全局定义一个事件代理管理器,每一个组件只需要引入这个事件代理者即可。
事件代理文件,eventBus.js
:
const eventBus = {
onObj: {},
// 事件监听
on(key, fn) {
this.onObj[key] === undefined && (this.onObj[key] = []);
this.onObj[key].push(fn);
},
// 事件关闭
off(key) {
this.onObj[key] = [];
this.oneObj[key] = [];
},
// 事件触发
trigger() {
/*
备注:
除了事件参数,其他参数允许传入数组,但对于传入的map结构、函数、以及多个其他参数都没有做处理,
这点可以根据个人需要进行拓展
*/
// 无参返回false
if (arguments.length === 0) {
return false;
}
// key是事件,args是参数 - 通信信息
const argumentsArr = [...arguments];
let key = argumentsArr[0];
let args = argumentsArr.slice(1);
if (this.onObj[key] !== undefined && this.onObj[key].length > 0) {
for (let i in this.onObj[key]) {
this.onObj[key][i].apply(null, args);
}
}
}
};
export default eventBus;
事件代理文件,eventBus.js
:
class Parent extends Component {
render() {
return (
);
}
}
class ChildA extends Component {
componentDidMount() {
let hello = 'ChildA - 结束';
setTimeout(() => {
eventBus.trigger('change', hello);
}, 1000);
}
render() {
return ;
}
}
class ChildB extends Component {
state = {
msg: 'ChildB - 开始'
};
componentDidMount() {
eventBus.on('change', msg => {
this.setState({
msg
});
});
}
render() {
return (
ChildA to ChildB component: {this.state.msg}
);
}
}
3. 关于Redux与Flux
关于 Redux
与 Flux
就是用来管理状态和解决组件通信问题的。但虽然 Redux
对于组件间的解耦提供了很大的便利,如果你在考虑该不该使用 Redux
的时候,社区里有一句话说,“当你不知道该不该使用 Redux
的时候,那就是不需要的”。Redux
用起来一时爽,重构或者将项目留给后人的时候,就是个大坑,Redux
中的 dispatch
和 subscribe
方法遍布代码的每一个角落。虽然 Flux
设计中的 Controller-Views
概念就是为了解决这个问题出发的,将所有的 subscribe
都置于 Parent
组件(Controller-Views
),由最上层组件控制下层组件的表现,然而,这不就是我们所说的子组件向父组件通讯这种方式了。
参考来源:淘宝-React组件通信原理