script setup基本语法
要使用这个语法,需要将 setup
属性添加到 <script>
代码块上:
里面的代码会被编译成组件setup()
函数的内容。
这也就意味着与普通的 <script>
只在组件被首次引入的时候仅执行一次不同,<script setup>
中的代码会在每次组件实例被创建的时候执行。这一点非常的重要,也就是写在<script setup>
中的代码,例如初始化的赋值等在组件每次实例创建时都重新执行一次。
<script setup>
const a = ref(1);
console.log('hello script setup')
</script>
当使用 <script setup>
的时候,任何在<script setup>
声明的顶层的绑定 (包括声明的变量
,函数声明
,以及 import 引入的内容
) 都能在模板中直接使用,不再需要使用 return 导出。
script setup新特性
自动组件名推断
在 vue2.x options API 和使用普通的 <script>
的情况下,都可以为组件进行命名
但是在 <script setup>
下,却并没有提供直接的方式来设置的组件的名称,因此,vue 在上述情况下会依据它的文件名来自动推断组件名称。
例如:名为 Foo.vue
的文件可以在模板中用 <Foo/>
引用它自己,在 devtools 中看到的组件名称也是 Foo
。
使用 <script setup>
的情况下如何修改注册组件名呢??===>使用script
<script lang="ts">
export default { name: 'CustomComponentsName' }
</script>
<script setup lang="ts">
// code
</script>
<template>
<p>利用 script 自定义组件名称</p>
</template>
普通组件
<script setup>
中引入组件后可直接在模板使用,不再需要注册了。
<script setup lang='ts'>
import SaySomething from "./Components/SaySomething.vue";
</script>
<template>
<SaySomething />
</template>
动态组件
动态组件仍然是使用 is
,相对于 vue2.x 没有变化
<script setup lang='ts'>
import { ref } from "vue";
import Bar from "./Components/Bar.vue";
import Foo from "./Components/Foo.vue";
const condition = ref(false);
setTimeout(() => condition.value = true, 2000);
</script>
<template>
<component :is="condition ? Bar : Foo"/>
</template>
递归组件
因为自动组件名推断的缘故,一个单文件组件可以通过它的文件名被其自己所引用。例如:名为 Foo.vue 的组件可以在其模板中用 <Foo/>
引用它自己。
请注意这种方式相比于 import 导入的组件和自主注册的组件优先级更低。所有如果有命名的 import 导入和组件的推断名冲突了,可以使用 import 别名导入:
import { Foo as FooChild } from './components'
命名空间组件
可以使用带点的组件标记,例如 <Foo.Bar>
来引用嵌套在对象属性中的组件。这在需要从单个文件中导入多个组件的时候非常有用:
Components/index.ts
用于导出组件
import Foo from './Foo.vue';
import Bar from './Bar.vue';
export { Foo, Bar };
index.vue
基于命名空间使用组件
<script setup lang='ts'>
import * as Form from "../Components";
</script>
<template>
<Form.Foo />
<Form.Bar />
</template>
props的使用
接受父组件传递过来的属性
definedProps
为了在<script setup>
中 声明 props ,必须使用 defineProps
API,这是一个宏命令,不需要导入,直接可在<script setup>
使用且只能在 <script setup>
中使用,有两种方式可以使用这个宏命令类声明 props,运行时声明
和类型声明式
,不同的方式下使用这个宏命令后 props 将具备不同的类型推断。
- 使用运行时声明(runtime declaration)
defineProps 运行时声明的基本用法如下,仅支持运行时的校验
。和vue2类似
<script setup lang='ts'>
const props = defineProps({
foo: String,
bar: {
type: Number,
required: true
}
})
</script>
- 类型声明(type declaration)
defineProps
类型声明的基本用法如下,完美的支持 IDE 的类型推断和检查。推荐这种方式
<script setup lang='ts'>
const props = defineProps<{
foo?: string //?表示可选,不带?表示必传
bar: number
}>()
</script>
运行时声明和类型声明的比较
类型 | 优势 | 劣势 |
---|---|---|
运行时声明 | 不使用 ts 的情况下能够对 props 进行一定的、运行时的类型校验 | 1. 运行时校验2. 只能进行基本类型的校验3. 编码时无任何提示 |
类型声明 | 完美的支持类型的校验,包括props 的完美类型约束、父组件在传 props 时的提示以及子组件在使用 props 的提示 | 目前 ts 的接口暂时只支持写在本组件的文件内,未来应该会实现可从外部导入的,但目前可通过ts自动扫描types来解决 |
widthDefaults
defineProps
使用类型声明时的不足之处在于,它没有可以给 props 提供默认值的方式。为了解决这个问题,提供了 withDefaults
宏命令。
<script setup lang="ts">
const props = withDefaults(defineProps<{
title?: string,
list?: List.Basic[],
}>(), {
title: 'Hello withDefaults',
list: () => [{ id: 3, content: '3', isDone: false }],
});
</script>
注意:widthDefaults
是为了给 defineProps
使用类型声明时提供添加默认值的的方法,因此,需要注意这仅仅适用于 <script setup lang='ts'>
且 defineProps
使用类型声明。
自定义事件-defineEmits
在 <script setup>
中 声明 emit
,必须使用 defineEmits
API,这也是一个宏命令。同样可采用运行时声明和类型声明式两种方式,在类型声明下 emit
将具备完美的类型推断。
- 运行时声明
<script setup lang="ts">
// 这样是没有任何的类型检查的
const emit = defineEmits(['handleClick', 'handleChange']);
const handleClick = () => emit('handleClick', Date.now()+'');
const handleChange = () => emit('handleChange', Date.now());
</script>
- 类型声明
<script setup lang="ts">
interface Click {
id: string,
val: number,
}
// 完美的类型检查
// List.Basic 是基于 ts 自动扫描 types 文件夹以及 delcare namespace 自动导入的
const emit = defineEmits<{
(e: 'handleClickWithTypeDeclaration', data: Click): void,
(e: 'handleChangeWithTypeDeclaration', data: List.Basic): void,
}>();
const handleClickWithTypeDeclaration = () => emit('handleClickWithTypeDeclaration', { id: '1', val: Date.now() });
const handleChangeWithTypeDeclaration = () => emit('handleChangeWithTypeDeclaration', {
id: 1,
content: 'change',
isDone: false,
});
</script>
跟 defineProps
一样,运行时声明和类型声明式同样不可同时使用,且类型声明只能用于在 ts 环境下。
显示的暴露-defineExpose
官方文档指出默认情况下使用 <script setup>
的组件是默认关闭的,也就是说通过模板 ref
或者 $parent
链获取到的子组件的实例,并不会暴露任何在<script setup>
中声明的绑定(变量,函数)。
为了在<script setup>
组件中明确要暴露出去的属性,那么就需要使用 defineExpose
这个宏命令。
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
当父组件通过模板 ref 的方式获取到当前子组件的实例,获取到的实例会像这样 { a: number, b: number }
(ref 会和在普通实例中一样被自动解包)
useSlots 和 useAttrs
在 <script setup>
使用 slots
和 attrs
获取插槽和兜底属性,可以用 useSlots
和 useAttrs
两个函数
<script setup lang="ts">
import { useSlots, useAttrs } from "vue";
const slot = useSlots();
console.log('TestUseSlots', slot.header && slot.header()); // 获取到使用插槽的具体信息
const attrs = useAttrs();
console.log('TestUseAttrs', attrs); // 获取到使用组件时传递的 attributes
</script>
<template>
<h1> Here is slots test!!</h1>
<slot name="header"></slot>
</template>
useSlots
和 useAttrs
是真实的运行时函数,它会返回与 setupContext.slots
和 setupContext.attrs
等价的值,同样也能在普通的 composition API
中使用。
与普通的script一起使用
<script setup>
可以和普通的 <script>
一起使用。普通的 <script>
在有这些需要的情况下或许会被使用到:
- 无法在
<script setup>
声明的选项,例如 inheritAttrs 或通过插件启用的自定义的选项。 - 显示定义组件的名称。
- 运行副作用或者创建只需要执行一次的对象。
<script>
// 普通 <script>, 在模块范围下执行(只执行一次)
runSideEffectOnce()
// 声明额外的选项
export default {
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// 在 setup() 作用域中执行 (对每个实例皆如此)
</script>
注意:如果同时使用 <script setup>
和 <script>
,那么将打破 <script setup>
的默认关闭(即外部无法获取组件内部的属性和方法),此时,子组件内部的属性和方法都将在外部可获取到,如 ref.xxx
顶层await(了解)
await
的使用必须是要在async
语法糖的包裹下,否者将无法执行,为了更简化代码, <script setup>
中可以使用顶层 await
。
<script setup>
const post = await fetch(`/api/post/1`).then(r => r.json())
</script>
限制使用src 导入(了解)
SFC 的三个模块都可以通过 src
的方式进行导入,如下所示:
<template src="./template.html"></template>
<style src="./style.css"></style>
<script src="./script.js"></script>
但是在<script setup>
下强烈建议不使用 Src 导入。
由于模块执行语义的差异,<script setup>
中的代码依赖单文件组件的上下文。当将其移动到外部的 .js 或者 .ts 文件中的时候,对于开发者和工具来说都会感到混乱。因而<script setup>
不能和src attribute
一起使用。
style v-bind 新特性
style module(了解)
设计和使用上跟 Vue2.x 是一致的,因此也不多赘述。
唯一新的点是使用 <script setup>
时,可以使用 useCssModule
API 获取到 css module 对象。
<script setup lang="ts">
import { useCssModule } from "vue";
const css = useCssModule();
console.log(css); // { blue: "_blue_13cse_5", red: "_red_13cse_2"}
</script>
<style module>
.red {
color: red;
}
.blue {
color: blue;
}
</style>
动态css
单文件组件的 <style>
标签可以通过 v-bind
这一 CSS 函数将 CSS 的值关联到动态的组件状态上,有了这一特性,可以将大量的动态样式通过状态来驱动了,而不是写动态的 calss 类名或者获取 dom 来动态设置了。
<script setup lang="ts">
import { ref } from "vue";
const color = ref('red');
setTimeout(() => color.value = 'blue' , 2000);
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
color: v-bind(color);
}
</style>
volar插件
vue3必备vscode的插件,安装后有代码提示、点击css类/组件跳转、自动补全等等功能!