Vue3.3新特性对比

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原方式:

image.png

由于提示unknown[],我们需引入PropType来限定数据类型,修改如下(最终的child.vue):

image.png

ts字面量方式:

image.png

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值