Vue 3.3新增了一些语法糖和宏,包括泛型组件、defineSlots、defineEmits、defineOptions
1. defineProps
1.1 原方式
parent.vue
<template>
<div>
<Child :name="['金木']"></Child>
</div>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
</script>
<style scoped></style>
child.vue(接受父组件传来的name)
非ts原方式:
由于提示unknown[],我们需引入PropType来限定数据类型,修改如下(最终的child.vue):
ts字面量方式:
1.2 Vue3.3新方式
child.vue
PS:原方式中parent.vue中name数据类型可能变化,导致child.vue的defineProps也需对应修改(麻烦)。
vue3.3使用泛型很好的解决这一问题。
<template>
{{ name }}
</template>
<script generic="T" setup lang="ts">
/* vue3.3对defineProps的改进 新增反省支持需要在script标签加上generic='T' */
defineProps<{
name: T[];
}>();
</script>
<style scoped></style>
2. defineProps
2.1 原方式
parent.vue
<template>
<Child @send="getName"></Child>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
const getName = (name: string) => {
console.log(name);
};
</script>
<style scoped></style>
child.vue(派发Emit)
非ts原方式:
<template>
<div>
<button @click="send">派发</button>
</div>
</template>
<script setup lang="ts">
/* 原方法 */
const emit = defineEmits(["send"]);
const send = () => {
emit("send", "hello");
};
</script>
<style scoped></style>
ts字面量方式:
<template>
<div>
<button @click="send">派发</button>
</div>
</template>
<script setup lang="ts">
/* ts方式 */
const emit = defineEmits<{
(event: "send", name: string): void;
}>();
const send = () => {
emit("send", "hello");
};
</script>
<style scoped></style>
2.2 Vue3.3新方式
child.vue
<template>
<div>
<button @click="send">派发</button>
</div>
</template>
<script setup lang="ts">
/* Vue3.3改进 更简洁 */
const emit = defineEmits<{
send: [name: string];
}>();
const send = () => {
emit("send", "hello");
};
</script>
<style scoped></style>
3. defineOptions
defineOptions 里面的属性和optionAPI一样(常用于定义name)
注:props和emits不能写里面,因为这两个有专门的编译宏
PS官方解释:这个宏可以用来直接在 <script setup> 中声明组件选项,而不必使用单独的<script>块
defineOptions({
name: "Child",
inheritAttrs: false,
});
4. defineModel
由于该API处于实验性特性 可能会被删除暂时不管
warnOnce(
`This project is using defineModel(), which is an experimental ` +
`feature. It may receive breaking changes or be removed in the future, so ` +
`use at your own risk.\n` +
`To stay updated, follow the RFC at https://github.com/vuejs/rfcs/discussions/503.`
)
5. 源码
5.1 defineSlots
core-main\packages\compiler-sfc\src\script\defineSlots.ts
export function processDefineSlots(
ctx: ScriptCompileContext,
node: Node,
declId?: LVal
): boolean {
// 是否调用了defineSlots
if (!isCallOf(node, DEFINE_SLOTS)) {
return false
}
// 是否重复调用了defineSlots
if (ctx.hasDefineSlotsCall) {
ctx.error(`duplicate ${DEFINE_SLOTS}() call`, node)
}
// 函数将ctx对象的hasDefineSlotsCall 属相设置为true,表示已经调用了DEFINE_SLOTS函数
ctx.hasDefineSlotsCall = true
// 然后函数检查传递给DEFINE_SLOTS 函数的参数个数是否为0,如果不是,则函数抛出错误,
// 指示 DEFINE_SLOTS 函数不接受参数。
// eg:defineSlots<{}>(判断此处不能有参数)
if (node.arguments.length > 0) {
ctx.error(`${DEFINE_SLOTS}() cannot accept arguments`, node)
}
// 接下来,如果函数接受到了一个可选的表示插槽定义的标识符的节点对象
// 则函数使用ctx.s.overwrite
// 方法将该节点对象替换为一个表示使用插槽的帮助函数的调用
if (declId) {
ctx.s.overwrite(
ctx.startOffset! + node.start!,//开始位置
ctx.startOffset! + node.end!,//结束位置
`${ctx.helper('useSlots')}()`//替换的内容 此时就拥有了类型检查
)
}
return true //继续往下执行
}
5.2 defineOptions
core-main\packages\compiler-sfc\src\script\defineOptions.ts
export function processDefineOptions(
ctx: ScriptCompileContext,
node: Node
): boolean {
// 是否调用了defineOptions
if (!isCallOf(node, DEFINE_OPTIONS)) {
return false
}
// 是否重复调用了defineOptions
if (ctx.hasDefineOptionsCall) {
ctx.error(`duplicate ${DEFINE_OPTIONS}() call`, node)
}
// defineOptions()不能接受类型参数
// 不能接受泛型字面量方式 eg:defineOptions<>
// 只能接受普通的传入参数方式 eg:defineOptions({})
if (node.typeParameters) {
ctx.error(`${DEFINE_OPTIONS}() cannot accept type arguments`, node)
}
// defineOptions()必须接受一个参数
if (!node.arguments[0]) return true
// 函数将ctx对象的hasDefineOptionsCall属相设置为true,表示已经调用了DEFINE_OPTIONS函数
ctx.hasDefineOptionsCall = true
// 函数将ctx对象的optionRuntimeDecl属性设置为传递给DEFINE_OPTIONS函数的参数
ctx.optionsRuntimeDecl = unwrapTSNode(node.arguments[0])
let propsOption = undefined
let emitsOption = undefined
let exposeOption = undefined
let slotsOption = undefined
// 遍历optionsRuntimeDecl的属性,查找props、emits、expose和slots属性
if (ctx.optionsRuntimeDecl.type === 'ObjectExpression') {
for (const prop of ctx.optionsRuntimeDecl.properties) {
if (
(prop.type === 'ObjectProperty' || prop.type === 'ObjectMethod') &&
prop.key.type === 'Identifier'
) {
if (prop.key.name === 'props') propsOption = prop
if (prop.key.name === 'emits') emitsOption = prop
if (prop.key.name === 'expose') exposeOption = prop
if (prop.key.name === 'slots') slotsOption = prop
}
}
}
// 禁止使用defineOptions()来声明props、emits、expose和slots
if (propsOption) {
ctx.error(
`${DEFINE_OPTIONS}() cannot be used to declare props. Use ${DEFINE_PROPS}() instead.`,
propsOption
)
}
if (emitsOption) {
ctx.error(
`${DEFINE_OPTIONS}() cannot be used to declare emits. Use ${DEFINE_EMITS}() instead.`,
emitsOption
)
}
if (exposeOption) {
ctx.error(
`${DEFINE_OPTIONS}() cannot be used to declare expose. Use ${DEFINE_EXPOSE}() instead.`,
exposeOption
)
}
if (slotsOption) {
ctx.error(
`${DEFINE_OPTIONS}() cannot be used to declare slots. Use ${DEFINE_SLOTS}() instead.`,
slotsOption
)
}
return true //以上没问题,就正常运行
}
参考文档
https://blog.csdn.net/qq1195566313/article/details/131618820