无法在vue3合成API中的组件上使用模板引用

cclgggtu  于 2023-08-07  发布在  Vue.js
关注(0)|答案(6)|浏览(157)

我想从父组件中获取vue.js组件的尺寸(我正在使用实验性的script setup)。
当我在组件内部使用ref时,它会按预期工作。我得到尺寸:

// Child.vue
<template>
  <div ref="wrapper">
   // content ...
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue'

const wrapper = ref(null)

onMounted(() => {
  const rect = wrapper.value.getBoundingClientRect()
  console.log(rect) // works fine!
})
</script>

字符串
但我想得到父零部件内部的尺寸。这可能吗?
我试过这个:

// Parent.vue
<template>
  <Child ref="wrapper" />
</template>
<script setup>
import Child from './Child'
import { ref, onMounted } from 'vue'

const wrapper = ref(null)

onMounted(() => {
  const rect = wrapper.value.getBoundingClientRect()
  console.log(rect) // failed!
})
</script>


控制台记录此错误消息:第一个月
在文档中,我只能找到在子组件中使用template refs的方法
这种方法是否不起作用,因为参考文件是“默认关闭”的RFCS描述说?

3bygqnnd

3bygqnnd1#

我今天遇到了这个问题。问题是,当使用<script setup>模式时,没有返回任何声明的变量。当你得到一个组件的引用时,它只是一个空对象。解决这个问题的方法是在setup块中使用defineExpose

// Child.vue

<template>
  <div ref="wrapper">
   <!-- content ... -->
  </div>
</template>

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

const wrapper = ref(null)

defineExpose({ wrapper })
</script>

字符串
您在父级中设置模板参考的方式很好。您在控制台中看到empty object { }这一事实意味着它正在工作。
就像前面提到的另一个答案一样,可以从父对象访问子对象ref,如下所示:wrapper.value.wrapper.getBoundingClientRect()的值。
RFC中有一节讨论了如何/为什么这样做:https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md#exposing-components-public-interface
同样重要的是要注意,使用<script setup>模式,父组件中的引用将不是ComponentInstance。这意味着你不能像其他方式那样在上面调用$el。它只包含您在defineExpose中输入的值。

oxosxuxt

oxosxuxt2#

我不认为这一定与<script setup>标签有关。即使在标准脚本语法中,第二个示例也不能按原样工作。
问题是您将ref直接放在Child组件上:

<template>
  <Child ref="wrapper" />
</template>

字符串
并且对组件的引用与对该组件的 root 元素的引用NOT相同。它没有getBoundingClientRect()方法。
事实上,Vue 3不再要求组件有一个根元素。您可以将子组件定义为:

<template>
  <div ref="wrapper1">// content ...</div>
  <div ref="wrapper2">// content ...</div>
</template>
<script >
import { ref } from "vue";

export default {
  name: "Child",

  setup() {
    const wrapper1 = ref(null);
    const wrapper2 = ref(null);
 
    return { wrapper1, wrapper2 };
  },
};
</script>


现在父组件中的ref应该是什么?
从父组件将wrapper.value记录到控制台。它实际上是Child组件中所有引用的对象:

{
  wrapper1: {...}, // the 1st HTMLDivElement
  wrapper2: {...}  // the 2nd HTMLDivElement
}


你可以做wrapper.value.wrapper1.getBoundingClientRect(),这会很好。

0yg35tkg

0yg35tkg3#

您可以使用$el字段访问根元素,如下所示:

<template>
  <Child ref="wrapper" />
</template>

<script setup>
import Child from './Child'
import { ref, onMounted } from 'vue'

const wrapper = ref(null)

onMounted(() => {
  const rect = wrapper.value.$el.getBoundingClientRect()
  console.log(rect) 
})
</script

字符串

uplii1fm

uplii1fm4#

好吧,你要做的是:

// Parent component
<template>
  <Child :get-ref="(el) => { wrapper = el }" />
</template>

<script setup>
import Child from './Child.vue';
import { ref, onMounted } from 'vue';

const wrapper = ref();

onMounted(() => {
  const rect = wrapper.value.getBoundingClientRect()
  console.log(rect) // works fine!
});
</script>

字符串
和/或

// Child component
<template>
  <div :ref="(el) => { wrapper = el; getRef(el)}">
   // content ...
  </div>
</template>

<script setup>
import { defineProps, ref, onMounted } from 'vue';
  
const props = defineProps({
  getRef: {
    type: Function,
  },
});

const wrapper = ref();

onMounted(() => {
  const rect = wrapper.value.getBoundingClientRect()
  console.log(rect) // works fine!
});
</script>


要了解原因,我们需要查看ref上的Vue文档:Vue特殊属性“ref”。
关于(模板)ref的动态绑定,它说:

<!-- When bound dynamically, we can define ref as a callback function,
passing the element or component instance explicitly -->
<child-component :ref="(el) => child = el"></child-component>


由于prop允许您将数据从父节点传递给子节点,因此我们可以使用prop和动态ref绑定的组合来获得所需的结果。首先,我们将动态ref回调函数作为getRefprop传递给子对象

<Child :get-ref="(el) => { wrapper = el }" />


然后,子元素在元素上执行动态ref绑定,其中它将目标el分配给它的wrapperref,并在该回调函数中调用getRefprop函数,让父元素也获取el

<div :ref="(el) => {
            wrapper = el; // child registers wrapper ref
            getRef(el); // parent registers the wrapper ref
            }">


请注意,这允许我们在父组件和子组件中都拥有wrapper元素的ref。如果您希望在父组件中only访问wrapper元素,您可以跳过子组件的回调函数,只需像这样将ref绑定到prop

// Child component
<template>
  <div :ref="getRef">
   // content ...
  </div>
</template>

<script setup>
import { defineProps } from 'vue';

const props = defineProps({
  getRef: {
    type: Function,
  },
});
</script>


这将使只有父级具有模板wrapperref

vmpqdwk3

vmpqdwk35#

如果你看到的wrapper.valuenull,那么请确保你试图让ref的元素没有隐藏在一个假的v-if下。Vue不会示例化ref,直到实际需要该元素。
我意识到这个答案不适用于当前的问题,但它是“template ref null vue 3 composition API”的顶级结果,所以我怀疑更多像我一样的人会来到这里,并会欣赏这个诊断。

3duebb1j

3duebb1j6#

稍微紧凑一点:

<script setup>
const elementRef = ref(null)

const getElementRect = () => {
  return elementRef.value.getBoundingClientRect()
}

defineExpose({
  getElementRect
})

</script>
<template>
    <div ref="elementRef">
        <!-- content ... -->
    </div>
</template>

字符串

相关问题