有没有办法检测作用域插槽是否已传递给组件?

Beh*_*ath 4 javascript vue.js

我有一个嵌套在几层组件内的组件。作用域槽可以向下传递给该组件。我想检测什么时候不是。

例子:

Vue.component("parent", {
    template: `
        <child>
            <template slot="expand" slot-scope="{data}" v-if="$scopedSlots.expand">
                <slot name="expand" :data="data"></slot>
            </template>
        </child>
`,
});

Vue.component("child", {
    template: `
        <div>
            <slot name="expand" :data="1"></slot>
            <div @click="$scopedSlots.expand && log()">Child</div>
        </div>
`,
    methods: {
        log() {
            console.log(this.$scopedSlots.expand);
            console.log("This shouldn't work");
        }
    }
});

new Vue({
    el: "#app",
    template: `<parent></parent>`,
})
Run Code Online (Sandbox Code Playgroud)

代码笔

在此示例中,作用域槽永远不会从根组件传递到<parent>. 结果,我本以为$scopedSlots.expandv-ifin<parent>都是假的,所以没有插槽会被传递到<child>,并且单击不会执行任何操作。

然而,这似乎是错误的,原因有两个:

  • 由于$scopedSlots.expand在内部存储为函数,因此它将始终评估为真,正如您在 CodePen 的控制台中看到的那样。
  • 看起来<template>s 总是渲染,即使使用v-if="false". 因此,即使$scopedSlots.expandinside 评估为假<parent>,空槽仍会被传递到<child>,并且无论如何, $scopedSlots.expandinside<child>都会评估为真。

有办法让这项工作发挥作用吗?

我知道我可以通过传递一个布尔属性来实现这一点,该属性表示是否处理点击。我想弄清楚是否可以从插槽本身来确定。

Dec*_*oon 5

我以前也遇到过这种情况,很烦人。

Vue 模板编译器将编译它

<template v-if="false"/>
Run Code Online (Sandbox Code Playgroud)

实质上进入

vm._e()
Run Code Online (Sandbox Code Playgroud)

其中_e是一个内部辅助方法,它是createEmptyVNode. 因此,我们立刻就知道应该期待空的 vnode,而不是错误的值。

如果我们通过查看此模板的编译代码来进一步调查

<child>
  <template slot="expand" slot-scope="scope" v-if="false"/>
</child>
Run Code Online (Sandbox Code Playgroud)

我们得到了这段代码(我对其进行了一些简化以仅显示相关内容)

_c('child', {
  scopedSlots: {
    expand(scope) {
      return undefined
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

总结一下:v-if="false"并不意味着作用域槽函数将被省略,而是将提供它,但在调用时它将返回undefined。因此,在子组件中,您可以期望$scopedSlots.expand存在,但在调用时将返回undefined

AFAIK 没有简单的方法来处理这个问题。您的示例的快速“修复”是

<div @click="$scopedSlots.expand() && log()">Child</div>
                                ^^
Run Code Online (Sandbox Code Playgroud)

但要警惕

  • 您的父组件能够处理slot-scope="{data}"不存在的情况吗{data}?最好$scopedSlots.expand(scope)使用真实scope数据进行调用。
  • $scopedSlots.expand()在渲染周期中多次调用并丢弃可能真实的结果可能会导致性能问题。

FWIW 我在将和分成单独的节点方面取得了不同的成功:v-ifslot<template>

<template v-if="$scopedSlots.expand">
  <template slot="expand" slot-scope="{data}">
    <slot name="expand" :data="data"/>
  </template>
</template>
Run Code Online (Sandbox Code Playgroud)