隐藏小技巧!妙用provide/inject封装高级容器组件

210 阅读2分钟

前言

下面谈谈如何使用provideinject来封装一个列表项信息展示高级组件。比如UI组件库的表单组件除了展示表单输入项,也可以进行表单项的信息展示。本文的目的也是通过这个列表项信息展示的栗子,来使大家对高级组件封装provide/inject能有更深的理解。

关键词: vue3、ts、provide/inject、高级组件封装,列表项信息展示。

组件封装

下面封装了一个列表项容器组件VList和列表项组件VListItem

1.VList容器组件负责定义向所有VListItem组件传递的公共参数,比如labelWidth标签宽度和suffix标签后缀,从而不需要每一个VListItem列表项都传递一遍。

2.VListItem则只负责维护自身组件的内部状态。

VList列表容器组件

<template>
  <div class="v-list">
    <slot></slot>
  </div>
</template>

<script setup lang="ts">
import { provide } from 'vue'
import type { VListProps } from './index.d'
import { Props } from './constant'

const props = withDefaults(defineProps<VListProps>(), {
  labelWidth: '100px'
})

provide(Props, props)

</script>

./index.d文件

export type VListProps = {
  suffix?: string,
  labelWidth?: string
}

./constant文件

import type { InjectionKey  } from 'vue'
import type { VListProps } from './index.d'

export const Props = Symbol() as InjectionKey<VListProps>

provideinject 通常会在不同的组件中运行。要正确地为注入的值标记类型,Vue 提供了一个 InjectionKey 接口,它是一个继承自 Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型。参考:为 provide / inject 标注类型

VListItem列表项组件

<template>
  <div class="v-list-item">
    <div class="label">
      {{ label }}
      <span class="suffix" v-if="vListProps.suffix">{{ vListProps.suffix }}</span>
    </div>
    <div class="value">{{ value }}</div>
  </div>
</template>

<script setup lang="ts">
import { inject, computed } from 'vue'
import { Props } from './constant'

const vListProps = inject(Props, {})

const props = defineProps<{
  label: string,
  value: string,
  labelWidth?: string
}>()

const labelWidth = computed(() => {
  // 优先使用自身组件的label宽度
  return props.labelWidth || vListProps.labelWidth
})
</script>

<style scoped lang="scss">
.v-list-item {
  display: flex;
  padding: 10px;
  .label {
    width: v-bind(labelWidth);
  }
}
</style>

在列表项组件注入了列表容器组件的Props(包括labelWidthsuffix参数),并在组件内部定义了一个计算属性labelWidth优先使用列表容器组件labelWidth,最后在css中通过v-bind(labelWidth)应用样式;suffix参数(标签后缀)的道理也是一样。看到这,相信大家完全有能力进行举一反三,实现更多其他需求!

用法

<script setup lang="ts">
import { VList, VListItem } from '@/components/VList';

</script>

<template>
  <div>
    <VList suffix=":" label-width="150px">
      <VListItem label="姓名" value="张三"></VListItem>
      <VListItem label="性别" value="男"></VListItem>
      <VListItem label="手机号码" value="152********"></VListItem>
    </VList>
  </div>
</template>

components/VList/index.ts文件

export { default as VList } from './VList.vue'
export { default as VListItem } from './VListItem.vue'

页面效果:

image.png

总结

在本例子中的核心思想就是把所有子组件的公共参数提取到容器组件当中,再通过provide向所有子组件注入所有公共参数;在子组件中通过inject接收父组件注入的参数。从而达到简化代码,提升代码可维护性的目的。

掘金2024年度人气创作者打榜中,快来帮我打榜吧~ activity.juejin.cn/rank/2024/w…