全局编译器宏
前言 在 script-setup 模式下,新增了 4 个全局编译器宏,他们无需 import 就可以直接使用。
下边都是非常有意思的内容~~ 请接着往下看 ~~~
- // 项目根目录下的 .eslintrc.js
module.exports = {
// 原来的lint规则,补充下面的globals…
globals: {
defineProps: ‘readonly’,
defineEmits: ‘readonly’,
defineExpose: ‘readonly’,
withDefaults: ‘readonly’,
},
}
1. 变量无需进行 return
标准组件模式下,setup 里定义的变量,需要 return 后,在 template 部分才可以正确拿到:
eg:
<template>
<p>{{ msg }}</p>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
setup () {
const msg: string = 'Hello World!';
// 要给 template 用的数据需要 return 出来才可以
return {
msg
}
}
})
</script>
在 script-setup 模式下,你定义了就可以直接使用。
<template>
<p>{{ msg }}</p>
</template>
<script setup lang="ts">
const msg: string = 'Hello World!';
</script>
2. 子组件无需手动注册
子组件的挂载,在标准组件里的写法是需要 import 后再放到 components 里才能够启用:
eg:
<template>
<Child />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
// 导入子组件
import Child from '@cp/Child.vue'
export default defineComponent({
// 需要启用子组件作为模板
components: {
Child
},
// 组件里的业务代码
setup () {
// ...
}
})
</script>
script-setup 模式下,只需要导入组件即可,编译器会自动识别并启用。
<!-- 使用 script-setup 格式 -->
<template>
<Child />
</template>
<script setup lang="ts">
import Child from '@cp/Child.vue'
</script>
3. props 的接收方式变化
- 由于整个 script 都变成了一个大的 setup function ,没有了组件选项,也没有了 setup 入参,所以没办法和标准写法一样去接收 props 了。这里需要使用一个全新的 API :
defineProps
。defineProps 是一个方法,内部返回一个对象,也就是挂载到这个组件上的所有 props ,它和普通的 props 用法一样,如果不指定为 prop, 则传下来的属性会被放到attrs
那边去。
defineProps 的基础用法:
const props = defineProps([
'name',
'userInfo',
'tags'
])
console.log(props.name);
有两种方式来处理类型定义props:
1.通过构造函数检查 prop
defineProps({
name: String,
userInfo: Object,
tags: Array
});
2.使用类型注解检查 prop (使用 TypeScript 的类型注解)
defineProps<{ name: string }>();
比如字符串是 string
,而不是 String
。
如果有多个 prop ,就跟写 interface 一样:
defineProps<{
name: string;
phoneNumber: number;
userInfo: object;
tags: string[];
}>();
如果你想对某个数据设置为可选,也是遵循 TS 规范,通过英文问号 ? 来允许可选:
// name 是可选
defineProps<{
name?: string;
tags: string[];
}>();
如果你想设置可选参数的默认值,需要借助 withDefaults(props, defaultValues)
API。
eg:
withDefaults(defineProps<{
size?: number
labels?: string[]
}>(), {
size: 3,
labels: () => ['default label']
})
4. emits 的接收方式变化
和 props 一样,emits 的接收也是需要使用一个全新的 API 来操作,这个 API 就是 defineEmits
。
// 获取 emit
const emit = defineEmits(['chang-name']);
// 调用 emit
emit('chang-name', 'Tom');
5 attrs 的接收方式变化
attrs
和 props
很相似,也是基于父子通信的数据,如果父组件绑定下来的数据没有被指定为 props
,那么就会被挂到 attrs
这边来。
在标准组件里, attrs
的数据是通过 setup 的第二个入参 context
里的 attrs
API 获取的
eg:
// 标准组件的写法
export default defineComponent({
setup (props, { attrs }) {
// attrs 是个对象,每个 Attribute 都是它的 key
console.log(attrs.class);
// 如果传下来的 Attribute 带有短横线,需要通过这种方式获取
console.log(attrs['data-hash']);
}
})
但和 props
一样,由于没有了 context
参数,需要使用一个新的 API 来拿到 attrs 数据。这个 API 就是 useAttrs
。
6. useAttrs 的基础用法
顾名思义, useAttrs 可以是用来获取 attrs 数据的,它的用法非常简单:
eg:
// 导入 useAttrs 组件
import { useAttrs } from 'vue'
// 获取 attrs
const attrs = useAttrs()
// attrs是个对象,和 props 一样,需要通过 key 来得到对应的单个 attr
console.log(attrs.msg);
7. slots 的接收方式变化
slots
是 Vue 组件的插槽数据,也是在父子通信里的一个重要成员。
<template>
<div>
<!-- 插槽数据 -->
<slot />
<!-- 插槽数据 -->
</div>
</template>
新版本的 Vue 也提供了一个全新的 useSlots API
来帮助 script-setup 用户获取插槽
eg:
先来看看父组件,父组件先为子组件传入插槽数据,支持 “默认插槽” 和 “命名插槽” :
<template>
<!-- 子组件 -->
<ChildTSX>
<!-- 默认插槽 -->
<p>I am a default slot from TSX.</p>
<!-- 默认插槽 -->
<!-- 命名插槽 -->
<template #msg>
<p>I am a msg slot from TSX.</p>
</template>
<!-- 命名插槽 -->
</ChildTSX>
<!-- 子组件 -->
</template>
<script setup lang="ts">
import ChildTSX from '@cp/context/Child.tsx'
</script>
在使用 JSX / TSX 编写的子组件里,就可以通过 useSlots
来获取父组件传进来的 slots 数据进行渲染:
// 注意:这是一个 .tsx 文件
import { defineComponent, useSlots } from 'vue'
const ChildTSX = defineComponent({
setup() {
// 获取插槽数据
const slots = useSlots()
// 渲染组件
return () => (
<div>
{/* 渲染默认插槽 */}
<p>{ slots.default ? slots.default() : '' }</p>
{/* 渲染命名插槽 */}
<p>{ slots.msg ? slots.msg() : '' }</p>
</div>
)
},
})
export default ChildTSX
8. ref 的通信方式变化
在标准组件写法里,子组件的数据都是默认隐式暴露给父组件的,也就是父组件可以通过 childComponent.value.foo 这样的方式直接操作子组件的数据(参见:DOM 元素与子组件 - 响应式 API 之 ref)。
但在 script-setup 模式下,所有数据只是默认隐式 return 给 template 使用,不会暴露到组件外,所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。
在 script-setup
模式下,如果要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,这个操作,就是由 defineExpose
来完成。
defineExpose 的基础用法:
eg:
<script setup lang="ts">
// 定义一个想提供给父组件拿到的数据
const msg: string = 'Hello World!';
// 显示暴露的数据,才可以在父组件拿到
defineExpose({
msg
});
</script>
9. 顶级 await 的支持
在 script-setup 模式下,不必再配合 async 就可以直接使用 await 了,这种情况下,组件的 setup 会自动变成 async setup 。
<script setup lang="ts">
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
它转换成标准组件的写法就是:
<script lang="ts">
import { defineComponent, withAsyncContext } from 'vue'
export default defineComponent({
async setup() {
const post = await withAsyncContext(
fetch(`/api/post/1`).then((r) => r.json())
)
return {
post
}
}
})
</script>