1. 父子组件间通信
父传子 - props
props 基本用法
- props的值有两种方式
- 1 、 字符串数组。数组中的字符就是传递是的名称
- 2 、 对象。对象可以设置传递是的类型,也可以设置默认值等
当需要对props进行类型等验证时,需要对象写法。支持一下写法
props: {
// 基础类型检查('null'匹配任何类型)
propA: Number,
// 多个可能类型
propB: [String,Number]
// 必传字符串
propC: {
type: String,
required: true
},
// 带默认值的数字
propD: {
type: Number,
default: 100
},
// 数组或对象的默认值必须从一个工厂函数获取
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}
父组件中,向子组件中传入数据
<parent>
<child :cMovices="cMovices"></child>
</parent>
/* *************************************** */
子组件中,定义传入的变量
{
props: ['cMovices'],
data() {
return {}
}
}
注意 props: {}里的变量名称需要驼峰标识。 组件标签里不能使用驼峰标识,需要加横杠
子传父 - $emit
// 子组件
<template>
<div class="app">
<input @click="sendMsg" type="button" value="给父组件传递值">
</div>
</template>
<script>
export default {
data () {
return {
//将msg传递给父组件
msg: "我是子组件的msg",
}
},
methods:{
sendMsg(){
//func: 是父组件指定的传数据绑定的函数,this.msg:子组件给父组件传递的数据
this.$emit('func',this.msg)
}
}
}
/ 子组件通过this.$emit()的方式将值传递给父组件
/ 注意:这里的func是父组件中绑定的函数名
</script>
// 父组件
<template>
<div class="app">
<child @func="getMsgFormSon"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
data () {
return {
msgFormSon: "this is msg"
}
},
components:{
child,
},
methods:{
getMsgFormSon(data){
this.msgFormSon = data
console.log(this.msgFormSon)
}
}
}
</script>
2. 多层父子嵌套(隔代)组件间通信
provide/inject
通过provide/inject 实现在父组件中通过provide提供变量,然后在子组件中通过inject注入变量
优点: 无论子组件嵌套有多深,只要调用inject那么就可以从父组件中通过provide拿取数据
例如有A、B、C三个组件
A嵌套B,B嵌套C
// A 组件
<script>
import comB from '../components/test/comB.vue'
export default {
name: "A",
provide: {
data: "AAA"
},
components:{
comB
}
}
</script>
<script>
import comC from '../components/test/comC.vue'
export default {
name: "B",
inject: ['for'],
data() {
return {
data: this.for
}
},
components: {
comC
}
}
</script>
// C.vue
<template>
<div>
{{demo}}
</div>
</template>
<script>
export default {
name: "C",
inject: ['for'],
data() {
return {
data: this.for
}
}
}
</script>
listeners
$attrs
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (
class和style除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class和style除外),并且可以通过v-bind="$attrs"传入内部组件——在创建高级别的组件时非常有用。简单的理解就是:子组件可以通过this.$attrs接收父组件传递的除了props声明的所有属性
子组件中可以通过v-bind:$attrs,将属性继续的传递下去,孙组件可以访问到父组件的属性
// 父组件
<child name="zhangsan" age="12" height="188" />
// 子组件
<template>
<grandson :$attrs height="180"/>
</template>
....
export default {
props: ['name'],
....
created() {
console.log(this.$attrs) // {age="12" height="188"}
}
}
// 孙组件
export default {
props: ['name'],
....
created() {
console.log(this.$attrs) // {age="12" height="180"}
}
}
注意
绑定的属性和 $attrs 中的属性有重复时,绑定的属性优先级会更高。
$listeners
包含了父作用域中的 (不含
.native修饰器的)v-on事件监听器。它可以通过v-on="$listeners"传入内部组件——在创建更高层次的组件时非常有用。简单理解:组件可以通过this.$listeners接收父组件传递的除了props声明的所有事件
子组件中可以通过v-on:emit触发
// 父组件
<child @click='handleclick' @click.native='handleclickNative'/>
// 子组件
<template>
<grandson v-on='$listeners' height="180"/>
</template>
....
export default {
created() {
console.log(this.$listeners) // { handleclick: fn }
}
}
// 孙组件
export default {
....
created() {
this.$emit('handleclick', 666)
}
}
注意:
绑定的事件和 $listeners 中的事件有重复时,并不会被覆盖掉,而是会类似于冒泡的方式被触发
当组件之间出现隔代关系时,它们之间的通信方式引入listeners,
3. ref属性实现通信
ref属性有多种用法
1. 获取DOM元素
ref 加在普通的元素上,用this.ref.name 获取到的是Dom元素
···
<h1 ref='h1'>2222</h1>
···
mounted() {
showDOM() {
console.log(this.$refs.h1)
}
}
2. 获取组件实例(实现组件间通信)
ref 加在子组件上,用this.$refs. 获取到的是组件实例,可以使用组件的所有方法并且获取组件中的数据。
// 子组件
export default {
data () {
return {
name: 'data-ref'
}
},
methods: {
sayRef () {
console.log('ref')
}
}
}
// 父组件
<template>
<child ref="A"></child>
</template>
<script>
export default {
mounted () {
const A = this.$refs.A;
console.log(A.name); // data-ref
comA.sayRef(); // ref
}
}
</script>
注意:
ref 需要在DOM渲染完成后才会有,在使用的时候确保DOM已经渲染完成。比如在生命周期 mounted(){} 钩子中调用,或者在 this.$nextTick(()=>{}) 中调用。
4. 全局事件总线bus - 任意组件间通信
全局事件可以实现任意组件间通信
1、其本质是定义一个对象,让所有的组件对象都能看到它
2、 并且可以使用emit去绑定和触发事件
3、 在接收数据的组件中,获取总线绑定事件
4、在发送数据组件中,获取总线触发的事件
定义事件总线
1—在main.js中设置全局事件总线
- 在main.js中创建一个Vue实例化对象,因为只有在组件对象中或Vue实例化对象才能调用emit
- 要想实现所有组件对象都能看见总线对象,所以得把总线对象放在Vue原型上
- 这个事件总线对象必须能调用emit方法(总线对象必须是Vue的实例化对象或者是组件对象)
- 在beforeCreate钩子放置$bus,在beforeCreate钩子中的this指向的是new Vue()这是实例化对象;在模板未解析前放置好后,在组件生效是就不会报错了
new Vue ({
beforeCreate() {
Vue.prototype.$bus = this
},
el: '#app',
render: h => h(App)
})
2—在接收数据组件中使用
mounthed() {
this.$bus.$on('propsBus', (data) => {
console.log('接收到了数据:', data)
})
}
// 在组件销毁之前,最好进行解绑操作,因为所有的组件都在用$bus,组件被销毁后,还占着事件,这样不太好
beforeDestroy(){
this.$bus.$off('propsBus')
}
3—在发送数据组件中使用
methods: {
this.$bus.$emit('propsBus', 7777)
}
5. 消息订阅与发布 - 任意组件间通信
1— 安装pubsub库
npm i pubsub-js
2—引入
import pubsub from 'pubsub-js'
3— 订阅消息
接收数据: 在需要接收数据的组件中订阅消息,订阅回调函数留在组件自身
methods(){
demo(_,data){......}
}
mounted() {
this.pubId = pubsub.subscribe('msgName',this.demo) //订阅消息执行demo回调函数
}
注意:回调函数中的第一个参数固定是消息名称,是固定写法,第二个参数才是传过来的数据,基本上这个 函数的回调方法的第一个参数msgName是没用到的,所以可以使用英文下划线代替 _
this.pubId = pubsub.subscribe('msgName',(_,data)=>{})
最好在beforeDestroy钩子中,用PubSub.unsubscribe(pubId)去取消订阅。
beforeDestroy() {
pubsub.unsubscribe(this.pubId) //接收的是一个订阅消息的Id,不是消息名称
}
4—发布消息
发送数据
pubsub.publish('msgName',data)
部分笔记来源于codewhy老师