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 {
//是否调用了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 函数的参数个数是否为零,如果不是,则函数抛出错误,指示 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 {
//是否调用了defineOptions
if (!isCallOf(node, DEFINE_OPTIONS)) {
return false
}
//是否重复调用了defineOptions
if (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 = 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
}
标签:node,name,ctx,prop,Vue3.3,编译,组件,DEFINE
From: https://blog.51cto.com/u_13463935/6895152