简介:Vue组件通信方式一直都是单向数据流,就是父组件数据的更改会影响子组件信息的更改,而子组件信息更改不会影响父组件信息更改,那么父子组件之间如何通信呢?
1.props和emit
父组件 假设msg="爸爸的爱"
<son :dataMsg="msg" @updateMsg="getMsgByChild"></son>
子组件
<div class="content" >
{{dataMsg}}
<button @click="returnMsg">反馈</button>
</div>
props:{
dataMsg:{
type:String
}
},
returnMsg(){
this.$emit('updateMsg','反馈爱'); // 注意updateMsg方法会放在_event属性上,稍后讲中央事件统一解释
}
父传子,通过dataMsg映射,子组件向父组件通过$emit传递信息,父组件通过@updateMsg接受
2.通过provide和inject监听(可祖与孙相通)
父组件
<son></son>
data () {
return {
name: '张三'
}
},
provide () {
return {
name2: this.name
}
},
子组件
{{name2}} // 显示为张三
export default {
inject: ['name2']
}
这个时候传递的信息不是响应式的,可以测试在mounted中将name改为李四。
mounted() {
this.name = '李四'
}
可以看见显示依旧为张三。那么为什么provider不是响应式呢?可以看看provider的源码
const provideOption = vm.$options.provide
if (provideOption) {
const provided = isFunction(provideOption) // 如果写法为provider:{}就算一个对象,如果为provider(){}就是一个函数。这里主要是为了转换成对象
? provideOption.call(vm)
: provideOption
if (!isObject(provided)) {
return
}
const source = resolveProvided(vm) // 在vue上创建一个provider属性
const keys = hasSymbol ? Reflect.ownKeys(provided) : Object.keys(provided) // 取出provider的属性
//将属性挂载在_provider属性上
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
Object.defineProperty(
source,
key,
Object.getOwnPropertyDescriptor(provided, key)!
)
}
}
所以initProvider仅仅只是在vue上创建一个__provided属性,并将其provider的属性和值挂载在上面。
那么inject实现了什么呢?主要是为了判定是否在provided里面,如果在里面就取出来。
const provideKey = inject[key].from
if (provideKey in vm._provided) {
result[key] = vm._provided[provideKey]
}
所以我们应该怎么实现响应式呢?可以传递一个函数,去获取结果
// 父组件
provide () {
return {
name2: () => this.name
}
}
// 子组件就去执行这个函数
{{name2()}}
也可以传递一个对象,这样挂载的就会是一个引用地址~~
3.通过.sync实现双向绑定
父组件
<son :showSon.sync="isShowSon" v-if="isShowSon"></son>
子组件
<div class="content" >
我开始是显示的
<button @click="returnMsg">{{showSon ? '隐藏' : '显示'}}</button>
</div>
returnMsg(){
this.$emit('update:showSon',!this.showSon);
}
注意父组件的showSon和子组件的prop['showSon']和update:showSon的对应。
4.通过child
5.通过eventbus
eventbus原理上是跟方法一$emit一样的,毕竟我们实现也是new Vue()。但不同的是我们通常会把它挂载
在原型的属性上,所以我们要找其触发的方法一般要去原型上找。下面我们看看vue的event实现的原理:
Vue.prototype.$on = function (
event: string | Array<string>,
fn: Function
): Component {
const vm: Component = this
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
;(vm._events[event] || (vm._events[event] = [])).push(fn)
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
分析起来其实比较简单,我们$on的时候就是将事件一个个塞进_event对象中,$emit就是将该事件取出,然后执行。值得一提的是$on可以监听多个事件。因为其接受数组。