让我们来看看Vue中的无效插槽模式,看看它可以帮助解决的问题。
通过 Vue.js 2.3.0 引入,范围式插槽显著提高了组件的可重复使用性,例如,无渲染组件模式出现并解决了提供可重复使用的行为和可插入的演示的问题。
在这里,我们将看到如何解决相反的问题:提供可重复使用的演示和可插入的行为。
无线元件
此模式适用于实现复杂行为和可自定义演示的组件。
这样做:
- 组件实现了所有行为
- 目标插槽负责渲染
- 倒退内容确保该组件可以被用在盒子之外
举一个例子:一个组件执行Ajax请求,并且有一个插槽来显示结果.该组件处理Ajax请求和加载状态,而默认插槽提供演示文稿。
以下是简化实施:
1<template>
2 <div>
3 <slot v-if="loading" name="loading">
4 <div>Loading ...</div>
5 </slot>
6 <slot v-else v-bind={data}>
7 </slot>
8 </div>
9</template>
10
11<script>
12export default {
13 props: ["url"],
14 data: () => ({
15 loading: true,
16 data: null
17 }),
18 async created() {
19 this.data = await fetch(this.url);
20 this.loading = false;
21 }
22};
23</script>
使用:
1<lazy-loading url="https://server/api/data">
2 <template #default="{ data }">
3 <div>{{ data }}</div>
4 </template>
5</lazy-loading>
对于这个模式的原始帖子, 点击这里 。
不同的问题
如果问题是相反的:想象一个组件的主要特征是它的呈现,另一方面,行为应该是可自定义的。
假设您正在创建基于 SVG 的树组件,如下:
您想要提供 SVG 显示和行为,例如退出节点和单击文本突出。
当您决定不硬编码这些行为时,会出现问题,并允许组件的用户自由排列它们。
暴露这些行为的一个简单的解决方案是将方法和事件添加到组件中。
你会以某种方式结束:
1<script>
2export default {
3 mounted() {
4 // pseudo code
5 nodes.on('click',(node) => this.$emit('click', node));
6 },
7 methods: {
8 expandNode(node) {
9 //...
10 },
11 retractNode(node) {
12 //...
13 },
14 highlightText(node) {
15 //...
16 },
17 }
18};
19</script>
要将行为添加到组件中,组件的消费者需要在母组件中使用一个ref,例如:
1<template>
2 <tree ref="tree" @click="onClick"></tree>
3</template>
4
5<script>
6export default {
7 methods: {
8 onClick(node) {
9 this.$refs.tree.retractNode(node);
10 }
11 }
12};
13</script>
这种方法有几个缺点:
不能再提供默认行为 2 行为最终成为你需要复制的烹饪书 3 行为不能重复使用
让我们看看无效插槽如何解决这些问题。
无人机 Slots
行为基本上是证明对事件的反应,所以让我们创建一个可以访问事件和组件方法的插槽:
1<template>
2 <div>
3 <slot name="behavior" :on="on" :actions="actions">
4 </slot>
5 </div>
6</template>
7
8<script>
9export default {
10 methods: {
11 expandNode(node) { },
12 retractNode(node) { },
13 //...
14 },
15 computed:{
16 actions() {
17 const {expandNode, retractNode} = this;
18 return {expandNode, retractNode};
19 },
20 on() {
21 return this.$on.bind(this);
22 }
23 }
24};
25</script>
on
属性是母组件的$on
方法,因此可以聆听所有事件。
实施一个行为可以做作为一个无渲染的组件. 让我们写出扩展点击的组件:
1export default {
2 props: ['on','action']
3
4 render: () => null,
5
6 created() {
7 this.on("click", (node) => {
8 this.actions.expandNode(node);
9 });
10 }
11};
使用:
1<tree>
2 <template #behavior="{ on, actions }">
3 <expand-on-click v-bind="{ on, actions }"/>
4 </template>
5</tree>
该解决方案的主要优点是:
- 通过提供反馈内容提供默认行为的可能性:
例如,通过将图表组件宣布为:
1<template>
2 <div>
3 <slot name="behavior" :on="on" :actions="actions">
4 <expand-on-click v-bind="{ on, actions }"/>
5 </slot>
6 </div>
7</template>
- 创建可重复使用的组件的可能性,该组件的用户可以选择
让我们来考虑一个突出的hover组件:
1export default {
2 props: ['on','action']
3
4 render: () => null,
5
6 created() {
7 this.on("hover", (node) => {
8 this.actions.highlight(node);
9 });
10 }
11};
规范性行为:
1<tree>
2 <template #behavior="{ on, actions }">
3 <highlight-on-hover v-bind="{ on, actions }"/>
4 </template>
5</tree>
- 行为插槽是可编译的
让我们添加两个预先定义的行为:
1<tree>
2 <template #behavior="{ on, actions }">
3 <expand-on-click v-bind="{ on, actions }"/>
4 <highlight-on-hover v-bind="{ on, actions }"/>
5 </template>
6</tree>
解决方案的可读性
作为行为的组成部分是自我解释的。
- 可扩展性
on
属性允许访问所有组件事件. 新的事件默认情况下可用于插槽。
包装上
Renderless 插槽提供了一个有趣的替代方案,以揭示组件中的方法和事件,它们提供了更可读和可重复使用的代码。
實施此模式的樹木組件的代碼可在github上找到: Vue.D3.tree