< Script Setup > is a new syntactic sugar introduced in VUe3 to simplify lengthy template code when using the Composition API.
< Script Setup > is a compile-time syntactic sugar for using composite apis in single-file components (SFC). It has many advantages over plain
- Less boilerplate content, cleaner code.
- Be able to use pure
Typescript
The statementprops
And throw events. - Better runtime performance (their templates are compiled into renderers of the same scope as them, without any intermediate proxies).
- Better IDE type inference performance (less work for the language server to extract types from code).
The basic grammar
To use this syntax, add the setup attribute to the
<script setup>
console.log('hello script setup')
</script>
Copy the code
The code inside is compiled into the contents of the component setup() function. This means that instead of normal
The top-level binding is exposed to the template
When using
<script setup>
/ / variable
const msg = 'Hello! '
/ / function
function log() {
console.log(msg)
}
</script>
<template>
<div @click="log">{{ msg }}</div>
</template>
Copy the code
Import imports are exposed in the same way. Means you can use the imported helper function directly in a template expression, without exposing it with the Methods option:
<script setup>
import { capitalize } from './helpers'
</script>
<template>
<div>{{ capitalize('hello') }}</div>
</template>
Copy the code
responsive
Reactive state needs to be explicitly created using reactive APIs. Like the value returned from setup(), the ref value is automatically unpacked when used in the template:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
Copy the code
Using the component
Values in the
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<MyComponent />
</template>
Copy the code
Think of MyComponent as being referenced by a variable. If you have used JSX, the mental model for using it is the same here. Its kebab-case < my-Component > can also be used in templates. However, we strongly recommend using the PascalCase format for consistency. It also helps to distinguish between native custom elements.
Dynamic components
Since components are referenced as variables and not registered as string keys, dynamic components should be bound with dynamic :is when used in
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>
<template>
<component :is="Foo" />
<component :is="someCondition ? Foo : Bar" />
</template>
Copy the code
Notice how components are used as variables in ternary expressions.
Recursive components
A single-file component can be referenced by itself by its filename. For example, a component named foobar.vue can reference itself in its template with
.
Note that this approach has lower priority than importing components. If there is a named import that conflicts with the inferred name of the component, use the import alias import:
import { FooBar as FooBarChild } from './components'
Copy the code
Namespace component
You can use dotted component tags, such as < foo.bar >, to refer to components nested in object properties. This is useful when you need to import multiple components from a single file:
<script setup>
import * as Form from './form-components'
</script>
<template>
<Form.Input>
<Form.Label>label</Form.Label>
</Form.Input>
</template>
Copy the code
Use custom instructions
Globally registered custom directives work as expected, and locally registered directives can be used directly in templates, just like the components mentioned above.
There is one limitation to note: local custom directives must be named as vNameOfDirective so that they can be used directly in the template.
<script setup>
const vMyDirective = {
beforeMount: (el) = > {
// Do something on the element}}</script>
<template>
<h1 v-my-directive>This is a Heading</h1>
</template>
Copy the code
<script setup>
// The imported directive also works and can be renamed to conform to the naming convention
import { myDirective as vMyDirective } from './MyDirective.js'
</script>
Copy the code
DefineProps and defineEmits
DefineProps and defineEmits apis must be used to declare props and emits in
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change'.'delete'])
// setup code
</script>
Copy the code
-
DefineProps and defineEmits are compiler macros that can only be used in
-
DefineProps receives the same value as the props option, and defineEmits receives the same value as the emits option.
-
DefineProps and defineEmits provide appropriate type inference when the option is passed in.
-
The options passed to defineProps and defineEmits are promoted from setup to the scope of the module. Therefore, the passed option cannot refer to local variables declared in the setup scope. Doing so will cause a compilation error. However, it can refer to imported bindings because they are also module scoped.
-
If you use Typescript, it’s ok to declare prop and emits using pure type declarations.
defineExpose
Components that use < Script Setup > are turned off by default, that is, public instances of components retrieved from the template ref or $parent chain do not expose any bindings declared in < Script Setup >.
To specify the attributes to expose in the < 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>
Copy the code
When the parent component gets an instance of the current component from the template ref, the instance will look like {a: number, b: number} (ref is automatically unpacked as in normal instances).
UseSlots and useAttrs
The use of slots and attrs in
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
Copy the code
UseSlots and useAttrs are real runtime functions that return values equivalent to setupContext.slots and setupContext.attrs, which can also be used in normal combinatorial apis.
With the ordinary<script>
Used together
- Can’t in
<script setup>
Declared options, such as inheritAttrs or custom options enabled through plug-ins. - Declare named exports.
- Run side effects or create objects that only need to be executed once.
<script></script>
<script setup>
// execute in the setup() scope (for each instance)
</script>
Copy the code
Warning:
The render function is not supported in this scenario. Use a plain
The top await
Top-level await can be used in
<script setup>
const post = await fetch(`/api/post/1`).then(r= > r.json())
</script>
Copy the code
In addition, the expression of await is automatically compiled in a format that preserves the context of the current component instance after await.
Pay attention to
Async Setup () must be combined with Suspense, which is currently an experimental feature. We plan to develop and document it in a future release – if you’re interested now, you can refer to tests to see how it works.
TypeScript only features
Props /emit declarations of type only
Both props and emits can be declared using a pure type syntax that passes literal types to defineProps and defineEmits:
const props = defineProps<{
foo: string bar? : number }>()const emit = defineEmits<{
(e: 'change'.id: number): void
(e: 'update'.value: string): void} > ()Copy the code
-
DefineProps or defineEmits can only use either a runtime declaration or a type declaration. Using both declarations will cause compilation errors.
-
When type declarations are used, static analysis automatically generates equivalent runtime declarations to eliminate the need for dual declarations and still ensure correct runtime behavior.
-
In a development environment, the compiler tries to infer the corresponding runtime validation from the type. For example, here we infer foo: string from foo: string. If the type is a reference to an imported type, the inference here would be foo: null (equal to any) because the compiler has no information about the external file.
-
In production mode, the compiler generates declarations in array format to reduce the packing volume (here the props are compiled to [‘foo’, ‘bar’]).
-
The generated code is still Typescript code with type, which is processed by other tools later in the process.
-
-
As of now, the type declaration parameter must be one of the following to ensure correct static analysis:
- Type literals
- A reference to an interface or type literal in the same file
Complex types and type imports from other files are not currently supported. In theory, it is possible to implement type imports in the future.
Default props value when using type declarations
The weakness of the type-only defineProps declaration is that it has no way to provide a default value for props. To solve this problem, the withDefaults compiler macro is provided:
interface Props { msg? : string labels? : string[] }const props = withDefaults(defineProps<Props>(), {
msg: 'hello'.labels: () = > ['one'.'two']})Copy the code
The above code is compiled to the default option for the equivalent runtime props. In addition, the withDefaults helper function provides type checking of default values and ensures that the type of the returned props removes optional flags for properties for which default values have been declared.
Limitation: No Src import
Due to differences in module execution semantics, the code in < Script Setup > depends on the context of a single-file component. Moving it to an external.js or.ts file can be confusing for developers and tools alike. Therefore,
Used with vscode plug-in
Volar is a vscode plug-in designed to enhance the vue writing experience. Use volar to get the best support for script setup syntax.
Original link: v3.cn.vuejs.org/api/sfc-scr…