vue中组件之间的关系可以分为两类:
- 父子组件
- 非父子组件(兄弟组件,隔代组件)
1. props/$emit
父组件通过props向子组件传递数据,只读不可修改。
子组件通过$emit向父组件通信,$emit绑定一个自定义事件,当被执行时,把参数arg传递给父组件,父组件以v-on的方式监听并接收参数。
// comA.vue 父组件
<template>
<div>
<!-- 给子组件传值 , @接受子组件定义的事件-->
<comB :list='list' @onAddIndex='onAddIndex'></comB>
<p>{{activeIndex}}</p>
</div>
</template>
<script>
import comB from './comB'
export default {
name: 'comA',
components: {comB},
data() {
return {
list: ['11', '22', '33'],
activeIndex: 0
}
},
methods: {
onAddIndex(idx) {
this.activeIndex = idx + 1;
}
}
}
</script>
// comB.vue 子组件
<template>
<div>
<p v-for='(item, index) in list' :key='index' @click="addIndex(index)">{{item}}</p>
</div>
</template>
<script>
export default {
name: 'comB',
props: ['list'], //获取父组件的传值
methods: {
addIndex(index){
this.$emit('onAddIndex', index) //给父组件传值
}
}
}
</script>
2. $parent/$children
$parent 和 $children 可以访问组件的实例,拿到此组件的所有数据和方法,但是不推荐使用。
$parent是一个对象,$children拿到的是一个数组。
// comA.vue 父组件
<template>
<div>
<comB></comB>
<button @click="changeB">修改B的值</button>
</div>
</template>
<script>
import comB from './comB'
export default {
name: 'comA',
components: {comB},
data() {
return {
list: ['11', '22', '33']
}
},
methods: {
changeB() {
this.$children[0].msg = 'newVal' //给子组件传值
}
}
}
</script>
// comB.vue 子组件
<template>
<div>
<p v-for='(item, index) in parentList' :key='index'>{{item}}</p>
<p>{{msg}}</p>
</div>
</template>
<script>
export default {
name: 'comB',
data() {
return {
msg: 'oldVal'
}
},
computed: {
parentList() {
return this.$parent.list //获取父组件的值
}
}
}
</script>
3. provide / inject
父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。
不管层级有多深,解决跨级组件的通信,主要应用是子组件获取上级组件的状态,为高阶组件和组件库提供。
// comA.vue 父组件
<template>
<div>
<comB></comB>
</div>
</template>
<script>
import comB from './comB'
export default {
name: 'comA',
components: {comB},
provide: { //将属性提供给所有的子组件
name: 'aa'
}
}
</script>
// comB.vue 子组件
<template>
<div>
</div>
</template>
<script>
export default {
name: 'comB',
inject: ['name'], //获取父组件的值
mounted() {
console.log(this.name)
}
}
</script>
provide 和 inject 绑定不是可响应的。但是,如果传入一个可监听的对象,那么其对象的属性还是可响应的。
所以 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的。
4. ref / refs
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM元素。如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据。
// comA.vue 父组件
<template>
<div>
<comB ref='comB'></comB>
</div>
</template>
<script>
import comB from './comB'
export default {
name: 'comA',
components: {comB},
mounted() {
const B = this.$refs.comB;
console.log(B.name);
B.say();
}
}
</script>
// comB.vue 子组件
<template>
<div>
</div>
</template>
<script>
export default {
name: 'comB',
data() {
return {
name: 'bb'
}
},
methods: {
say() {
console.log('hello')
}
}
}
</script>
5. $emit / $on
这种方法通过一个空的Vue实例作为中央事件总线eventBus,所有组件都可以向该中心注册发送事件或接收事件,实现了任何组件间的通信。
// event-bus.js,先创建事件总线并导出
import Vue from 'vue'
export const EventBus = new Vue()
//EventBus.$emit(事件名,数据);
//EventBus.$on(事件名,data => {})
// comA.vue,包含两个组件comB和comC
<template>
<div>
<comB ref='comB'></comB>
<comC ref='comC'></comC>
</div>
</template>
<script>
import comB from './comB'
import comC from './comC'
export default {
name: 'comA',
components: {comB, comC}
}
</script>
// comB.vue,注册事件
<template>
<div>
<button @click="addNum">加法</button>
</div>
</template>
<script>
import {EventBus} from './event-bus.js'
export default {
name: 'comB',
data() {
return {
num: 1
}
},
methods: {
addNum() {
EventBus.$emit('addition', {
num: this.num++
})
}
}
}
</script>
// comC.vue,接受事件
<template>
<div>
<p>{{count}}</p>
</div>
</template>
<script>
import {EventBus} from './event-bus.js'
export default {
name: 'comB',
data() {
return {
count: 0
}
},
mounted() {
EventBus.$on('addition', param => {
this.count = this.count + param.num
})
}
}
</script>
6. vuex
当项目比较大时,可以使用状态管理器vuex。
7. $attrs / $listeners
多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。
如果仅仅是传递数据,而不做中间处理,也可以使用$attrs / $listeners。
-
$attrs:存放的是父组件中绑定的非 Props 属性,可以通过 v-bind="$attrs" 传入内部组件。 -
$listeners:包含了父作用域中的 v-on 事件监听器,可以通过 v-on="$listeners" 传入内部组件。
// comA.vue父组件
<template>
<div>
<comB
:name='name'
:age='age'
:sex='sex'
:height='height'
title='aa'></comB>
</div>
</template>
<script>
import comB from './comB'
export default {
name: 'comA',
components: {comB},
data() {
return {
name: 'xx',
age: 20,
sex: 'man',
height: 180
}
}
}
</script>
// comB.vue子组件
<template>
<div>
<comC v-bind="$attrs"></comC>
</div>
</template>
<script>
import comC from './comC'
export default {
name: 'comB',
components: {comC},
inheritAttrs: false,
props: {
name: String //name作为props属性绑定
},
created() {
console.log(this.$attrs);
//{age: 20, sex: "man", height: 180, title: "aa"},没有name
}
}
</script>
// comC.vue子组件
<template>
<div>
<p>{{$attrs}}</p>
</div>
</template>
<script>
export default {
name: 'comB',
props: {
age:Number
},
created() {
console.log(this.$attrs);
//{ "sex": "man", "height": 180, "title": "aa" },没有age
}
}
</script>
总结
- 父子组件通信:
props/$emit,$parent/$children,provide/inject,ref,$attrs/$listeners - 兄弟组件通信:
eventBus,vuex - 跨级组件通信:
eventBus,vuex,provide/inject,$attrs/$listeners