Vue:忽略已定义插槽的插槽包装器

Posted

技术标签:

【中文标题】Vue:忽略已定义插槽的插槽包装器【英文标题】:Vue: wrapper for slots ignoring already-defined slots 【发布时间】:2021-01-20 14:07:01 【问题描述】:

Vue 对象有一个非常有用的成员,叫做$attrs。 $attrs 所做的是包含所有未被识别为当前组件道具的属性。 $attrs 的一个很好的例子是 here。

我想知道$attrs 是否有对应于$scopedSlots 的等价物。我目前正在使用类似于 https://***.com/a/50892881/6100005 的第一个建议的方法。 $scopedSlots 的问题是它也通过了已经定义的槽。要在此处使用该示例:


<template>
    <wrapper>
      <b-table :foo="foo" v-bind="$attrs" v-on="$listeners">
        <template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>
      </b-table>
      <slot name="mySlot"></slot>
    </wrapper>
</template>

<script>
export default 
    props: 
        // let's pretend that `b-table` has a prop `foo` that is default `false`
        foo: 
            type: boolean,
            default: true,
        
    

</script>

现在,由于$attrs 的行为,foo 不会被绑定两次,但mySlot 将被发送到wrapperb-table

那么,我怎么能把所有的插槽都传下来除了我定义自己的插槽

我的一个想法是拥有

computed: 
   bTableSlots() 
       Object.keys(this.$scopedSlots)
          .filter( key => key!=='mySlot' )
          .reduce( (res, key) => (res[key] = this.$scopedSlots[key], res),  );
    

然后

        <template v-for="(_, slot) of bTableSlots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>

想知道是否有更好的方法来做到这一点。谢谢!

【问题讨论】:

【参考方案1】:

那么除了我自己定义的那个,怎么能把所有的槽都传下去呢?

为什么这甚至是一个问题?如果&lt;b-table&gt; 没有名称为mySlot 的命名槽,它将忽略它——最后它只是它的$scopedSlots 属性中的一个条目,组件永远不会访问。而且因为作用域插槽是作为函数传递的,所以您无需支付任何额外价格。

当 Vue 编译器看到 slot 的内容 (&lt;template #slotName&gt;) 时,它会获取该内容并将其编译成 VNodes 的 ArrayVNodes 的返回 Array 的函数并传递下去到子组件。您的子组件进一步传递它的事实不会产生任何额外的成本。它是对现有数组的引用或对现有函数的引用(对于作用域插槽),在这两种情况下,如果组件进一步向下不知道插槽具有完全相同的名称,则不存在额外费用,除了$scopedSlots中多了一个条目...

如果您觉得这是一个问题,恐怕过滤是唯一的方法 - 您可以按照自己的方式或通过一些辅助数组 [ 'slot1', 'slot2' ] 定义所有现有的 b-table 插槽并过滤其他所有内容(恕我直言,不是更好)每次b-table添加新插槽时都需要更新组件)......

我理解这个想法并看到与 $attrs 的相似之处 - 组件上可能有 $unknownSlots 之类的东西来保存当前组件未定义的插槽,但现在 Vue 公共 API 中没有这样的东西。 .

【讨论】:

我更担心性能而不是命名冲突。你是说因为$scopedSlots 是一个以names 为键的对象,如果我向其中添加一个未使用的条目,它对性能的影响会最小吗?如果是这样,这是一个可以接受的解决方案 如果添加“未使用”插槽,代价将是编译时间(构建时)和 JS 引擎解析传递的插槽的渲染函数所需的内存 + 时间。但是如果你传递“mySlot”,创建的函数将被你的组件使用(没有浪费)。该函数传递到'b-table'$scopedSlots这一事实是无关紧要的......它是相同的函数 @MaxCoplan 扩展了我的答案,重点关注性能。希望现在很清楚......

以上是关于Vue:忽略已定义插槽的插槽包装器的主要内容,如果未能解决你的问题,请参考以下文章

Vue 3 从插槽访问子组件

Vue渲染功能:在没有包装器的情况下将插槽包含到子组件中

Vue.js(17)之 插槽

如何封装/包装一个 VueJS 组件?

如何正确使用 vue js Web 组件内部的插槽并应用样式

vue中slot插槽的使用