打造自己的组件库(一)宏函数解析

1. 初始化项目

cmd 复制代码
npm create vite

生成项目后,文件目录如下:

csharp 复制代码
├── 📁 .idea/                    # IntelliJ IDEA 配置目录
├── 📁 .vscode/                  # VS Code 配置目录
├── 📁 public/                   # 静态资源目录
│   └── vite.svg                 # Vite 默认图标
├── 📁 src/                      # 源代码目录
│   ├── 📁 assets/               # 项目资源文件
│   │   └── vue.svg              # Vue 图标
│   ├── 📁 components/           # Vue 组件目录
│   │   └── HelloWorld.vue       # Hello World 组件
│   ├── App.vue                  # 主应用组件
│   ├── main.ts                  # 应用入口文件
│   ├── style.css                # 全局样式文件
│   └── vite-env.d.ts            # Vite 环境类型声明
├── index.html                   # HTML 入口文件
├── package.json                 # 项目配置和依赖管理
├── README.md                    # 项目说明文档
├── tsconfig.json                # TypeScript 基础配置
├── tsconfig.app.json            # TypeScript 应用配置
├── tsconfig.node.json           # TypeScript Node.js 配置
├── vite.config.ts               # Vite 构建工具配置
└── .gitignore                   # Git 忽略文件配置

2. 打造自己的组件,以button为例

Button.vue

html 复制代码
<template>
  <button
      ref="_buttonRef"
      class="ms-button"
      :disabled=disabled
      :autofocus=autofocus
      :type=nativeType
      :class="{
        [`ms-button--${type}`]: type,
        [`ms-button--${size}`]: size,
        'is-plain': plain,
        'is-round': round,
        'is-circle': circle,
        'is-disabled': disabled,
      }">
    <slot></slot>
  </button>
</template>

<script setup lang="ts">
import {type ButtonProps} from "./type";
import {onMounted, ref} from "vue";

const _buttonRef = ref<HTMLButtonElement>();
onMounted(()=>{
  console.log('buttonRef === ', _buttonRef.value)
})
//宏函数
defineOptions({
  name: 'MsButton'
})
withDefaults(defineProps<ButtonProps>(),{
  nativeType: 'button'
})
defineExpose({
  buttonRef: _buttonRef
})
</script>

<style scoped>

</style>

type.ts

ts 复制代码
export type ButtonType = 'primary' | 'info' | 'success' | 'warning' | 'danger';
export type ButtonSize = 'large' | 'small';
export type NativeType = 'button' | 'submit' | 'reset'

export interface ButtonProps {
    type?: ButtonType,
    size?: ButtonSize,
    nativeType?: NativeType,
    plain?: boolean,
    round?: boolean,
    circle?: boolean,
    disabled?: boolean,
    autofocus?: boolean,
}

宏函数

在 Vue 3 中,宏函数(Macros) 是特殊的编译时函数,它们在代码被浏览器执行前就被 Vue 编译器处理,主要服务于 <script setup> 语法糖。这些函数不是普通的 JavaScript 函数,而是 Vue 编译器识别的特殊标记,用于声明组件选项或执行编译时优化。

以下是 Vue 3 的核心宏函数详解:


一、宏函数的本质特性

  1. 编译时处理
    在构建阶段被 Vue 编译器转换,不会出现在最终输出代码中
  2. 仅限顶层作用域
    必须直接在 <script setup> 的顶层使用,不可嵌套在函数内
  3. 类型安全
    完美支持 TypeScript 类型推导(如 props/emits 的类型检查)
  4. 零运行时开销
    编译后会被移除,不增加生产包体积

二、核心宏函数详解

1. defineProps - 声明组件 Props
vue 复制代码
<script setup lang="ts">
// 类型声明式(推荐)
const props = defineProps<{
  title: string
  size?: 'small' | 'medium' | 'large'
  disabled: boolean
}>()

// 运行时声明式
const props = defineProps({
  title: { type: String, required: true },
  size: { type: String, default: 'medium' },
  disabled: Boolean
})
</script>

编译后效果

👉 转换为标准的 props 组件选项

👉 生成类型安全的 Props 验证代码


2. defineEmits - 声明组件事件
vue 复制代码
<script setup lang="ts">
// 类型声明式(推荐)
const emit = defineEmits<{
  (e: 'update:title', value: string): void
  (e: 'confirm'): void
}>()

// 运行时声明式
const emit = defineEmits(['update:title', 'confirm'])
</script>

用法

js 复制代码
emit('update:title', 'New Title')  // 触发事件

3. defineExpose - 暴露组件公共属性
vue 复制代码
<script setup>
import { ref } from 'vue'

const count = ref(0)
const reset = () => { count.value = 0 }

// 暴露给父组件
defineExpose({
  count,
  reset
})
</script>

父组件调用

vue 复制代码
<template>
  <ChildComponent ref="child" />
</template>

<script setup>
const child = ref(null)
child.value.reset()  // 调用子组件方法
</script>

4. defineOptions (Vue 3.3+) - 设置组件选项
vue 复制代码
<script setup>
defineOptions({
  name: 'CustomComponent',
  inheritAttrs: false,
  // 自定义选项
  customOption: 'value' 
})
</script>

5. defineSlots (Vue 3.3+) - 类型化插槽
vue 复制代码
<script setup lang="ts">
defineSlots<{
  default: (props: { msg: string }) => any
  header?: () => VNode
}>()
</script>

三、宏函数 vs 普通函数

特性 宏函数 (如 defineProps) 普通函数 (如 ref)
执行时机 编译阶段处理 运行时执行
代码位置 必须位于 <script setup> 顶层 可出现在任意位置
类型支持 完整 TS 类型推导 需手动标注类型
输出结果 被编译器替换为选项 API 生成实际 JavaScript 代码
可组合性 不可组合使用 可组合到自定义 hook 中

四、工作原理解析

defineExpose 为例的编译过程:

flowchart LR A[源代码] -->|defineExpose| B[编译器] B --> C[AST 解析] C --> D[转换为 Options API] D --> E[输出编译后代码]

输入 (源代码):

vue 复制代码
<script setup>
defineExpose({ count: 0 })
</script>

输出 (编译后):

js 复制代码
export default {
  setup() {
    const __returned__ = { count: 0 }
    // 编译器注入的暴露逻辑
    Object.assign(__returned__, { 
      __exposed: __returned__ 
    })
    return __returned__
  },
  // 特殊标记
  __expose: true
}

五、最佳实践与注意事项

  1. 避免在逻辑块中使用

    js 复制代码
    // ❌ 错误!宏不能在函数内使用
    function init() {
      defineProps({ /*...*/ }) 
    }
  2. 组合式 API 优先

    宏函数应配合 ref/computed 等组合式 API 使用

  3. 类型声明优先于运行时声明

    使用 TypeScript 时选择类型声明方式:

    ts 复制代码
    // ✅ 推荐
    defineProps<{ title: string }>()
    
    // ⚠️ 次选
    defineProps({ title: String })
  4. 与普通函数区分使用

    vue 复制代码
    <script setup>
    // 宏函数(编译时)
    const props = defineProps(...)
    
    // 普通函数(运行时)
    const count = ref(0)
    </script>
  5. 版本兼容注意

    • defineOptions/defineSlots 需 Vue 3.3+
    • 旧项目可通过插件 unplugin-vue-define-options 兼容

总结

Vue 3 的宏函数是现代化组件开发的基石,它们:

  • 🚀 通过编译时魔法简化组件声明
  • 🛡️ 提供类型安全的 Props/Emits 声明
  • 📦 实现零运行时开销的 API 设计
  • 💡 与组合式 API 协同提升代码组织性

理解宏函数的编译时特性,能帮助开发者更高效地构建健壮且类型安全的 Vue 3 应用。


相关推荐
RadiumAg23 分钟前
记一道有趣的面试题
前端·javascript
yangzhi_emo27 分钟前
ES6笔记2
开发语言·前端·javascript
yanlele43 分钟前
我用爬虫抓取了 25 年 5 月掘金热门面试文章
前端·javascript·面试
中微子2 小时前
React状态管理最佳实践
前端
烛阴2 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
中微子2 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...2 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
初遇你时动了情2 小时前
腾讯地图 vue3 使用 封装 地图组件
javascript·vue.js·腾讯地图
华子w9089258593 小时前
基于 SpringBoot+VueJS 的农产品研究报告管理系统设计与实现
vue.js·spring boot·后端
天天扭码3 小时前
《很全面的前端面试题》——HTML篇
前端·面试·html