持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情
一、前言
在vue项目中关于Vue组件间的通信方式有很多种,也是面试高频问点,今天就对这些通信方式做一个简单的总结和归纳。
二、归纳一下什么情况用哪种
父子组件
- props
- vue自定义事件
- v-model
- sync
$ref,$children与$parent- 插槽 ==> 作用域插槽
祖孙组件
$attrs与$listeners- provide与inject
兄弟或其它/任意
- 全局事件总线
- Vuex
三、组件间通信方式具体
1. props/$emit
- 实现父向子通信: 属性值是非函数
- 实现子向父通信: 属性值是函数
//父组件
<template>
<son :msg="msg" @search-list="searchList"></son>
</template>
export defalut{
data(){
name:'Father'
return{
msg:'lucky'
searchForm:{}
}
},
methods:{
searchList(val){
//val即可拿到子组件传过来的参数
this.searchForm = val
}
}
}
//子组件
<template>
<div @click="search"></div>
</template>
export defalut{
data(){
name:'Son',
//子组件用父组件传过来的
props:{
msg:{
type:String
default:''
}
}
},
method:{
saerch(){
//this.$emit(方法名,参数) 子组件向父组件
this.$emit('search-list',list)
}
}
}
2. vue自定义事件
用来实现子组件向父组件通信
- 实现
- 父组件中绑定自定义事件监听:
<Child @eventName="callback"> - 子组件中分发事件
this.$emit('eventName', data)
- 父组件中绑定自定义事件监听:
- 应用
- elment-ui的组件的事件监听语法都用的是自定义事件,项目里自定义事件用的也比较多
3. 全局事件总线 ===> 消息订阅与发布
实现任意组件间通信
- 实现
- 将入口js中的vm作为全局事件总线对象:
beforeCreate() {Vue.prototype.$bus = this} - 分发事件/传递数据的组件:
this.$bus.$emit('eventName', data) - 处理事件/接收数据的组件:
this.$bus.$on('eventName', (data) => {})
- 将入口js中的vm作为全局事件总线对象:
- 应用
- 前台项目中使用全局事件总线
4. v-model
实现父子之间相互通信/同步
- 实现
//父组件
<CustomInput v-model="name"/>
<!-- 等价于 -->
<CustomInput :value="name" @input="name=$event"/>
//子组件
<input type="text" :value="value" @input="$emit('input', $event.target.value)">
props: ['value']
- 应用
- element-ui中的表单项相关组件都用了v-model: Input / Select / Checkbox / Radio
- 注意
- 组件标签上的v-model的本质: 动态value属性与自定义input监听来接收子组件分发的数据更新父组件数据
5. .sync
实现父子之间相互通信/同步(在原本父向子的基础上增加子向父)
- 实现
- 组件标签的属性上使用.sync的本质: 通过事件监听来接收子组件分发过来的数据并更新父组件的数据
//父组件
<child :money.sync="total"/>
<!-- 等价于 -->
<Child :money="total" @update:money="total=$event"/>
data () {
return {
total: 1000
}
//子组件
<button @click="$emit('update:money', money-100)">花钱</button>
props: ['money']
- 应用
- element-ui在有显示隐藏的组件上: Dialog / Drawer
6. $attrs与$listeners
- $attrs
- 实现当前组件的父组件向当前组件的子组件通信
- 它是包含所有父组件传入的标签属性(排除props声明, class与style的属性)的对象
- 使用: 通过
v-bind="$attrs"将父组件传入的n个属性数据传递给当前组件的子组件
- $listeners
- 实现当前组件的子组件向当前组件的父组件通信
$listeners是包含所有父组件传入的自定义事件监听名与对应回调函数的对象- 使用: 通过
v-on="$listeners"将父组件绑定给当前组件的事件监听绑定给当前组件的子组件
//父组件
<template>
<son1 :title="title" :msg="msg"></son1>
</template>
//子组件1 son1
<template>
<div>
<div>{{title}}</div>
<div>{{$attrs.msg}}</div>
<son2 v-bind="$attrs"></son1>
</div>
</template>
<script>
export default{
props:['title']
}
</script>
//子组件2 son2
<template>
<div>
<div>{{$attrs.title}}</div>
<div>{{$attrs.msg}}</div>
</div>
</template>
7. $refs & $children & $parent
$children- 实现父组件向多个子组件通信
$children是所有直接子组件对象的数组- 使用: 通过
this.$children遍历子组件对象, 从而可以更新多个子组件的数据
$parent- 实现子组件向父组件通信
$parent是当前组件的父组件对象- 使用: 通过
this.$parent得到父组件对象, 从而可以更新父组件的数据
//父组件
<template>
<div>
<div>{{msg}}</div>
<son></son>
<botton @click="change">改变子组件</button>
</div>
</template>
export default{
name:'Father',
data(){
return{
masg:'lucky'
}
},
method:{
change(){
//获取第一个子组件的数据
this.$children[0].msg = 'this is lucky'
}
}
}
//子组件
<template>
<div>
<div>{{msg}}</div>
<div>获取父组件的值:parentValue()</div>
</div>
</template>
export default{
name:'Son',
data(){
return{
masg:'lucky'
}
},
computed:{
parentValue(){
return this.$parent.msg
}
}
}
$refs- 实现父组件向指定子组件通信
- $refs是包含所有有ref属性的标签对象或组件对象的容器对象
- 使用: 通过
this.$refs.child得到子组件对象, 从而可以直接更新其数据或调用其方法更新数据
//子组件
export deault{
data(){
return{
msg:'lucky'
}
}
methods:{
do(){
console.log('what are you doing')
}
}
}
//父组件
<template>
<son ref='son'></son>
</temaplate>
<script>
export default{
mounted(){
let msg = this.$refs.msg
this.$refs.son.do()
}
}
</script>
8. provide与inject
实现祖孙组件间直接通信
- 使用
- 在祖组件中通过provide配置向后代组件提供数据
- 在后代组件中通过inject配置来声明接收数据
//顶层父组件
export default{
provide(){
mag:'lucky'
}
}
//后代组件 不限于多少层嵌套
export default{
inject:{'msg'},
data(){
return{
msg:this.msg
}
}
}
- 注意
- 不太建议在应用开发中使用, 一般用来封装vue插件
- provide提供的数据本身不是响应式的 ==> 父组件更新了数据, 后代组件不会变化
- provide提供的数据对象内部是响应式的 ==> 父组件更新了数据, 后代组件也会变化
- 应用
- element-ui中的Form组件中使用了provide和inject
9. vuex
vuex用来统一管理多个组件共享的状态数据,任意要进行通信的2个组件利用vuex就可以实现
- 实现
- A组件触发action或mutation调用, 将数据保存到vuex的状态中
- B组件读取vuex中的state或getters数据, 得到最新保存的数据进行显示
10. 插槽 ==> 作用域插槽slot-scope
实现父组件向子组件传递标签内容
- 什么情况下使用作用域插槽?
- 父组件需要向子组件传递标签结构内容,但决定父组件传递怎样标签结构的数据在子组件中
- 实现
//子组件:
<slot :row="item" :$index="index"> <!-- slot的属性会自动传递给父组件 -->
</slot>
//父组件:
<template slot-scope="{row, $index}">
<span>{{$index+1}}</span>
<span :style="{color: $index%2===1 ? 'blue' : 'green'}" >{{row.text}}</span>
</template>
- 应用
- element-ui中的Table组件
好了,以上就是本篇文章的分享,感谢阅读!