vue.js 模板引用拆箱逻辑

nom7f22z  于 2023-10-23  发布在  Vue.js
关注(0)|答案(2)|浏览(95)

我在创建一个元素的引用并将其存储在一个(非React性)对象中时遇到了麻烦,我这样初始化:

const myObj = {
  myRef: ref(null),
  ...
}

而在模板中我也有类似的情况:

<template>
   ...
   <Comp :ref="(el) => { myObj.myRef = el }"/>
   ...
</template>

现在,如果我尝试访问myObj.myRef(安装后),我会得到undefined.我确信这是由于我缺乏对ref和reactive如何在模板中打开的理解。有人能解释一下我在:ref="..."中实际使用的值是什么吗?

sycxhyv7

sycxhyv71#

**只有 top-level refs会在模板中自动解包。**对于其他refs(例如您在对象中的refs),您仍然需要使用.value

有人能解释一下我在:ref="..."中实际使用的值是什么吗?
这些是文档:Vue指南-模板参考:功能参考

<Comp :ref="(el) => { myObj.myRef = el }"/>

el是元素引用或组件示例:
该函数接收元素引用作为第一个参数。
在本例中,它是一个组件引用(请参阅我在本答案末尾的注解)。
myObj.myRef是一个引用,* 不是自动解包 *(因为它不是顶级的)。因此,ref * 本身 * 被覆盖,而不是它的值。如果您想使用这种方法,可以使用myObj.myRef.value = el设置ref。
现在,如果我尝试访问myObj.myRef(安装后),我会得到undefined
我不太确定这里发生了什么,尤其是因为您还没有显示这段代码。myObj.myRef应该逐字给予组件示例(当您用引用覆盖ref时)。但是,我怀疑您实际上使用的是.valuemyObj.myRef.value),它在逻辑上会产生undefined,因为组件示例默认没有value属性(如果您愿意,可以公开一个属性)。
因为你似乎只是简单地将引用绑定到ref,所以你不需要函数语法。您可以使用以下代码:

<Comp :ref="myObj.myRef"/>

请注意,我现在正在 * 绑定 * 它,而不仅仅是传递名称(:ref vs ref)。这是必需的,因为它包括属性访问,因此不仅仅是一个名称。
然而,我怀疑这是一个简化的例子,还有更多的事情要做。
现在,在评论中,你给出了一个完整的实现,并询问是否可以改进。
(As我之前猜的,比你的问题更详细:你实际上是在访问和保存组件上的一个公开的引用,而不仅仅是保存引用本身。
让我们来看看你想达到什么目的

您似乎希望访问组件内部的模板引用。

您当前的实现通过以下方式实现此目的

  • 在组件中创建模板引用,
  • 将其曝光(使用defineExpose),
  • 在父代中创建一个普通的引用,
  • function 绑定到组件的ref属性,这会将暴露的模板ref保存到另一个ref。

以下是我们目前所得到的(考虑到您在评论中链接的实现):
Comp.vue

<script setup>
import { ref } from 'vue'

const el = ref(null);

defineExpose({ inner: el });
</script>

<template>
  <p ref="el">Phrase one</p>
  <p>Phrase two</p>
</template>

App.vue

<script setup>
import { ref } from 'vue'
import Comp from './Comp.vue'

const msg = {
  ref: ref(null)
}

function logMe() {
  console.log(msg.ref.value);
}
</script>

<template>
  <button @click="logMe">Log the First one</button>
  <Comp :ref="(el) => { msg.ref.value = el ? el.inner : undefined }" />
</template>

Playground1
这个组件很好,但是通过使用计算引用而不是函数ref绑定,可以大大简化父组件。
变化:

  • 导入computed
import { ref, computed } from 'vue'
  • 为组件本身创建模板参考:
const comp = ref(null)

您可以使用任何您想要的名称,只要您在模板中使用相同的名称。

  • msgref属性从纯引用更改为计算的:
computed(() => comp.value?.inner ?? null)

这将创建一个计算的ref,其中的值将是组件的暴露ref inner(如果存在),否则为null

  • 然后我们简单地将组件的template ref绑定到组件:
<Comp ref="comp" />

这些变化产生如下结果:

<script setup>
import { ref, computed } from 'vue'
import Comp from './Comp.vue'

const comp = ref(null)

const msg = {
  ref: computed(() => comp.value?.inner ?? null)
}

function logMe() {
  console.log(msg.ref.value);
}
</script>

<template>
  <button @click="logMe">Log the First one</button>
  <Comp ref="comp" />
</template>

Playground2
虽然这实际上是两行 * 长 *(额外的ref和空白行),它是非常 * 简单 *。在ref绑定的函数处理程序中,没有改变对象,只有一个计算引用访问暴露的属性。
当然,这都是相当主观的。您可能更喜欢函数绑定,或者函数方法,但提取到一个方法。等
请注意,组件的模板引用是组件的 * 组件示例 *:
ref也可以用于子组件。在这种情况下,引用将是组件示例的引用。
我不知道你实际上想达到什么目的,但我觉得这值得一提。

rdlzhqv9

rdlzhqv92#

只有顶级引用在模板中会自动魔术般地展开。
你的ref函数将被调用,并将在挂载时完全替换你的ref。尝试访问.value很可能会导致undefined
如果你想要你描述的行为,你需要:ref="(el) => { myObj.myRef.value = el }"
您可以使用绑定语法,正如其他人建议的那样::ref="myObj.myRef"
DOM引用有点棘手,所以我推荐一种替代方法(我个人使用):

  • 为您想要引用的每个DOM元素设置一个顶级ref,这样您就可以直接使用名称,并拥有随之而来的所有优点
  • 如果您愿意,稍后将它们分组到对象中
  • 可能会在该对象周围使用reactive,以便使用更好的东西

这将给你给予:

const compRef = ref();

const obj = reactive({
  // [...]
  compRef,
  // [...]
});

<Comp ref="compRef" />

相关问题