defineExpose from component's <script setup> not working in vue 3

34.4k Views Asked by At

New to Vue in general and currently using 3.2.37, I possibly missunderstood the correct usage of composition api’s defineExpose directive as documented here: https://vuejs.org/api/sfc-script-setup.html#defineexpose

There are also some threads to be found, explaining how to expose members from inside <script setup> like Calling method on Child Component - Composition API. But somehow I cannot manage to expose the version constant ref from child component HelloWorld so that it's version can be interpolated in app component.

app.vue:

<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <main>
    <HelloWorld />
    <h1>version:{{HelloWorld.version}}</h1>
  </main>
</template>

HelloWorld.vue:

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

const version = ref('1.0.0');

defineExpose({ version });
</script>

<template>
    <div>
        <h3> You’ve successfully created a project with Vue.js and Vuetify. </h3>
    </div>
</template>

Image: version 1.0.0 not showing

3

There are 3 best solutions below

0
On

you can check this website...

Components using Options API or Composition API without are open by default.

If we try to access the public instance of such a component via template ref or $parent chains, it will not expose any of the bindings declared inside the block.

We can use the defineExpose compiler macro to explicitly expose properties:

Child.vue

<script setup>
import { ref } from 'vue'
const foo = ref('foo')
const bar = ref('bar')
defineExpose({
  foo,
  bar,
})
</script>

When a parent gets an instance of Child.vue via template ref, the retrieved instance will be of the shape { foo: string, bar: string } (refs are automatically unwrapped just like on normal instances):

Parent.vue

<template>
  <Child ref="child" />
</template>
<script setup>
import { ref } from 'vue'
const child = ref(null);

onMounted(() => {
  console.log(child.value.foo)
  console.log(child.value.bar)
})

</script>
0
On

defineExpose() exposes properties on a component's template ref, not on the component definition imported from the .vue file.

<App> could use a computed prop to access <HelloWorld>'s exposed version prop:

  1. Apply a template ref on <HelloWorld>:
<!-- App.vue -->
<HelloWorld ref="helloWorldRef" />
  1. Create a ref with the same name as the template ref:
<!-- App.vue -->
<script setup>
import { ref } from 'vue'

const helloWorldRef = ref(null)
</script>
  1. Create a computed prop that returns <HelloWorld>'s exposed version prop. Make sure to use optional chaining (or a null-check) on helloWorldRef.value, as it's initially null:
<!-- App.vue -->
<script setup>
import { computed } from 'vue'

const version = computed(() => helloWorldRef.value?.version)
</script>

<template>
  ⋮
  <h1>version:{{ version }}</h1>
</template>

demo

1
On

An object of DOM elements and component instances, registered via template refs.

You can do it this way.

<HelloWorld ref="hello" />
<h1>version:{{ $refs.hello.version }}</h1>