组件库二次封装——透传问题

发布于:2025-06-10 ⋅ 阅读:(27) ⋅ 点赞:(0)

属性和事件透传

属性和事件的透传需要借助一个内置 api $attrs

<my-input a="1" b="2" @change="() => {}" />
<template>
  <div class="input-wrapper">
    输入框:<el-input v-bind="$attrs"></el-input>
  </div>
</template>

<script lang="ts">
export default {
  mounted() {
    console.log("attrs", this.$attrs)
  }
}
</script>

在这里插入图片描述

但是这样直接使用 $attrs 透传的优先级会比组件自身的属性和事件优先级低。比如:

<template>
  <div class="input-wrapper">
    输入框:<el-input v-bind="$attrs"></el-input>
  </div>
</template>

<script lang="ts">
export default {
  props: ['a'],
  emits: ['change'],
  mounted() {
    console.log("attrs", this.$attrs)
  }
}
</script>

在这里插入图片描述

插槽透传

一般我们需要透传插槽的话,需要:

  <my-input v-model="text" >
    <template #prepend>
      prepend
    </template>
    <template #append>
      append
    </template>
    <template #suffix>
      suffix
    </template>
    <template #prefix>
      prefix
    </template>
  </my-input>
<template>
  <div class="input-wrapper">
    输入框:
    <el-input v-bind="$attrs">
      <template #prepend>
        <slot name="prepend"></slot>
      </template>
      <template #append>
        <slot name="append"></slot>
      </template>
      <template #suffix>
        <slot name="suffix"></slot>
      </template>
      <template #prefix>
        <slot name="prefix"></slot>
      </template>
    </el-input>
  </div>
</template>

在这里插入图片描述

这样写有一些问题,一是繁琐,二是可能传递的插槽个数不同,导致不同的渲染逻辑(业务要求)。所以我们需要使用 $slots 这个 api。

插槽的本质是一个函数。

  <my-input v-model="text" >
    <template #prepend>
      prepend
    </template>
    <template #append>
      append
    </template>
  </my-input>
<template>
  <div class="input-wrapper">
    输入框:
    <el-input v-bind="$attrs">
      <template v-for="(_, name) in $slots" #[name]="scopedData">
        <slot :name="name" v-bind="scopedData"/>
      </template>
    </el-input>
  </div>
</template>

<script lang="ts">
export default {
  mounted() {
    console.log("slots", this.$slots)
  }
}
</script>

在这里插入图片描述

ref 透传

在 vue 中 ref 是无法透传的,在 react 中可以通过 forwardRef 获取子组件。

所以我们只能将子组件内部的原组件库 ref 挂载到外部 二次封装的组件 ref 上。

<template>
  <div class="input-wrapper">
    输入框:
    <el-input ref="elInput" v-bind="$attrs">
      <template v-for="(_, name) in $slots" #[name]="scopedData">
        <slot :name="name" v-bind="scopedData"/>
      </template>
    </el-input>
  </div>
</template>

<script>
export default {
  mounted() {
    console.log("elInput", this.$refs.elInput)
    for (const elInputKey in this.$refs.elInput) {
      this[elInputKey] = this.$refs.elInput[elInputKey];
    }
  }
}
</script>