官方网站:
<脚本设置> |维.js (vuejs.org)https://vuejs.org/api/sfc-script-setup.html<script lang="ts" setup>
<script setup>
是一种编译时语法糖,用于在单文件组件 (SFC) 中使用组合 API。如果同时使用 SFC 和组合 API,则这是建议的语法。与正常语法相比,它提供了许多优点:<script>
- 更简洁的代码,更少的样板
- 能够使用纯 TypeScript 声明 props 和发出的事件
- 更好的运行时性能(模板编译为同一作用域中的渲染函数,无需中间代理)
- 更好的 IDE 类型推断性能(语言服务器从代码中提取类型的工作量更少)
基本语法#
要选择加入语法,请将该属性添加到块中:setup
<script>
<script setup>
console.log('hello script setup')
</script>
内部代码被编译为组件函数的内容。这意味着与正常不同,普通仅在首次导入组件时执行一次,每次创建组件的实例时,内部代码都会执行。setup()
<script>
<script setup>
顶级绑定向模板公开#
使用 时,其中声明的任何顶级绑定(包括变量、函数声明和导入)都可以直接在模板中使用:<script setup>
<script setup>
<script setup>
// variable
const msg = 'Hello!'
// functions
function log() {
console.log(msg)
}
</script>
<template>
<div @click="log">{
{
msg }}</div>
</template>
进口以同样的方式公开。这意味着您可以在模板表达式中直接使用导入的帮助程序函数,而不必通过以下选项公开它:methods
<script setup>
import {
capitalize } from './helpers'
</script>
<template>
<div>{
{
capitalize('hello') }}</div>
</template>
反应#
需要使用反应性 API 显式创建反应状态。与从函数返回的值类似,在模板中引用 ref 时会自动解开包装:setup()
<script setup>
import {
ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">{
{
count }}</button>
</template>
使用组件#
作用域中的值也可以直接用作自定义组件标记名称:<script setup>
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<MyComponent />
</template>
可以将其视为变量引用。如果你使用过JSX,这里的心智模型是相似的。烤肉串大小的等效项也适用于模板 - 但是强烈建议使用PascalCase组件标签以保持一致性。它还有助于与本机自定义元素区分开来。MyComponent
<my-component>
动态组件#
由于组件被引用为变量而不是在字符串键下注册,因此在内部使用动态组件时,我们应该使用动态绑定::is
<script setup>
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>
<template>
<component :is="Foo" />
<component :is="someCondition ? Foo : Bar" />
</template>
请注意如何将组件用作三元表达式中的变量。
递归组件#
车间作业控制可以通过其文件名隐式引用自身。例如,名为 的文件可以像在其模板中一样引用自身。FooBar.vue
<FooBar/>
请注意,此组件的优先级低于导入的组件。如果命名导入与组件的推断名称冲突,则可以为导入添加别名:
import {
FooBar as FooBarChild } from './components'
命名空间组件#
您可以使用带有点的组件标记来引用嵌套在对象属性下的组件。当您从单个文件导入多个组件时,这很有用:<Foo.Bar>
<script setup>
import * as Form from './form-components'
</script>
<template>
<Form.Input>
<Form.Label>label</Form.Label>
</Form.Input>
</template>
使用自定义指令#
全局注册的自定义指令正常工作。本地自定义指令不需要显式注册到 ,但它们必须遵循命名方案:<script setup>
vNameOfDirective
<script setup>
const vMyDirective = {
beforeMount: (el) => {
// do something with the element
}
}
</script>
<template>
<h1 v-my-directive>This is a Heading</h1>
</template>
如果要从其他位置导入指令,可以对其进行重命名以适合所需的命名方案:
<script setup>
import {
myDirective as vMyDirective } from './MyDirective.js'
</script>
defineProps() & defineEmits()#
要声明具有完整类型推理支持和之类的选项,我们可以使用 和 API,它们在 以下部分中自动可用:props
emits
defineProps
defineEmits
<script setup>
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// setup code
</script>
-
defineProps
并且是编译器宏,只能在 内部使用。它们不需要导入,并且在处理时会被编译掉。defineEmits
<script setup>
<script setup>
-
defineProps
接受与选项相同的值,同时接受与选项相同的值。props
defineEmits
emits
-
defineProps
并根据传递的选项提供正确的类型推断。defineEmits
-
传递给和将从设置中吊出的选项进入模块范围。因此,这些选项不能引用在安装范围中声明的局部变量。这样做将导致编译错误。但是,它可以引用导入的绑定,因为它们也在模块范围内。
defineProps
defineEmits
如果您使用的是 TypeScript,则还可以使用纯类型注释声明 props 和 emits。
defineExpose()#
默认情况下,使用的组件是关闭的 - 即,通过模板引用或链检索的组件的公共实例不会公开其中声明的任何绑定。<script setup>
$parent
<script setup>
若要显式公开组件中的属性,请使用编译器宏:<script setup>
defineExpose
<script setup>
import {
ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
当父级通过模板 refs 获取此组件的实例时,检索到的实例将具有该形状(refs 会自动展开,就像在普通实例上一样)。{ a: number, b: number }
useSlots()
& useAttrs()
#
和 内部的使用应该相对较少,因为您可以直接在模板中访问它们。在极少数情况下,您确实需要它们,请分别使用 和 帮助程序:slots
attrs
<script setup>
$slots
$attrs
useSlots
useAttrs
<script setup>
import {
useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
useSlots
并且是实际的运行时函数,返回 等效的 和 。它们也可以用于正常的组合 API 函数。useAttrs
setupContext.slots
setupContext.attrs
与正常一起使用<script>
#
<script setup>
可与普通 一起使用。在我们需要的情况下,可能需要一个正常:<script>
<script>
- 声明不能用 表示的选项,例如或通过插件启用的自定义选项。
<script setup>
inheritAttrs
- 声明命名导出。
- 运行副作用或创建应仅执行一次的对象。
<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>
顶级await
#
顶级可以在 内部使用。生成的代码将编译为:await
<script setup>
async setup()
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
此外,等待的表达式将自动编译为的格式,该格式将保留 后面的当前组件实例上下文。await
注意
async setup()
必须与 结合使用,这目前仍然是一个实验性功能。我们计划在将来的版本中完成并记录它 - 但如果你现在很好奇,你可以参考它的测试来看看它是如何工作的。Suspense
仅键入脚本功能#
仅键入属性/发出声明#
道具和发出也可以使用纯类型语法声明,方法是将文本类型参数传递给 或 :defineProps
defineEmits
const props = defineProps<{
foo: string
bar?: number
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
-
defineProps
或者只能使用运行时声明或类型声明。同时使用两者将导致编译错误。defineEmits
-
使用类型声明时,等效的运行时声明是从静态分析中自动生成的,以消除对双重声明的需要,同时仍可确保正确的运行时行为。
-
在开发模式下,编译器将尝试从类型推断出相应的运行时验证。例如,这里是从类型推断出来的。如果类型是对导入类型的引用,则推断的结果将是(等于类型),因为编译器没有外部文件的信息。
foo: String
foo: string
foo: null
any
-
在 prod 模式下,编译器将生成数组格式声明以减小捆绑包大小(此处的 props 将编译为
['foo', 'bar']
) -
发出的代码仍然是具有有效类型的TypeScript,可以通过其他工具进一步处理。
-
-
截至目前,类型声明参数必须是以下参数之一,以确保正确的静态分析:
- 文字类型
- 对同一文件中的接口或类型文本的引用
目前不支持复杂类型和从其他文件导入类型。将来可以支持类型导入。
使用类型声明时的默认属性值#
仅类型声明的一个缺点是它没有办法为 props 提供默认值。若要解决此问题,还提供了一个编译器宏:defineProps
withDefaults
interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})
这将编译为等效的运行时属性选项。此外,该帮助程序还为默认值提供类型检查,并确保返回的类型已为声明了默认值的属性删除了可选标志。default
withDefaults
props
限制#
由于模块执行语义的差异,内部代码依赖于 SFC 的上下文。当移动到外部或文件中时,可能会导致开发人员和工具的混淆。因此,<脚本设置>
不能与属性一起使用。<script setup>
.js
.ts
src