Slot invoked outside of the render function - 解决使用 slot 时遇到的问题

1,542 阅读1分钟

在写代码的时候,偶然遇到了这个问题。

Slot "default" invoked outside of the render function: this will not track dependencies used in the slot. Invoke the slot function inside the render function instead.

看样子是关于插槽的问题。

最后发现了问题所在:useSlots 返回的 slots 是一个代理对象,也就是说它是具有响应式的。

定义一个父组件。它会动态改变 msg,来使插槽改变。

它使用了一个 v-button 组件。

<script setup lang="ts">
import { ref } from 'vue';
import vButton from './components/button.vue'

const msg = ref('123')
</script>
<template>
  <input type="text" v-model="msg" />
  <br />
  <v-button>{{msg}}</v-button>
</template>
<style lang="scss" scoped>

</style>

v-button 中使用 computed 动态计算 slot 的内容

我们有一个子组件 button.vue。

如果像一开始那样写,相当于只是给 isShowIcon 赋了一个初始值。

如果想要具有响应式,需要放在 computed 计算属性里。(因为计算返回的是一个引用地址,引用地址始终没变,所以不会重新触发 computed 计算函数。)

<script setup lang="ts">
import { computed, useSlots } from 'vue';

/** 获取插槽 */
const slots = useSlots()

- // const isShowIcon = !(slots.default && slots.default() && slots.default()[0].children)

+ const isShowIcon = computed(() => {
+   return !(slots.default && slots.default() && slots.default()[0].children)
+ })

</script>
<template>
  <button>
    <span v-show="isShowIcon">🤩</span>
    <slot />
  </button>
</template>
<style lang="scss" scoped>

</style>

v-button 中直接模板上使用 $slots

除了在 computed 中使用 slot,也可以直接再模板上使用 $slots。

注意前缀 $

<template>
  <button>
    <span v-show="!($slots.default && $slots.default() && $slots.default()[0].children)">🤩</span>
    <slot />
  </button>
</template>

总结

只有这样,当动态输入改变 msg,按钮上的内容为 "" 时,“icon”文字就会显示了。

结尾来张动图。当输入框内的内容为空时,按钮的 icon 就会显示。

2023-01-16-10-50-48.gif