如何解决Vue在渲染函数之外调用插槽的问题

更新日期: 2022-09-22阅读: 580标签: 插槽

如果你是用 vue 来开发项目的,那么,你曾经有可能访问 slot.default() 遇到如下问题:

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. 

本文本中,将会解释这个错误背后的原因以及如何解决这个问题。

插槽的调用需要发生在渲染函数或模板中。要抑制这个错误,我们只需要把代码移到一个计算的属性或从模板或渲染函数中调用的方法中。

“this will not track dependencies used in the slot” 指的是什么?

错误信息解释了问题产生的本质原因,但这个提示不是很清晰,无法帮助我们界定问题的本质。下面,我们来详细介绍下错误背后的原因产生。

this will not track dependencies used in the slot.

经过一些调查,我做了一个可复现的代码,并理解了在渲染函数之外使用slots.default()语法的含义。为了理解这个问题,我们先复习一下 Vue 的响应式原理。

Vue 的响应式性系统允许我们声明属性、数据和计算属性,而不需要跟踪它们的变化。响应式性系统在幕后工作,确保我们的变量始终是最新的。

在Vue框架内,最常见的响应式特征的情况是使用 computed:

计算属性指的是一个变量,它可以被用来以有效和响应式的方式修改和操作你的组件中的数据和属性。

计算属性的一个简单例子是博客片段,我们把一篇完整的博客文章作为属性传递,并把它截断成一定数量的字符。另一个更常见的例子是一个简单的变量,用来定义一个按钮的文本,根据当前的状态 "显示 "或 "隐藏"。

举例来说,在 "expanded"的值被改变之前,下面的属性将永远不会再被运行。

const buttonText = computed( () => {
  return expanded.value ? 'Show less' : 'Show more';
});

除非 expanded 的值发生变化,否则上述方法不会再被触发。Vue 在幕后所做的观察 expanded 变量的工作就是所谓的 "跟踪依赖性"。

你可能已经意识到了,"跟踪依赖" 这几个字和Vue框架在试图访问插槽时产生的错误中提到的一样。事实上,这个错误是为了告诉我们,在渲染函数之外使用slots.default()的语法,会使变量失去响应性,因此它不会 "跟踪" 任何可能影响它的变化。

拿上面的例子来说,失去依赖关系的跟踪将意味着无论 expanded 的值是多少,按钮都不会改变。

// 下面的代码只是为了说明问题
//  我们只是假设了一个具有跟踪依赖性的变量,这也是我们插槽发生的情况
const expanded = ref( false ); //Broken Tracking

console.log(buttonText)
// 输出 "Show more"

expanded.value = true;

console.log(buttonText)
// 输出: "Show more"  值没有没有改变,因为Vue无法跟踪 expanded 的变化。

在我们的代码库中,未被追踪的变量不是我们想要的东西,应该要尽量的避免它。

如何确保 Vue 插槽被跟踪依赖

接下来,我们分析下可以做些什么来确保我们的插槽有一个响应式的跟踪系统,确保不会更新失败

通过确保我们的槽调用发生在渲染函数和模板中,问题就可以解决了,正如错误信息中提到的那样。

Invoke the slot function inside the render function

我们现在要介绍两种不同的情况。第一种是在使用渲染函数时调用插槽函数,第二种是在使用vue单文件组件的<template>部分。

在渲染函数中使用插槽

当在一个有渲染函数的组件中使用插槽时,我们必须确保在渲染函数的 "return"语句中调用插槽函数,而不是在 setup 中。

// 不好
import { h } from 'vue'

export default {
  setup( props, { slots } ) {
    const defaultSlot = slots.default();
    return () => h('div', defaultSlot)
  }
}

// 好
import { h } from 'vue'

export default {
  setup( props, { slots } ) {
    return () => h('div', slots.default())
  }
}

在使用单一文件组件(SFC)时使用插槽

如果使用单文件组件并使用 <template> 块声明 html,你可能会认为不能直接访问渲染函数,但事实并非如此。

当我第一次遇到这个问题时,我花了一些时间试图了解如何在渲染函数中移动插槽函数,但在Spa 之后,我想起了 <template>标签是由编译器为我们转化成渲染函数的。

了解 <template> 块和渲染函数是等价的,对我们定义解决问题的方法有很大帮助。事实上,为了消除警告并确保在我们的组件中跟踪依赖关系,我们需要确保插槽的调用发生在HTML中(随后被框架编译成一个渲染函数)。

举个例子:

// 缺点 - 如插槽改变,它将不会改变
<template>
  <div :class="{ 'style-for-svg': isSvg }">
    <slot></slot>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup( props, { slots } ) {
    const isSvg = ref( false );

    if( slots.default()[0].type === 'svg' ) {
      isSvg.value = true;
    }

    return {
      isSvg
    }
  }
}
</script>


// 优点:插槽改变,跟着变化
<template>
  <div :class="{ 'style-for-svg': $slots.default()[0].type === 'svg' }">
    <slot></slot>
  </div>
</template>

<script>

export default {
  setup( ) {
  }
}
</script>

解决这个问题是很简单的。直接在模板中加入函数调用,就可以解决我们的问题了。不幸的是,上面的解决方案代码不够简洁。

那要怎么做呢?使用计算属性。

在调查过程中,计算属性也被编译为渲染函数的一部分,可以用来使代码更易读,并且仍然保持变量的响应式。

<template>
  <div :class="{ 'style-for-svg': isSvg }">
    <slot></slot>
  </div>
</template>

<script>
import { computed } from 'vue'

export default {
  setup( ) {

    const isSvg = computed( () => {
      return slots.default()[0].type === 'svg';
    } );

    return {
       isSvg
    }

  }
}
</script>

总结

在开发Vue组件时,需要访问插槽函数的情况并不常见,但如果你需要这样做,我希望上面的解决方案能为你节省一些时间。

链接: https://www.fly63.com/article/detial/12161

Vue 技能进阶:使用设计模式写出优雅的前端代码

本文针对 Vue 中如何控制组件子树之外的东西,探讨了四种解决方案,并展示了每种解决方案的优缺点。希望读者能从中受到启发。问你个问题,以前你可能从来没想过:有没有办法从子组件填充父组件插槽?

vue插槽slot和slot-scope

插槽我对他的理解就是父组件的东西插到子组件的<slot></slot>里面,也不知道这样理解对不对,方便自己记忆;作用域插槽我对他的理解就是数据在子组件里面,父组件可以用

一个案例搞懂 Vue.js 的作用域插槽

作用域插槽是 Vue.js 中一个很有用的特性,可以显著提高组件的通用性和可复用性。问题在于,它实在不太好理解。尝试搞清楚父子作用域之间错综复杂的关系,其痛苦程度不亚于求解一个棘手的数学方程。

Vue.js 中的无渲染行为插槽

在本文中我们讨论 Vue 中的无渲染插槽模式能够帮助解决哪些问题。在 Vue.js 2.3.0 中引入的作用域插槽显著提高了组件的可重用性。无渲染组件模式应运而生,解决了提供可重用行为和可插入表示的问题。

vue具名插槽、作用域插槽的新写法

有时让插槽内容能够访问子组件中才有的数据是很有用的。但是由于子组件的作用域在子组件,而父组件的作用域在父组件,这样一来,父组件就访问不到子组件的信息了,但是我们又不想用$emit发送事件去传递信息

深入了解Vue.js的作用域插槽

作用域槽是Vue.js的一个有用特性,它可以使组件更加通用和可重用。唯一的问题是它们很难理解!试着让你的头在父母和孩子的范围内交织,就像解决一个棘手的数学方程。

Vue 2.60 中, 插槽新增的v-slot

注意v-slot只能添加在<template>上(只有一种例外情况),这一点和已经废弃的slotattribute不同。有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的<current-user>组件:

如何通过带有Vue插槽的组件传递HTML内容

这篇文章将向您介绍如何使用Vue插槽在Vue.js中将数据从父组件传递到子组件。这篇文章适合所有阶段的开发人员(包括初学者)。

如何使用Vue中的嵌套插槽(包括作用域插槽)

最近我弄清楚了如何递归地实现嵌套插槽,包括如何使用作用域插槽来实现。起因是我想看看是否可以构建一个复制v-for指令但仅使用template组件。

在Vue中,如何从插槽中发出数据

我们知道使用作用域插槽可以将数据传递到插槽中,但是如何从插槽传回来呢?将一个方法传递到我们的插槽中,然后在插槽中调用该方法。 我信无法发出事件

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!