Skip to content

标题备选

  1. Vue / React 组件传值怎么选?一篇梳理常见通信方式
  2. 前端组件通信补课:Vue 和 React 的传值思路对比
  3. 手把手梳理 Vue / React 组件传值:父子、兄弟和跨层通信

摘要

这篇文章适合正在学习组件化开发的前端同学,对比 Vue 和 React 的组件传值方式,帮你理清常见通信场景。

正文

哈喽大家好👋 我是程序🦍kk。把复杂知识掰成大白话讲明白,是我一直以来的小追求✨;打好基础才能稳步进阶,是我始终秉持的学习理念~

📢 我搭建了5000人程序猿专属学习交流群 群内会同步前端开发/全栈开发/Web3开发/远程工作等干货资源 关注我并回复 加群 ,就能加入交流圈啦🚀

前言

组件传值是前端项目绕不开的基本功。

Vue 和 React 都是组件化思路,但数据流、事件传递和跨层通信的写法并不完全一样。

这篇围绕「Vue / React 的组件传值」把常见传值方式整理出来,方便大家对比理解。

知识点整理

下面进入正文。建议把父子组件、兄弟组件、跨层组件这几类场景分开看,会更容易理解。

如果你正在做组件拆分,可以先判断数据是父传子、子传父,还是需要跨层共享,再选择合适方案。

Vue

父传子

props

html
<!-- 父组件模板 -->
<div id="root">
    <h2>{{msg}}</h2>
    <!-- 组件标签 :变量名="值" -->
    <Son1 v-bind:age="18"></Son1>
</div>

<!-- 子组件模板 -->
<template id="son1">
    <div>
        <h3>{{msg}}</h3>
        <p>{{age}}</p>
    </div>
</template>

<script>
    const Son1 = {
        template: "#son1",
        // 组件里props选项接收
        props:['age',],
        data(){
            return {
                msg:"son"
            }
        }
    }
    var vm = new Vue({
        el:"#root",
        data:{
            msg:"father"
        },
        components:{
            Son1
        }
    })
</script>

子传父

$emit自定义事件

  • 子组件给父组件发送自定义事件
  • 父组件在子组件标签上监听自定义事件
html
    <!-- 父组件模板 -->
    <div id="root">
        <h2>{{msg}}</h2>
        <!-- 监听子组件传的自定义事件 -->
        <Son1 @savemsgfromson="save"></Son1>
    </div>

    <!-- 子组件模板 -->
    <template id="son1">
        <div>
            <button @click="sendEventToFather(toFather)">{{msg}}</button>
        </div>
    </template>

    <script>
        // 子组件
        const Son1 = {
            template: "#son1",
            data(){
                return {
                    msg:"son",
                    toFather:"少抽点烟"
                }
            },
            methods:{
                sendEventToFather(message){
                    // 给父组件发送自定义事件
                    this.$emit('savemsgfromson',message)
                }
            }
        }

        // 父组件
        var vm = new Vue({
            el:"#root",
            data:{
                msg:"father"
            },
            components:{
                Son1
            },
            methods:{
                save(message){
                    console.log(message)
                }
            }
        })
    </script>

任何关系组件传值(EventBus和vuex)

EventBus

  • EventBus称之为中央事件总线
  • 通过一个空的Vue实例作为中央事件总线
  • $emit自定义事件用来发送数据
  • $on触发自定义事件来接收数据
  • $off销毁自定义事件
html
    <!-- 父组件模板 -->
    <div id="root">
        <Son1></Son1>
        <Son2></Son2>
        <hr/>
        <button @click="deb">销毁事件</button>
    </div>

    <!-- Son1模板 -->
    <template id="son1">
        <div>
            <h3>{{msg}}</h3>
            <button @click="toson2">toSon2</button>
        </div>
    </template>

    <!-- Son2模板 -->
    <template id="son2">
        <div>
            <h3>{{msg}}</h3>
        </div>
    </template>

    <script>
        // 创建中央事件总线
        const eventBus = new Vue()

        const Son1 = {
            template: "#son1",
            data(){
                return {
                    msg:"son1"
                }
            },
            methods:{
                toson2(){
                    // 自定义事件用来发送数据
                    eventBus.$emit('sendtoson2','弟弟')
                }
            }
        }

        const Son2 = {
            template: "#son2",
            data(){
                return {
                    msg:"son2"
                }
            },
            // 在mounted中触发自定义事件
            mounted(){
                // 触发自定义事件来接收数据
                eventBus.$on('sendtoson2',msg=>{
                        console.log(msg)
                    })
            }
        }

        var vm = new Vue({
            el:"#root",
            components:{
                Son1,Son2
            },
            methods:{
                deb(){
                    eventBus.$off('sendtoson2')
                    console.log('销毁事件')
                }
            }
        })
    </script>

vuex

链接

React

父传子

  • 使用props进行父传子
  • 父组件
jsx
import React from "react";

import Item from "./Components/Item";

class App extends React.Component {
   render() {
       return <Item name="林则徐" next="壁立千仞无欲则刚"></Item>;
   }
}

export default App;
  • 子组件(函数)
jsx
import React from "react";

const Item = (props) => {
   return (
       <div>
           海纳百川有容乃大,{props.next}。 -- {props.name}
       </div>
   );
};

export default Item;
  • 子组件(类)
jsx
import React, { Component } from "react";

class Item extends Component {
   render() {
       return (
           <div>
                海纳百川有容乃大,{this.props.next}。 -- {this.props.name}
           </div>
       );
   }
}

export default Item;
  • 使用ref(待编辑)

子传父

该传值的实现可以分为两种,思想大致如下:

  • (父主动获取子的数据)父通过ref标记子组件,随后通过子组件实例对象获取子组件的数据
  • 在父组件中预埋一个修改父组件自身的方法,将该方法以props的形式传递给子组件,子组件收到方法时去调用,并且将自己需要给父的数据以实参的形式给这个方法
jsx
// 组件通信:子-父传值
// 1. 方式有两种,思路与前面基本一致
//      方法1:通过ref对象,给子组件绑定ref对象以获取整个子组件实例,也就获取到了子的state,进而可以获取state中的数据(被动,父去取的)
//      方法2:同时属性传递方法,方法依旧需要子去执行,但是子需要将自己的数据传递给该方法,由父接收并且可以打印输出(主动,子给父的)

import React, { Component, createRef } from "react";

// 父组件
class Father extends Component {

    // 创建子组件的ref对象
    child = createRef();

    render() {
        return (
            <div>
                <Son ref={this.child} fun={this.SonSetMsg.bind(this)}></Son>
                {/* 方法1:父主动去获取子的数据 */}
                <button onClick={this.getMsgFromSon.bind(this)}>子: 还钱!爸</button>
            </div>
        );
    }

    // 方法1:父主动去获取子的数据
    getMsgFromSon() {
        console.log(this.child.current.state.msg);
    }

    // 方法2:父亲准备一个篮子,空的,需要子往里放东西
    SonSetMsg(msg) {
        console.log(msg);
    }
}

// 子组件
class Son extends Component {
    state = {
        msg: "后来学会了做空,股票涨的时候也会赔钱",
    };
    render() {
        return (
            <div>
                <div>子组件</div>
                <button onClick={this.setMsg.bind(this)}>子把数据放篮子里</button>
            </div>
        );
    }
    // 方法2:子存放数据
    setMsg() {
        this.props.fun(this.state.msg);
    }
}

export default Father;

跨组件传值

网址:https://zh-hans.reactjs.org/docs/context.html

在react没有类似vue中的事件总线来解决这个问题。在实际的项目中,当需要组件间跨级访问信息时,如果还使用组件层层传递props,此时代码显得不那么优雅,甚至有些冗余。在react中,我们还可以使用context来实现跨级父子组件间的通信。

javascript
import React, { Component, createContext } from "react"

const {
	Provider,
	Consumer
} = createContext()

提示:在React的context中,数据被看成了商品,发布数据的组件会用provider身份(卖方),接收数据的组件使用consumer身份(卖方)。

  • 创建Context对象

当React渲染一个订阅了这个Context对象的组件,这个组件会从组件树中离自身最近的那个匹配的Provider中读取到当前的context值。

javascript
// 定义全局context
// 由于这个操作后期可能被复用,建议独立文件去创建。此处以`src/Context/index.js`为例
import { createContext } from "react"

export default createContext()
  • 发布消息

在App.jsx组件中发布消息,这样所有的组件都可以消费它的消息。

jsx
import React, { Component } from "react";
import Cmp1 from "./Components/Cmp1";
import Cmp2 from "./Components/Cmp2";
// 导入context对象
import ContextObj from "./Context/index";
let { Provider } = context;

class App extends Component {
    state = {
        count: 12345,
    };

    render() {
        return (
            <div>
                <Provider value={this.state.count}>
                    <Cmp6></Cmp6>
                    <Cmp7></Cmp7>
                </Provider>
            </div>
        );
    }
}

export default App;
  • 组件消费

在子组件中通过Api完成消费动作,从而实现消息通信。消费的方式有2种:

方式1:通过组件消费

jsx
import React, { Component } from "react";

import ContextObj from "../Context/index";
let { Consumer } = ContextObj;

class Cmp1 extends Component {
    render() {
        return (
            <div>
                <Consumer>
                    {(value) => {
                        return <div>获取到的值是:{value}</div>;
                    }}
                </Consumer>
            </div>
        );
    }
}

export default Cmp1;

方式2:通过绑定静成属性来消费

jsx
import React, { Component } from "react";
import ContextObj from "../Context/index";

class Cmp2 extends Component {
    static contextType = ContextObj;
    render() {
        return <div>{this.context}</div>;
    }
}

export default Cmp2;

任何关系组件传值

Redux

写在最后

好啦,今天的分享就到这里!

💬 互动时间:

你平时做组件通信更喜欢 Vue 的写法还是 React 的写法?如果想看 Pinia、Redux 或 Context 的后续对比,也可以留言。

最后,感谢你看到这里👏

如果喜欢这篇内容,不妨顺手给小编安排一波👇 点赞👍|转发📲|推荐❤️|评论📣

要是想第一时间蹲到新内容推送,记得给我点个星标⭐️

更多干货内容正在持续填坑中,咱们下期见👋

标签建议

JavaScript、前端、Vue、React、组件通信