组件间通信
1、组件间通信基本原则:
(1)、不要在子组件中直接修改父组件的状态数据
(2)、数据在哪, 更新数据的行为(函数)就应该定义在哪
2、vue 组件间通信的五种方式:
(1)、props
(2)、自定义事件
(3)、消息订阅与发布
(4)、slot
(5)、vuex
组件间通信方式一:props
1、在父组件传递属性
<List name='tom' :age='3' :set-name='setName'></List >
2、 在子组件内接收所有的属性
(1)、方式一: 只指定名称
props:['name','age','setName']
(2)、方式二: 指定名称和类型
props:{
name:String,
age:Number,
setNmae:Function
}
(3)、方式三: 指定名称、类型、必要性和默认值
props:{
name:{type:String,required:true,default:xxx},
}
注意:
- 此方式用于父组件向子组件传递数据
- 所有标签属性都会成为组件对象的属性, 模板页面可以直接引用
- 问题:
a. 如果需要向非子后代传递数据必须多层逐层传递
b. 兄弟组件间也不能直接 props 通信, 必须借助父组件才可以
案例:
在父组件App.vue中传递属性
<Item :comment="comment" :deleteComment="deleteComment" :index="index"/>
<List :comments="comments" :deleteComment="deleteComment"/>
<Add :addComment="addComment"/>
在子组件List.vue中接收属性
//声明接收属性:这个属性就会成为组件对象的属性
props: ['comments','deleteComment'],//只是指定属性名
在子组件Item.vue中接收属性
props: {//指定属性名和属性值的类型
comment: Object,
deleteComment: Function,
index: Number
}
在子组件Add.vue中接收属性
props:{
addComment:{//指定属性名、属性值的类型、必要性
type: Function,
required: true
}
}
组件间通信方式二:自定义事件
1、绑定事件监听
方式一: 通过 v-on 绑定
@delete_todo="deleteTodo"
方式二: 通过$on()
this.$refs.xxx.$on('delete_todo',function(todo){
this.deleteTodo(todo)
})
2、 触发事件
this.$emit(eventName,data)
注意:
- 此方式只用于子组件向父组件发送消息(数据)
- 问题:隔代组件或兄弟组件间通信此种方式不合适
案例:
在父组件App.vue中绑定事件监听
<!--给TodoHeader标签对象绑定addTodo事件监听-->
<TodoHeader @addTodo="addTodo"/>
在子组件TodoHeader.vue中触发事件
methods:{
addItem(){
//触发自定义事件:addTodo
this.$emit('addTodo',todo)
}
}
组件间通信方式三:消息订阅与发布
下载PubSubJS:npm install --save pubsub-js
1、订阅消息
PubSub.subscribe('msg',function(msg,data){
})
2、发布消息
PubSub.publish('msg',data)
注意
优点: 此方式可实现任意关系组件间通信(数据)
案例:
在父组件App.vue中订阅消息
import PubSub from 'pubsub-js'
mounted(){
//订阅消息
PubSub.subscribe('deleteTodo', (msg,index) =>{
this.deleteTodo(index)
})
}
在子组件TodoItem.vue中发布消息
import PubSub from 'pubsub-js'
methods:{
deleteItem(){
const {todo,index} = this
if(window.confirm(`确认删除${todo.title}吗?`)){
//发布消息
PubSub.publish('deleteTodo',index)
}
}
}
组件间通信方式四:slot
此方式用于父组件向子组件传递标签数据
1、子组件:Child.vue
<template>
<div>
<slot name="xxx">不确定的标签结构</slot>
<div>组件确定的标签结构</div>
<slot name="yyy">不确定的标签结构</slot>
</div>
</template>
2、父组件:Parent.vue
<child>
<div slot="xxx">xxx对应的标签结构</div>
<div slot="yyy">yyyy对应的标签结构</div>
</child>
主要用于某一标签被反复使用时,可以使用slot插槽
案例:
在父组件App.vue中定义slot插槽
<todo-footer>
<input type="checkbox" v-model="isAllCheck" slot="checkAll"/>
<span slot="count">已完成{{completeSize}} / 全部{{todos.length}}</span>
<button class="btn btn-danger" v-show="completeSize" @click="deleteCompleteTodos" slot="delete">清除已完成任务</button>
</todo-footer>
在子组件TodoFooter.vue中使用slot插槽
<slot name="checkAll"></slot>
<slot name="count"></slot>
<slot name="delete"></slot>