【框架对比】React和Vue的通信方式

1,854 阅读4分钟

后续会从各角度对比React和Vue,除了此文,目前已有的:

【框架对比】React和Vue的hook函数

【框架对比】React和Vue的生命周期

【框架对比】React和Vue的diff算法

一、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是一个全局变量,容易引起传值混乱,难以维护。
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')
},
  • parent/parent/children
  • ref
  • vuex

跨级组件

  • 层层组件传递props
  • attrs/attrs/listeners
  • provide/inject
  • vuex

对比

相同点:

  1. 父组件向子组件: props
  2. 跨级组件通信:层层组件传递props
  3. 非嵌套组件间(兄弟组件):发布/订阅模式、借助同一父组件通过props传递数据

不同点:

  1. 子组件向父组件通信:
    react是子调用父传给子的函数;
    vue是emit
  2. 跨级组件通信:
    react有context;
    vue有attrs/attrs/listeners、provide/inject
  3. 不管任何通讯,react可以用redux;vue可以用vuex,但是根据项目合理选择