在开发的过程中,我们时常会用到类似到 element
或者 element-plus
的框架中的 el-form
或者 el-table
组件。在使用这些组件的时候,一般都是直接在组件内添加 el-form-item
或者 el-table-column
就可以了。
它们往往有一个特点,那就是不需要在父组件上增加太多的配置,直接通过添加子组件就可以完成多父组件的扩充。今天我们就简单来讨论一下,如何实现类似这样的功能。
现在我们假设,有这样的一个订单页面,需要在订单中进行分类显示,包括了「全部订单」「待支付」「待使用」「已完成」「售后/退款」这5个分类。
我们希望通过之前提到的类似功能,直接通过添加子组件的方式就可以添加分类,可是使用类似以下的代码。注意,以下代码均忽略了样式。
- 父组件
<!-- vue2 -->
<script>
export default {
name: 'Tabs',
data() {
return {
tabs: []
}
},
// 重点
provide() {
return {
orderTabs: this
}
},
methods: {
addTab(tabName) {
this.tabs.push({
name: tabName,
// 默认选中第一项
selected: this.tabs.length === 0
})
}
}
}
</script>
<!-- vue3 -->
<script setup>
import { ref, provide } from 'vue'
const tabs = ref([])
const addTab = (tabName) => {
tabs.value.push({
name: tabName,
selected: tabs.value.length === 0
})
}
// 重点
provide('orderTabs', {
addTab
})
</script>
<template>
<div>
<div>
<div v-for="item in tabs" :key="item.name">
{{ item.name }}
</div>
</div>
<slot></slot>
</div>
</template>
- 子组件
<!-- vue2 -->
<script>
export default {
name: 'TabItem',
props: {
name: {
type: String,
required: true
}
},
// 重点
inject: ['orderTabs'],
mounted() {
// 通过父组件的实例注册
this.orderTabs.addTab(this.name)
}
}
</script>
<!-- vue3 -->
<script setup>
import { defineProps, inject, onMounted } from 'vue'
const props = defineProps({
name: {
type: String,
required: true
}
})
// 重点
const orderTabs = inject('orderTabs', undefined)
onMounted(() => {
// 通过父组件暴露的方法注册
orderTabs && orderTabs.addTab(props.name)
})
</script>
<template>
<div>
<slot />
</div>
</template>
以上代码,我们演示了,通过在父组件中暴露一个 addTab
的方法,然后子组件生成后使用 addTab(name)
向父组件中注册。然后可以这样使用两个组件
<script>
import { Tabs, TabItem } from 'components'
export default {
components: {
Tabs,
TabItem,
}
}
</script>
<template>
<div>
<tabs>
<tab-item name="全部订单">
<!-- 全部订单列表 -->
</tab-item>
<tab-item name="待支付">
<!-- 待支付列表 -->
</tab-item>
<tab-item name="待使用">
<!-- 待使用列表 -->
</tab-item>
<tab-item name="已完成">
<!-- 已完成列表 -->
</tab-item>
<tab-item name="售后/退款">
<!-- 售后/退款列表 -->
</tab-item>
</tabs>
</div>
</template>
当然,这只是一个简单的示例,目前的代码会导致页面同时显示了五个列表。
解决这个问题可以有许多方法,比如直接在使用时通过 v-if
来判断是否显示列表,或者在父组件中暴露一个 selectedName
给子组件,子组件通过判断自己是不是当前选中的内容来判断是否显示,等等。