Quiero obtener las dimensiones de un componente vue.js del padre (estoy trabajando con la configuración de script experimental).
Cuando uso la referencia dentro de un componente, funciona como se esperaba. Obtengo las dimensiones:
// 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>Pero quiero obtener la dimensión dentro del componente principal. es posible?
He intentado esto:
// 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> la consola registra este mensaje de error: Uncaught (in promise) TypeError: x.value.getBoundingClientRect is not a function
En la documentación solo puedo encontrar la forma de usar referencias de template refs dentro del componente secundario
¿Este enfoque no funciona porque las referencias están "cerradas de forma predeterminada" como dice la descripción de rfcs ?
Me encontré con este problema hoy. El problema es que, al usar el patrón <script setup> , no se devuelve ninguna de las variables declaradas. Cuando obtiene una referencia al componente, es solo un objeto vacío. La forma de evitar esto es usando defineExpose en el bloque de configuración.
// Child.vue <template> <div ref="wrapper"> <!-- content ... --> </div> </template> <script setup> import { defineExpose, ref } from 'vue' const wrapper = ref(null) defineExpose({ wrapper }) </script> La forma en que configura la referencia de la plantilla en el padre está bien. El hecho de que estuviera viendo empty object { } en la consola significa que estaba funcionando.
Como ya se dijo en la otra respuesta, se puede acceder a la referencia secundaria desde el padre de esta manera: wrapper.value.wrapper.getBoundingClientRect() .
El rfc tiene una sección que habla sobre cómo/por qué funciona esto: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md#exposing-components-public-interface
También es importante tener en cuenta que, con el patrón <script setup> , su referencia en el componente principal no será una ComponentInstance. Esto significa que no puede llamar a $el como lo haría de otra manera. Solo contendrá los valores que puso en su defineExpose .
Correcto, esto es lo que debes hacer:
// 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>y
// 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> Para saber por qué, debemos consultar la documentación de Vue en ref : Vue special-attribute 'ref' .
En el enlace dinámico de (plantilla) ref , dice:
<!-- 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> Dado que la prop le permite pasar datos del padre al hijo, podemos usar la combinación de la prop y el enlace de ref dinámico para obtener los resultados deseados. Primero, pasamos la función de devolución de llamada ref dinámica al elemento secundario como getRef prop :
<Child :get-ref="(el) => { wrapper = el }" /> Luego, el hijo hace el enlace de ref dinámico en el elemento, donde asigna el objetivo el a su ref de wrapper y llama a la función getRef prop en esa función de devolución de llamada para permitir que el padre tome el el también:
<div :ref="(el) => { wrapper = el; // child registers wrapper ref getRef(el); // parent registers the wrapper ref }"> Tenga en cuenta que esto nos permite tener la ref del elemento wrapper tanto en el componente principal como en el secundario. Si desea tener acceso al elemento wrapper solo en el componente principal, puede omitir la función de devolución de llamada del elemento secundario y simplemente vincular la ref a un prop como este:
// Child component <template> <div :ref="getRef"> // content ... </div> </template> <script setup> import { defineProps } from 'vue'; const props = defineProps({ getRef: { type: Function, }, }); </script> Eso permitiría que solo el padre tenga la ref al wrapper de su plantilla.
Puede obtener acceso al elemento raíz usando el campo $el como se muestra a continuación:
<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) }) </scriptNo creo que esto esté necesariamente relacionado con la etiqueta <script setup> . Incluso en la sintaxis de script estándar, su segundo ejemplo no funcionará tal cual.
El problema es que está poniendo ref directamente en el componente secundario:
<template> <Child ref="wrapper" /> </template> y una referencia a un componente NO es lo mismo que una referencia al elemento raíz de ese componente. No tiene un método getBoundingClientRect() .
De hecho, Vue 3 ya no requiere que un componente tenga un único elemento raíz . Puede definir su componente secundario como:
<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 wrapper1 = ref(null); return { wrapper1, wrapper2 }; }, }; </script>¿Cuál debería ser la referencia en su componente principal ahora?
Registre el wrapper.value en su consola desde su componente principal. En realidad, es un objeto de todas las referencias en su componente secundario:
{ wrapper1: {...}, // the 1st HTMLDivElement wrapper2: {...} // the 2nd HTMLDivElement } Puede hacer wrapper.value.wrapper1.getBoundingClientRect() , eso funcionará bien.