初探 Vue 3.0 的组装式 API(四)

这次再说一说父子组件之间的传值与通信场景。

前文:

(四)组件通信与跨级传值

1. emit 与 slots

Vue3 中从父级向子级传值与 Vue2 一样,就是在模板里创建子组件的标签时,通过 v-bind:/: 指令传值。

而父组件通过 v-on:/@ 绑定的事件监听器,需要在子组件触发事件时,需要通过 props 之后的第二个参数 context 调用:

1
2
3
4
5
6
7
// child.vue
export default {
setup(props, context) {
const onClick = () => context.emit('click-child');
// ...
},
};

同时,context 中还提供了我们操作子组件时经常需要用到的插槽 slots

1
2
3
4
5
6
7
8
// parent.vue
export default {
setup(props, context) {
const { slots } = context;
const children = (slots.default ? slots.default() : []);
// ...
},
};

顺带一提,由于 context 不是响应式的,所以我们可以直接在参数表中,使用解构赋值取出 emitslots

1
2
3
4
5
6
// parent.vue
export default {
setup(props, { emit, slots }) {
// ...
},
};

不过需要注意,slots 的属性值也可能随时发生变化,但它本身并非响应式数据。为了确保你的组件随时获得最新的插槽状态,建议在 onUpdated 中操作其属性值。

2. 跨级传值

我们有时会遇到类似这样的需求(比如:Tab 与 TabPane),所有子组件需要根据同一个父组件/祖先组件的状态调整自身的状态,做到跨级数据联动。

a) Vue2 方案

在 Vue2 中,是被打散在不同构造参数中的 provideinject 属性实现的:

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
// tab.vue
exports default {
data() {
return {
tabState: {
// ...
};
},
},
// ...
provide() {
// 向子孙组件提供 tabState
const { tabState } = this;
return {
tabState,
};
},
};

// tab-pane.vue
exports default {
// 告知可以注入来自祖先的 tabState
inject: [ tabState ],
// ...
methods: {
getTabState() {
// 取用 provide 过来的数据
const { tabState } = this;
// ...
},
},
};

可以看到,又是被打散到不同 data/methods/computed 段落的零散数据,靠 this 强行绑定到一起。

b) Vue3 方案

在 Vue3 中,则是通过 provideinject 函数,更直观地组装出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// tab.vue
import { reactive, provide } from 'vue';
exports default {
setup() {
const tabState = reactive({
// ...
});
provide('tabState', tabState);
// ...
},
};

// tab-pane.vue
import { inject } from 'vue';
exports default {
setup() {
const tabState = inject('tabState');
// 还可以传入一个默认值: const tabState = inject('tabState', { });
// ...
},
};

传入响应式数据后,在子孙组件都可以方便地取到,以至于甚至可以替代很多 Vuex 的使用场景。


这一系列至此告一段落,Vue3 的组装 API 使用起来还是相对简单的,大部分问题都能查阅官方文档解决。

如果还有其他问题也欢迎留言联系,或者加入QQ群: 121757667 ,一起讨论和学习!