Vue.js 3 Feature: setup script
Setup script is a compile-time syntactic sugar for using Composition API inside SFCs. It is the recommended syntax if you are using both SFCs and Composition API. It provides a number of advantages over the normal script syntax. And it have a good support on TypeScript.
<script setup>
is a compile-time syntactic sugar for using Composition API inside SFCs. It is the recommended syntax if you are using both SFCs and Composition API. It provides a number of advantages over the normal <script>
syntax. And it have a good support on TypeScript.
Top-level bindings are exposed to template
When using <script setup>
, any top-level bindings (::variables, function declarations, and imports::) declared inside <script setup>
are directly usable in the template:
<script setup>
// imports node_modules, helper functions, and components
import { ref } from 'vue'
import { capitalize } from './helpers'
import MyComponent from './MyComponent.vue'
import * as Form from './form-components'
// variables
const msg = ref('Hello!')
// functions
const log = () => {
console.log(msg)
}
</script>
<template>
<div @click="log">{{ msg }}</div>
<Form.Input>
<Form.Label>label</Form.Label>
</Form.Input>
</template>
defineProps & defineEmits
To declare options like props
and emits
with full type inference support, we can use the defineProps
and defineEmits
APIs, which are automatically available inside <script setup>
:
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// setup code
</script>
defineProps
accepts the same value as theprops
option, whiledefineEmits
accepts the same value as theemits
option.defineProps
anddefineEmits
are compiler macros only usable inside<script setup>
. ::They do not need to be imported::, and are compiled away when<script setup>
is processed.
Props and emits can also be declared using ::pure-type(TypeScript):: syntax by passing a literal type argument to defineProps
or defineEmits
:
const props = defineProps<{
foo: string
bar?: number
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
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).
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 exampleinheritAttrs
or custom options enabled via plugins. - ::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>
Default props values when using type declaration
One drawback of the type-only defineProps
declaration is that it doesn't have a way to provide default values for the props. To resolve this problem, a withDefaults
compiler macro is also provided:
interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})
This will be compiled to equivalent runtime props default
options. In addition, the withDefaults
helper provides type checks for the default values, and ensures the returned props
type has the optional flags removed for properties that do have default values declared.