Vue3.3 编译宏

简介: Vue3.3 编译宏

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
}
目录
相关文章
|
3月前
|
自然语言处理 JavaScript 前端开发
Vue 3的编译器是什么
【8月更文挑战第15天】Vue 3的编译器是什么
37 0
|
3月前
|
JavaScript
Vue——使用JS文件中的函数ESLint报错未定义
Vue——使用JS文件中的函数ESLint报错未定义
41 0
|
6月前
|
JavaScript 前端开发 编译器
Vue 模板是如何编译的?
Vue 模板是如何编译的?
62 0
|
机器学习/深度学习 编译器
Vue3编译器 第一步Template转AST(上)
Vue3编译器 第一步Template转AST(上)
|
移动开发 编译器
Vue3编译器 第一步Template转AST(下)
Vue3编译器 第一步Template转AST(下)
|
JavaScript 开发者 容器
vue指令和用法
vue指令和用法
|
缓存 JSON JavaScript
关于Vue3编译器的一些优化
Vue3 编译器 本章主要介绍Vue3编译器的作用,这个编译器是如何提高性能的,静态dom与动态dom的不同处理,缓存的使用以及块的作用。 致谢Vue Mastery非常好的课程,可以转载,但请声明源链接:文章源链接justin3go.com(有些latex公式某些平台不能渲染可查看这个网站)
109 0
|
存储 JavaScript 开发者
vue2-编译之生成AST
前言 在上篇文章我们分析了编译中parse的部分代码,也就是parseHTML的实现。在parseHTML中通过逐字匹配将template进行了初步解析。现在我们继续分析在parseHTML中输出的结果是如何被parse进行使用的。以此结束完整parse流程的分析。
|
存储 缓存 JavaScript
vue2-编译入口
前言 在前几个月的时候,从0开始学习vue源码。侧重点在于vue的组件化实现,响应式原理,及渲染实现,主要分析的是vue的运行时代码逻辑。从现在开始我们将学习vue编译相关的代码,因为编译相关的代码主要是和AST的生成,转化有关,所以可能比较晦涩和考验JS操作。我们主要是了解其实现过程,知道其大概的实现逻辑和实现流程即可。 本篇文章先从入口开始,先简单分析下其入口逻辑
|
JavaScript 编译器 索引
从 vue 源码看问题 —— vue 编译器的解析(一)
从 vue 源码看问题 —— vue 编译器的解析
179 0