\<script setup>
En esta página
- Basic Syntax#
- Top-level bindings are exposed to template#
- Reactivity#
- Using Components#
- Dynamic Components#
- Recursive Components#
- Namespaced Components#
- Using Custom Directives#
- defineProps() & defineEmits()#
- Type-only props/emit declarations#
- Reactive Props Destructure #
- Default props values when using type declaration #
- defineModel()#
- Modifiers and Transformers#
- Usage with TypeScript #
- defineExpose()#
- defineOptions()#
- defineSlots()#
- useSlots() & useAttrs()#
- Usage alongside normal <script>#
- Top-level await#
- Import Statements#
- Generics #
- Restrictions#
\
```vue [Parent.vue]
<script setup>
const myRef = ref()
</script>
<template>
<Child v-model="myRef"></Child>
</template>
```vue [Parent.vue]
<script setup>
const myRef = ref()
</script>
<template>
<Child v-model="myRef"></Child>
</template>
Also, when using withDefaults with defineProps, default values for mutable reference types (like arrays or objects) should be wrapped in functions in defineModel to avoid accidental modification and external side effects.
:::
Modifiers and Transformers#
To access modifiers used with the v-model directive, we can destructure the return value of defineModel() like this:
const [modelValue, modelModifiers] = defineModel()
// corresponds to v-model.trim
if (modelModifiers.trim) {
// ...
}
When a modifier is present, we likely need to transform the value when reading or syncing it back to the parent. We can achieve this by using the get and set transformer options:
const [modelValue, modelModifiers] = defineModel({
// get() omitted as it is not needed here
set(value) {
// if the .trim modifier is used, return trimmed value
if (modelModifiers.trim) {
return value.trim()
}
// otherwise, return the value as-is
return value
}
})
Usage with TypeScript #
Like defineProps and defineEmits, defineModel can also receive type arguments to specify the types of the model value and the modifiers:
const modelValue = defineModel<string>()
// ^? Ref<string | undefined>
// default model with options, required removes possible undefined values
const modelValue = defineModel<string>({ required: true })
// ^? Ref<string>
const [modelValue, modifiers] = defineModel<string, 'trim' | 'uppercase'>()
// ^? Record<'trim' | 'uppercase', true | undefined>
defineExpose()#
Components using <script setup> are closed by default - i.e. the public instance of the component, which is retrieved via template refs or $parent chains, will not expose any of the bindings declared inside <script setup>.
To explicitly expose properties in a <script setup> component, use the defineExpose compiler macro:
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
When a parent gets an instance of this component via template refs, the retrieved instance will be of the shape { a: number, b: number } (refs are automatically unwrapped just like on normal instances).
defineOptions()#
- Only supported in 3.3+
This macro can be used to declare component options directly inside <script setup> without having to use a separate <script> block:
<script setup>
defineOptions({
inheritAttrs: false,
customOptions: {
/* ... */
}
})
</script>
- This is a macro. The options will be hoisted to module scope and cannot access local variables in
<script setup>that are not literal constants.
defineSlots()#
- Only supported in 3.3+
This macro can be used to provide type hints to IDEs for slot name and props type checking.
defineSlots() only accepts a type parameter and no runtime arguments. The type parameter should be a type literal where the property key is the slot name, and the value type is the slot function. The first argument of the function is the props the slot expects to receive, and its type will be used for slot props in the template. The return type is currently ignored and can be any, but we may leverage it for slot content checking in the future.
It also returns the slots object, which is equivalent to the slots object exposed on the setup context or returned by useSlots().
<script setup lang="ts">
const slots = defineSlots<{
default(props: { msg: string }): any
}>()
</script>
useSlots() & useAttrs()#
Usage of slots and attrs inside <script setup> should be relatively rare, since you can access them directly as $slots and $attrs in the template. In the rare case where you do need them, use the useSlots and useAttrs helpers respectively:
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
useSlots and useAttrs are actual runtime functions that return the equivalent of setupContext.slots and setupContext.attrs. They can be used in normal composition API functions as well.
Usage alongside normal <script>#
<script setup> can be used alongside normal <script>. A normal <script> may be needed in cases where we need to:
- Declare options that cannot be expressed in
<script setup>, for exampleinheritAttrsor custom options enabled via plugins (Can be replaced bydefineOptionsin 3.3+). - Declaring named exports.
- Run side effects or create objects that should only execute once.
<script>
// normal <script>, executed in module scope (only once)
runSideEffectOnce()
// declare additional options
export default {
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// executed in setup() scope (for each instance)
</script>
Support for combining <script setup> and <script> in the same component is limited to the scenarios described above. Specifically:
- Do NOT use a separate
<script>section for options that can already be defined using<script setup>, such aspropsandemits. - Variables created inside
<script setup>are not added as properties to the component instance, making them inaccessible from the Options API. Mixing APIs in this way is strongly discouraged.
If you find yourself in one of the scenarios that is not supported then you should consider switching to an explicit setup() function, instead of using <script setup>.
Top-level await#
Top-level await can be used inside <script setup>. The resulting code will be compiled as async setup():
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
In addition, the awaited expression will be automatically compiled in a format that preserves the current component instance context after the await.
:::warning Note
async setup() must be used in combination with Suspense, which is currently still an experimental feature. We plan to finalize and document it in a future release - but if you are curious now, you can refer to its tests to see how it works.
:::
Import Statements#
Import statements in vue follow ECMAScript module specification. In addition, you can use aliases defined in your build tool configuration:
<script setup>
import { ref } from 'vue'
import { componentA } from './Components'
import { componentB } from '@/Components'
import { componentC } from '~/Components'
</script>
Generics #
Generic type parameters can be declared using the generic attribute on the <script> tag:
<script setup lang="ts" generic="T">
defineProps<{
items: T[]
selected: T
}>()
</script>
The value of generic works exactly the same as the parameter list between <...> in TypeScript. For example, you can use multiple parameters, extends constraints, default types, and reference imported types:
<script
setup
lang="ts"
generic="T extends string | number, U extends Item"
>
import type { Item } from './types'
defineProps<{
id: T
list: U[]
}>()
</script>
You can use @vue-generic the directive to pass in explicit types, for when the type cannot be inferred:
<template>
<!-- @vue-generic {import('@/api').Actor} -->
<ApiSelect v-model="peopleIds" endpoint="/api/actors" id-prop="actorId" />
<!-- @vue-generic {import('@/api').Genre} -->
<ApiSelect v-model="genreIds" endpoint="/api/genres" id-prop="genreId" />
</template>
In order to use a reference to a generic component in a ref you need to use the vue-component-type-helpers library as InstanceType won't work.
<script
setup
lang="ts"
>
import componentWithoutGenerics from '../component-without-generics.vue';
import genericComponent from '../generic-component.vue';
import type { ComponentExposed } from 'vue-component-type-helpers';
// Works for a component without generics
ref<InstanceType<typeof componentWithoutGenerics>>();
ref<ComponentExposed<typeof genericComponent>>();
Restrictions#
- Due to the difference in module execution semantics, code inside
<script setup>relies on the context of an SFC. When moved into external.jsor.tsfiles, it may lead to confusion for both developers and tools. Therefore,<script setup>cannot be used with thesrcattribute. <script setup>does not support In-DOM Root Component Template.(Related Discussion)