后续会从各角度对比React和Vue,除了此文,目前已有的:
一、React
父组件向子组件
- 父组件使用props属性向子组件传递数据
子
export default function Child({ name }) {
return <h1>Hello, {name}</h1>;
}
父
<div>
<Child name="hannie" />
</div>
- ref获取整个子组件对象,可以调用子组件中的函数
<div>
<button onClick={() => {
this.refs.mychild.handleEvent("父组件传来的");
}}>子组件</button>
<Child ref='mychild' />
</div>
子组件向父组件
- 利用回调函数:
父组件使用props属性向子组件传递一个函数
,子组件携带自己的数据并通过调用该函数向父组件传递数据
子
class A extends Component {
static propTypes = {
hideConponent: PropTypes.func.isRequired,
}
render() {
return (
<div>
哈哈,我是组件A
<button onClick={this.props.hideComponent}>隐藏A组件</button>
</div>
);
}
}
父
export default class App extends Component {
constructor(...args) {
super(...args);
this.state = {
isShowA: false,
};
}
showComponent = () => {
this.setState({
isShowA: true,
});
}
hideComponent = () => {
this.setState({
isShowA: false,
});
}
render() {
return (
<div>
<button onClick={this.showComponent}>显示A组件</button>
{
this.state.isShowA ?<A hideComponent={this.hideComponent} /> : null
}
</div>
);
}
}
非嵌套组件间(兄弟组件)
- 自定义事件机制:发布/订阅模式,通过向事件对象上添加监听器和触发事件来实现组件之间的通信
import { EventEmitter } from 'events';
export default new EventEmitter();
收值方addListener和removeListener
componentDidMount() {
// 组件装载完成以后声明一个自定义事件
this.eventEmitter = emitter.addListener('comment', (data) => {
this.setState({
data,
});
});
}
componentWillUnmount() {
emitter.removeListener(this.eventEmitter);
}
传值方emit
handleClick = (data) => {
emitter.emit('comment', data);
};
通过emitter.addListener(事件名称,函数名)方法,进行事件监听(订阅)。 通过emitter.removeListener(事件名称,函数名)方法 ,进行事件销毁(取消订阅)
- 借助同一父组件通过props传递数据,也就是利用父组件实现中转传递
缺点:会增加子组件和父组件之间的耦合度,如果嵌套深的话,不易找到父组件
跨级组件
-
层层组件传递props
例如:A组件和B组件之间要通信,A先向C组件通信,C组件通过props和B组件通信 -
使用context:生产/消费者模式
- 生产者 Provider(
<GlobalCxt.Provider/>)
:用在组件树中更外层的位置。它接受一个名为 value 的 prop,其值可以是任何 JavaScript 中的数据类型 - 消费者consumer(
<GlobalCxt.Consumer/>)
: 可以在 Provider 组件内部的任何一层使用。它接收一个函数,这个函数的参数是 Provider 组件接收的那个 value prop 的值,返回值是一个 React 元素(一段 JSX 代码) - context是一个全局变量,在任何地方都可以访问到,把要通信的信息放在context上,这样在其他组件中可以随意取到;
- React官方不建议大量使用context,尽管它可以减少逐层传递,但当组件结构复杂的时候,不知道context是从哪里传来的;而且context是一个全局变量,容易引起传值混乱,难以维护。
- 生产者 Provider(
const GlobalCxt = React.createContext();
父
<GlobalCxt.Provider value={
{
//传递给消费者的数据
msg: this.state.msg,
//传递给消费者,一个函数,提供改变生产者内部的一个参数
onChangeShow: (text) => {
this.setState({
msg: text
});
},
}
}>
<div>
<h1>我是父组件,我提供数据给子孙吗:{this.state.msg}</h1>
<Son></Son>
</div>
</GlobalCxt.Provider>
儿子
<GlobalCxt.Consumer>
{
context => (
<div>
<h1>我是儿子组件,我要消费父亲的数据,{context.msg}</h1>
<GrandSon></GrandSon>
</div>
)
}
</GlobalCxt.Consumer>
孙子(可以有多个)
<GlobalCxt.Consumer>
{
context => (
<div>
<h1>我是孙子组件,我要消费祖宗的数据,{context.msg}</h1>
<button onClick={() => {
context.onChangeShow('我要改变我祖宗的规矩');
}
}>我要改变我祖宗的规矩</button>
</div>
)
}
</GlobalCxt.Consumer>
生产者 Provider
用在组件树中更外层的位置。它接受一个名为 value 的 prop,其值可以是任何 JavaScript 中的数据类型
消费者consumer
可以在 Provider 组件内部的任何一层使用。它接收一个名为 children 值为一个函数的 prop。这个函数的参数是 Provider 组件接收的那个 value prop 的值,返回值是一个 React 元素(一段 JSX 代码)。
- redux:管理多个组件共享的状态
二、Vue
父组件向子组件
- 父组件使用props属性向子组件传递数据
子
<h1>Hello, {name}</h1>
props: {
name: {
type: String,
default: '',
},
}
父
<div>
<Child name="hannie" />
</div>
子组件向父组件
- 事件形式:子$emit,父监听
this.$emit("input", this.name);
非嵌套组件间(兄弟组件)
- 中央总线new Bus(),发布/订阅模式,其实此方法也可以用在跨级、父子间通讯
引入中央总线
import Vue from 'vue'
const Bus = new Vue()
export default Bus
收值方$on
mounted() {
Bus.$on('comment', (data) => {
console.log('监听',data)
})
}
传值方emit,并且需要销毁,不然可能会触发多次
handleClick = (data) => {
Bus.$emit("comment",data);
};
destroyed(){
Bus.$off('comment')
},
- children
- ref
- vuex
跨级组件
- 层层组件传递props
- listeners
- provide/inject
- vuex
对比
相同点:
- 父组件向子组件: props
- 跨级组件通信:层层组件传递props
- 非嵌套组件间(兄弟组件):发布/订阅模式、借助同一父组件通过props传递数据
不同点:
- 子组件向父组件通信:
react是子调用父传给子的函数;
vue是emit - 跨级组件通信:
react有context;
vue有listeners、provide/inject - 不管任何通讯,react可以用redux;vue可以用vuex,但是根据项目合理选择