Vue 3.3新增了一些语法糖和宏,包括泛型组件、defineSlots、defineEmits、defineOptions
defineProps
- 父子组件传参
<template><div><Child name="xiaoman"></Child></div>
</template><script lang='ts' setup>import Child from './views/child.vue'
</script>
<style></style>
子组件使用defineProps
接受值
<template><div>{{ name }}</div>
</template><script lang='ts' setup>defineProps({name: String})
</script>
- 使用TS字面量模式
<template><div>{{ name }}</div>
</template><script lang='ts' setup>defineProps<{name:string}>()
</script>
Vue3.3
新增 defineProps 可以接受泛型
<Child :name="['xiaoman']"></Child>//-------------子组件-----------------<template><div>{{ name }}</div>
</template><script generic="T" lang='ts' setup>defineProps<{name:T[]}>()
</script>
defineEmits
- 父组件
<template><div><Child @send="getName"></Child></div>
</template><script lang='ts' setup>import Child from './views/child.vue'const getName = (name: string) => {console.log(name)}
</script>
<style></style>
子组件常规方式派发Emit
<template><div><button @click="send">派发事件</button></div>
</template><script lang='ts' setup>
const emit = defineEmits(['send'])
const send = () => {// 通过派发事件,将数据传递给父组件emit('send', '我是子组件的数据')
}
</script>
子组件TS字面量模式派发
<template><div><button @click="send">派发事件</button></div>
</template><script lang='ts' setup>
const emit = defineEmits<{(event: 'send', name: string): void
}>()
const send = () => {// 通过派发事件,将数据传递给父组件emit('send', '我是子组件的数据')
}
</script>
Vue3.3
新写法更简短
<template><div><button @click="send">派发事件</button></div>
</template><script lang='ts' setup>
const emit = defineEmits<{'send':[name:string]
}>()
const send = () => {// 通过派发事件,将数据传递给父组件emit('send', '我是子组件的数据')
}
</script>
defineExpose
没变化
defineExpose({name:"张三"
})
defineSlots
- 父组件
<template><div><Child :data="list"><template #default="{item}"><div>{{ item.name }}</div></template></Child></div>
</template>
<script lang='ts' setup>
import Child from './views/child.vue'
const list = [{name: "张三"},{name: "李四"},{name: "王五"}
]
</script>
<style></style>
子组件 defineSlots只做声明不做实现 同时约束slot类型
<template><div><ul><li v-for="(item,index) in data"><slot :index="index" :item="item"></slot></li></ul></div>
</template><script generic="T" lang='ts' setup>
defineProps<{data: T[]
}>()
defineSlots<{default(props:{item:T,index:number}):void
}>()
</script>
defineOptions
- 主要是用来定义 Options API 的选项
常用的就是定义name 在seutp 语法糖模式发现name不好定义了需要在开启一个script自定义name现在有了defineOptions就可以随意定义name了
defineOptions({name:"Child",inheritAttrs:false,
})
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.`)
源码解析
- core\packages\compiler-sfc\src\script\defineSlots.ts
export function processDefineSlots(ctx: ScriptCompileContext,node: Node,declId?: LVal
): boolean {//是否调用了defineSlotsif (!isCallOf(node, DEFINE_SLOTS)) {return false}//是否重复调用了defineSlotsif (ctx.hasDefineSlotsCall) {ctx.error(`duplicate ${DEFINE_SLOTS}() call`, node)}//函数将 ctx 对象的 hasDefineSlotsCall 属性设置为 true,表示已经调用了 DEFINE_SLOTS 函数ctx.hasDefineSlotsCall = true//然后函数检查传递给 DEFINE_SLOTS 函数的参数个数是否为零,如果不是,则函数抛出错误,指示 DEFINE_SLOTS 函数不接受参数。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
}
- core\packages\compiler-sfc\src\script\defineOptions.ts
export function processDefineOptions(ctx: ScriptCompileContext,node: Node
): boolean {//是否调用了defineOptionsif (!isCallOf(node, DEFINE_OPTIONS)) {return false}//是否重复调用了defineOptionsif (ctx.hasDefineOptionsCall) {ctx.error(`duplicate ${DEFINE_OPTIONS}() call`, node)}//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 对象的 optionsRuntimeDecl 属性设置为传递给 DEFINE_OPTIONS 函数的参数ctx.optionsRuntimeDecl = unwrapTSNode(node.arguments[0])let propsOption = undefinedlet emitsOption = undefinedlet exposeOption = undefinedlet 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 = propif (prop.key.name === 'emits') emitsOption = propif (prop.key.name === 'expose') exposeOption = propif (prop.key.name === 'slots') slotsOption = prop}}}//禁止使用defineOptions()来声明props、emits、expose和slotsif (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
}