# 组件之间的通信
# 最基本的props/$emit父子组件之间
props
/$emit
是父子组件之间最基本的通信了,父组件通过props的方式向子组件传递数据,而子组件可以通过$emit向父组件通信。
通过props
/$emit
这种基本方式演变的父子组件间的数据双向绑定通信还有 v-model
/ .sync
。
v-model指令
v-model
本来是应用在表单上进行双向绑定的,他的本质是会默认为组件绑定名为 value 的 prop 和名为 input 的事件。后来通过 model
选项自定义 props和 event 名代替 props
/$emit
在组件上实现数据的双向绑定。
参考链接:在组件上使用v-model
但是v-model
指令在组件上只能绑定一个数据,只能使用一次,没有办法绑定多个,不过vue3中可以通过 v-model:event1=xxx 跟参数来添加多个v-model
。
.sync 修饰符
v-model
这种形式只能实现一个数据在组件上进行双向绑定,如果要实现多个数据进行双向绑定,可以是 .sync
修饰符 来简化操作。
参考链接:在组件上使用v-model
其实 .sync
和 v-model
本质上都是相同的,都是通过 props/$emit 进行父子组件之间的通信,都不是真正的双向绑定,而是语法糖。只不过.sync
更灵活一点,可以给绑定多个数据。
# $refs
/$children
和$parent
父子组件之间
父子组件通过$children
和$parent
来相互各自的实例,调用其中的方法或属性进行相互操作,$refs
则是父组件通过在子组件上绑定 ref
来获取和操作子组件的实例或DOM元素。
通过访问父组件实例 $parent
:
this.$parent // 获取父组件实例
this.$parent.name // 获取父组件数据
this.$parent.getValue() // 调用父组件方法
访问子组件实例 ref
/ $children
:
this.$refs.son // 获取子组件
this.$refs.son.age // 获取子组件数据
this.$refs.son.getAttr() // 调用子组件方法
this.$children // 获取子组件数组
this.$children[0].age // 获取子组件数据
this.$children[0].getAttr() // 调用子组件方法
# 使用中央事件总线,兄弟组件之间。
使用event Bus
中央事件总线来通信,主要是通过创建一个Vue实例来当做桥梁,然后组件中引入这个实例,通过这个实例 $emit 提交事件,然后在另一个组件中通过 $on 来接收事件。
第一步:创建中间桥梁
// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()
第二步:发送事件
// A 组件
import EventBus from '@/event-bus'
methods: {
// event bus emit
emitEvent() {
EventBus.$emit('EMIT_EVENT', data)
}
}
第三步:接收事件
// B 组件
import EventBus from '@/event-bus'
created() {
// event bus on
EventBus.$on('EMIT_EVENT', (data) => {
console.log(data)
})
}
中央事件总线适合一些好维护的小项目,但是大项目就不合适了,状态多了就难维护了,最好使用vuex状态管理器。
# 使用 $attrs
和 $listeners
跨组件
父子传值 props
和子传父 $emit
的话跨一级传递还好,但是跨多个级别传递的话就会非常麻烦,而$attrs
和 $listeners
就是他们的加强版,不需要每个都要传,只需要在每个组件上绑定这两个属性,那么绑定的组件就可以获取最顶级父组件传递的 props
值和子组件发送的 $emit
事件。
$attrs
来获取父组件传递的props值 this.$attrs.age
。
中间的组件都要 添加 v-bind='$attrs' v-on='$listeners'
这两个属性才能监听到,并且中间的组件也能监听到传递的 props 和发送的事件。
<div id="app">
<!-- 最顶级父组件中接收孙组件传递的事件 -->
<father-com :age="age" @send-msg="getMsg"></father-com>
</div>
var vm = new Vue({
el: '#app',
data: {
age: 18
},
methods: {
// 父组件获取到孙组件发送的事件
getMsg: function(name) {
console.log(name) // xxxxx
}
}
})
Vue.component('father', {
inheritAttrs: true,
template: `
<div>
<p>父组件:{{$attrs.age}}</p>
<son v-bind='$attrs' v-on='$listeners'></son>
</div>
`
})
Vue.component('son', {
inheritAttrs: true,
methods: {
getValue: function(name) {
console.log(name) // xxxxx
}
},
template: `
<div>
<p>子组件:{{$attrs.age}}</p>
<grandson v-bind='$attrs' v-on='$listeners' @send-msg="getValue"></grandson>
</div>
`
})
// 孙组件进行发送事件
Vue.component('grandson', {
inheritAttrs: true,
methods: {
sendMsg() {
this.$emit('send-msg', 'xxxxx')
},
},
template: `
<div>
<p>孙组件:{{$attrs.age}}</p>
<button @click="sendMsg">发送事件</button>
</div>
`
})
# provide
/inject
依赖注入跨组件
provide
/inject
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。可以把依赖注入看作一部分“大范围有效的 prop”。
provide
选项应该是一个对象或返回一个对象的函数。inject
选项应该是一个字符串数组,或一个对象
TIP
提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
提供属性:
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
提供方法:
// 父级组件提供 'getMap' 方法
var Provider = {
provide: function () {
return {
getName: this.getName,
msg: this.msg
}
}
methods: {
getName: function() {
console.log('chang')
},
},
}
// 子组件注入 'getName'
var Child = {
inject: ['getName', 'msg'],
created () {
console.log(this.msg) //
console.log(this.getName()) // => 'chang'
}
}
# 使用vuex和localStorage 跨组件
vuex状态管理器和localStorage本地存储都适合vue项目全局来通信,vuex比较庞大,比较适合大项目,vuex有个不好就是页面刷新随之里面的状态也被刷新了,我们可以结合localStorage来把状态存储在本地,刷新时可以先获取本地有没有,然后再保存在vuex中。
← 风格指南 组件使用v-model →