【总结】Vue中的组件通信方式有哪些?React中的组件通信方式有哪些?
文章目录
- 一.Vue中的组件通信
- 1.父传子
- 2.子传父
- 3.事件总线EventBus
- 4.ref和$Refs
- 5.Vuex
- 二.React中的组件通信
- 1.父传子
- 2.子传父
- 3.状态提升(兄弟组件通信)
- 4.跨层级(祖孙组件通信)
一.Vue中的组件通信
1.父传子
思路:父组件在子组件占位符上绑定自定义属性,子组件通过props接收传值
示例:
//父组件:ProductDetail.vue
data(){return:{count:0}
}
//把value作为自定义属性值
<CountBox :value="count" />//子组件:CountBox.vue
//接收父组件的自定义属性value
props:{value:{type:Number}
}
//调用value数据,让input框有默认值
<input type="text :value="value"/>
2.子传父
思路:子组件使用$emit,第一个参数绑定自定义事件名,第二个参数传值给父组件,父组件在使用自定义事件接收传值
示例:
//父组件:ProductDetail.vue
//父组件把input作为自定义事件,并用count接收传值,更新count的值
<CountBox :value="count" @input="count" />//子组件:CountBox.vue
methods:{handleAdd(){//子组件把input作为自定义事件名,把value+1作为新值传给父组件this.$emit('input',this.value+1)},handleInput(e){this.$emit('input',e.target.value)}
}
<button @click="handleAdd">+</button>
<input type="text :value="value" @click="handleInput" />
<button @click="handleSub">-</button>
知识点:
v-model是语法糖,它等价于@input事件和value属性,上例中父组件可优化为:
<CountBox :value="count" @input="count" />
简写为:
<CountBox v-model='count' />
其他场景中v-model的拆解如下:
- 在
input文本框中,它是由value属性和oninput事件构成的 - 在
多选框中,它是由checked属性和onchange事件构成的 - 在
select下拉菜单中,它是由value属性和onchange事件构成的
3.事件总线EventBus
思路:
创建一个事件总线并导出,并在组件中导入,一个组件使用$emit发送数据,另一组件使用$on接收数据,最后可以使用$off移除eventBus
示例:
//step1:创建事件总线utils/EventBus.js
import Vue from "vue"
const Bus=new Vue();//通过一个空的Vue实例作为中央事件总线(事件中心)
export default Bus;//step2:在组件A引入事件总线并调用$emit发送数据
<button @click="handleClick">点击</button>
......
import Bus from "@/utils/EventBus.js";data() {return {num:100};},methods: {handleClick() {console.log("发送数据了...");Bus.$emit("changeNum", this.num);},},//step3:在组件B引入事件总线并使用$on接收数据
<div>{{count}}</div>
......
import Bus from "@/utils/EventBus.js";data() {return {count:0};},mounted() {Bus.$on("changeNum", (num) => {console.log("接收到数据了...",num);this.count += num;});}
注意事项和优化:
1).不能在模板中直接调用Bus.$emit,写成:<button @click="Bus.$emit("changeNum", this.num)">点击</button>
2).注销事件总线beforeDestroy() {// 清理事件监听,避免内存泄漏Bus.$off('custom-event', this.handleEvent);}
3).可以在main.js注册全局的Bus,而不用在每个组件中引入并注册import { Bus } from '@/utils/event-bus';Vue.prototype.$bus = Bus; // 全局注入
4.ref和$Refs
思路:父组件在子组件占位符上绑定ref属性.并在事件中通过$ref来调用子组件中对外暴露的方法
示例:
//子组件:ArticleDetail.vue
{{message}}
......data() {return {message: ''};},methods: {// 对父组件暴露的方法getMsg(newMsg){console.log('我是子组件的方法,获取的数据是:', newMsg); this.message = newMsg;}}//父组件:Article.vue
<div><ArticleDetail ref="ChildRef" /><button @click="sendToChild">点击</button>
</div>
......
import ArticleDetail from './ArticleDetail.vue'
export default {components: {ArticleDetail,},methods:{sendToChild() {this.$refs.ChildRef.getMsg(this.msg); // 调用子组件的方法}},data: () => ({msg:"hello Ref!",})}
//结果:父组件点击触发事件,子组件模板渲染{{message}}
5.Vuex
思路:把数据存在仓库的state中,不同组件分别调用mutations中更改state数据的方法
示例:
//step1:创建仓库store/index.js
import Vue from "vue";
import Vuex from "vuex";Vue.use(Vuex);export default new Vuex.Store({state: {isVaild: true},mutations: {setVaild(state, isVaild) {state.isVaild = isVaild;}},actions: {}
});//step2:在main.js中全局注入
import store from "./store/index.js"
new Vue({store,render: h => h(App),
})//step3:在组件中调用
<div class="wrapper"><button @click="handleClick">点击</button><div v-if="isVaild"> 显示了</div>
</div>
......computed: {...mapState(['isVaild'])},methods: {handleClick() {this.$store.commit('setVaild', false)}},
//结果:点击按钮触发事件,更改isVaild的值,隐藏div标签
二.React中的组件通信
1.父传子
格式同Vue,略
示例一:
function Son(props){return <div>我是子组件,接收到的数据是:{props.info}</div>
}
function App(){return {<div><Son info={message666}></Son></div>}
}
//输出:我是子组件,接收到的数据是:message666
示例二.
//父组件Parent.js
import React, { useState } from 'react';
import Child from './Child'; // 导入单独文件的子组件
const Parent = () => {const [msg,setMsg] = useState("hello react!");return (<div><div>{/* 向子组件传递数据:自定义属性为msg */}<Child msg={msg} /><button onClick={()=>{setMsg("hi react!")}}>点击切换</button></div></div>);
};
export default Parent;//子组件Child.js
import React from 'react';
// 子组件通过props接收父组件传递的数据
const Child = ({msg}) => {return (<div><p>子组件接收到的数据: {msg}</p></div>);
};
export default Child;
2.子传父
步骤:
- 1.父组件定义一个回调函数,用于接收子组件传递的值
- 2.父组件把该回调函数作为props传给子组件
- 3.子组件在点击事件中调用该回调函数并传参
示例:
//父组件Parent.js
import React, { useState } from 'react';
import Child from './Child'; // 导入单独文件的子组件
const Parent = () => {const [msg,setMsg] = useState("hello react!");//子传父step1:父组件定义一个回调函数,用于接收子组件传递的数据const getMsg = (msg) => {console.log('子组件传递的数据: ',msg)}return (<div><div>{/* 子传父step2:父组件将这个回调函数作为props传递给子组件*/}<Child msg={msg} onGetSonMsg={getMsg} /><button onClick={()=>{setMsg("hi react!")}}>点击切换</button></div></div>);
};
export default Parent;//子组件:Child.js
import React from 'react';
const Child = ({onGetSonMsg}) => {const msg1='你好'return (<div>{/* 子传父step3:子组件在点击事件中调用父组件传递的回调函数,并传递数据 */}<button onClick={()=>{onGetSonMsg(msg1)}}>点击传递数据给父组件</button></div>);
};
export default Child;
3.状态提升(兄弟组件通信)
(状态提升:我的理解是都把状态提升到共同的父组件App.js中,类似于vue中的EventBus或vuex)
思路:
将共享状态提升到最近的共同父组件中,然后通过props传递给子组件,同时将修改状态的函数也传递给子组件,这样兄弟组件就可以通过父组件来同步状态。
(流程:Son1子传父给父组件App,父组件App再父传子下发给Son2)
(难点:父组件获取到子组件Son1的数据后,要用变量接收,且要在将来的父传子中传给Son2并动态显示,解决方法是把数据做成状态再下发)
示例:
//第三步--子传父step3:在点击事件中调用回调函数并传参
const msg1="hello React!!"
function Son1(onGetSonMsg){//子组件1return <button onClick={onGetSonMsg(msg1)}>点击</button>
}
//第六步--父传子step2:通过props属性接收父组件传值
function Son2(props){return <div>接收兄弟组件的数据为:{props.msgToSon2}<
function App(){const [msg,setMsg]=useState('')//第一步--子传父step1:准备回调函数const getMsg=message=>{//console.log("接收子组件传递数据:",message)setMsg(message)//第四步--状态提升到父组件:把子传父获取到的数据作为状态变量msg的最新值}return{{*第二步-子传父step2:把回调函数作为props值传给子组件*}<Son1 onGetSonMsg={getMsg}/>{*第五步--父传子step1:把状态变量msg作为自定义属性的值传给Son2*}<Son2 msgToSon2={msg}/>}
}
4.跨层级(祖孙组件通信)
步骤:
- 1.使用
createContext方法创建一个上下文对象Ctx - 2.在顶层组件(
App)中通过Ctx.Provider为组件提供数据 - 3.在底层组件(
Grandson)中通过钩子函数useContext获取数据
示例:
//App.js
import { React,useState, createContext, useContext } from 'react';
const msg='hello'//准备App==>Grandson的数据
// step1:使用createContext方法创建一个上下文对象Ctx
const Ctx=createContext("默认消息")
return ({/* step2:在顶层组件(App)中通过Ctx.Provider为组件提供数据 */}<Ctx.Provider value={msg}><div style={{padding: '20px', border: '1px solid #ccc'}}><h2>App组件(数据提供方)</h2><Son /></div></Ctx.Provider>
)
// 中间组件
function Son(){return (<div style={{margin: '15px', padding: '15px', border: '1px dashed #999'}}><h3>Son组件(中间层)</h3><Grandson /></div>)
}
// 底层组件
function Grandson(){// step3:在底层组件(Grandson)中通过钩子函数useContext获取数据const message=useContext(Ctx)console.log(message)return (<div style={{margin: '10px', padding: '10px', background: '#f0f0f0'}}><h4>Grandson组件(数据消费方)</h4><p>接收到的消息:<strong>{message}</strong></p></div>)
}
