前言
有些数据需要在多个子组件间共享,或者父级组件需要向孙组件传递数据等情况,本文将梳理在React和Vue中嵌套的组件之间如何通信。
React 嵌套组件之间通信
React.createcontext()
首先来看下这个 api 返回值。
- 在 context.js 中拿到我们需要用到的这两个组件
- Provider 是数据的来源,可以为
多个 Consumer提供数据,如果组件中使用多个 Provider时,组件会找离自己最近的 Provider 拿数据 - Consumer 就是使用数据的地方,任何被包裹的组件都可以通过向 Consumer 组件传递一个函数中的回调中拿到 Provider 组件中共享的数据。
- Provider 是数据的来源,可以为
- 在父组件中通过 Provider 的 value 属性共享数据。
- 在所有的子孙组件中,通过 Consumer 来接收到父组件中共享的数据
// context.js
import React from 'react'
export const { Provider , Consumer} = React.createContext()
// father.jsx
<Provider value={{title:'标题'}}>
<Second></Second>
</Provider>
// Second.js
<Consumer>
{
state=>{
return <Third {...state}></Third>
}
}
</Consumer>
// Third.js
render(){
return <div>
接收到 provider 传递的值: {this.props.title}
</div>
}
导致的问题,一旦 Provider 组件接收的 value 值发生改变,所有 Consumer 组件包裹的组件都会重新渲染。
React.Children 方式传递孙组件
正常当前组件和孙组件传递数据通过 Props 传递,如 A -> B -> C 如果 B(子组件)有时候可能并不需要使用这些 props。所以找一种方式将数据直接传递给孙组件。
<Second>
<Third value={{title:'标题'}}></Third>
</Second>
// Second.jsx
<div>
{
// 兼容children 是单个和多个的情况
React.Children.map(this.props.children,function(child){
// 可以添加逻辑判断是否要选入 child
return child
})
}
</div>
上面代码中,将Third(孙组件)作为 children 传递给 Second 组件,
并且不仅父级组件可以将一些数据直接传递给 Third 组件。如果 Third 组件的 children 也可以通过函数向外部传递出一些数据
<Third>
{(value)=>{
return <h1>{value}</h1>
}}
</Third>
// third.jsx
render(){
return <div>
接收到 provider 传递的值: {this.props.children('h1内容')}
</div>
}
Vue 嵌套组件之间通信
eventBus
Vue 中使用 eventBus ,其实就是创建一个 Vue 实例专门用来操作、管理数据。来看下面这个例子 使用步骤分为三个步骤
- 创建 Vue 实例,用于操作数据
- 父组件通过这个实例 .$on 方法来注册监听事件
- 子组件通过这个实例 .$emit 方法来派发事件。
方式1:单文件引入
// EventBus.js
import Vue from 'vue'
// 通过一个新的 Vue 实例来单独管理数据状态
export default new Vue()
// father.js
import EventBus from './EventBus'
// 需要使用箭头函数才能拿到本组件中的实例对象
EventBus.$on('increment',(value)=>{
// ...
})
// 如果使用正常函数的话,this指向的是 EventBus 实例
EventBus.$on('decrease',function(value){
// ...
})
// child.js
import EventBus from '../EventBus'
increment(){
EventBus.$emit('increment',{value:this.value})
},
decrease(){
EventBus.$emit('decrease',{value:this.value})
},
可能会好奇,为啥这个实例存在 $on 、$emit 这些方法,其实每个 Vue 实例中原型上都挂载了这些方法。可以在任意一个 Vue 组件中 console.log(this['__proto__']['__proto__'])
this问题
如果我们想通过 EventBus.$on 来修改当前组件实例的数据,回调函数需要使用箭头函数, EventBus 本身也是一个 Vue 实例,所以使用正常的 function this会指向 EventBus 导致没法修改当前组件的状态。
我的理解就是借用 EventBus 实例中的方法来修改其他实例中的数据,所以我们在修改数据时需要弄清楚 this 的指向。
为什么不直接在组件中使用 $on,$emit,$off 这些方法来修改数据,而需要新建一个实例来管理? 因为每个组件都是一个Vue实例,这些方法只能在单个组件中注册监听,如果跨组件则无法监听到,所以需要 new Vue() 实例话一个单独的组件专门用于管理这些数据。
方式二:全局挂载
import Vue from 'vue'
const EventBus = new Vue()
Vue.prototype.$bus = EventBus
// father.js
this.$bus.$on('add',()=>{
this.num++
})
// child.js
add(){
this.$bus.$emit('add')
}
slot
使用 React.Children 那种通过向子组件传递孙组件的方法用Vue实现逻辑如下
// 子组件
<Search :placeholder="placeholder">
<div slot-scope="scope">
{{ scope }}
</div>
</Search>
// 孙组件
<slot content="{titel:'标题'}"></slot>
最后
这里没有使用 Vuex 和 redux 这类去管理数据,如果还有其他好用的数据管理的方法,欢迎在留言区交流👏