非父子组件通讯
# 非父子组件通讯
在开发中,我们构建了组件树之后,除了父子组件之间的通信之外,还会有非父子组件之间的通信,这里我们主要讲两种方式。
# Provide/Inject—依赖注入
# 介绍
比如有一些深度嵌套的组件,子孙组件想要获取爷父组件的部分内容。在这种情况下,如果我们仍然将props沿着组件链逐级传递下 去,就会非常的麻烦。对于这种情况下,我们可以使用 Provide 和 Inject:
- 无论层级结构有多深,父组件都可以作为其所有子组件的依赖提供者
- 父组件有一个 provide 选项来提供数据;
- 子组件有一个 inject 选项来开始使用这些数据
# 注意
- provide 函数绑定 this
- computed 响应式
# 示例
App.vue
<template>
<div>
<home></home>
<button @click="addName">+name</button>
</div>
</template>
<script>
import Home from './Home.vue'
import { computed } from 'vue'
export default {
components: {
Home,
},
// provide 对象中的 this 为 undefined,
// 为了绑定 Vue 实例,可以使用类似 data 函数一样的方式实现
// provide: { name: 'why' },
provide() {
return {
name: 'why',
age: 18,
// length 需要在 name 的长度变化时改变,可以使用计算属性,computed 返回的是 ref 对象,需要 .value 获取值
length: computed(() => this.names.length), // ref对象 .value
}
},
data() {
return {
names: ['abc', 'cba', 'nba'],
}
},
methods: {
addName() {
this.names.push('why')
console.log(this.names)
},
},
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Home.vue
<template>
<div>
<home-content></home-content>
</div>
</template>
<script>
import HomeContent from './HomeContent.vue';
export default {
components: {
HomeContent
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HomeContent.vue
<template>
<div>
HomeContent: {{name}} - {{age}} - {{length.value}}
</div>
</template>
<script>
export default {
// 也不知道从哪注入的?是不是全局的???
inject: ["name", "age", "length"],
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Mitt 全局事件总线
替代Vue实例,其实可以获取到Vue实例,就是比较麻烦。
Vue3从实例中移除了 $on、$off 和 $once 方法,所以我们如果希望继续使用全局事件总线,要通过第三方的库: Vue3官方有推荐一些库,例如 mitt (opens new window)(推荐) 或 tiny-emitter (opens new window);
# 安装
npm install mitt
1
# 封装 eventbus.js & constant.js
eventbus.js mitt 封装
import mitt from 'mitt'
// 可以返回多个对象
const emitter = mitt()
// export const emitter1 = mitt();
// export const emitter2 = mitt();
// export const emitter3 = mitt();
export default emitter
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
constant.js 事件名常量
export const WHY_EVENT = "why";
1
# 方法
emit
on
off:取消掉之前注册的函数监听
// 取消 emitter 中所有监听 emitter.all.clear() // 定义一个函数 function onFoo() {} emitter.on('foo', onFoo) // 监听 emitter.on('foo', onFoo) // 取消监听1
2
3
4
5
6
7
# 示例
App.vue
<template>
<div>
<home/>
<about/>
</div>
</template>
<script>
import Home from './Home.vue';
import About from './About.vue';
export default {
components: {
Home,
About
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Home.vue
<template>
<div>
<home-content></home-content>
</div>
</template>
<script>
import HomeContent from './HomeContent.vue';
export default {
components: {
HomeContent
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HomeContent.vue
<template>
<div>
</div>
</template>
<script>
import emitter from './utils/eventbus';
export default {
created() {
emitter.on("why", (info) => {
console.log("why:", info);
});
emitter.on("kobe", (info) => {
console.log("kobe:", info);
});
emitter.on("*", (type, info) => {
console.log("* listener:", type, info);
})
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
About.vue
<template>
<div>
<button @click="btnClick">按钮点击</button>
</div>
</template>
<script>
import emitter from './utils/eventbus';
export default {
methods: {
btnClick() {
console.log("about按钮的点击");
emitter.emit("why", {name: "why", age: 18});
// emitter.emit("kobe", {name: "kobe", age: 30});
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
编辑 (opens new window)
上次更新: 2022/03/23, 17:55:39