Vue 3 的 Composition API 中的宏

        在 Vue 3 的 Composition API 中,宏(Macros) 是一种特殊的语法糖,用于简化组件逻辑的书写,提升开发效率。以下是 Vue 3 中常用的宏及其详解:

一、核心宏列表

宏名称作用类型示例
ref创建响应式引用(基本类型 / 对象)函数宏const count = ref(0)
reactive创建响应式对象函数宏const state = reactive({ name: 'John' })
computed创建计算属性函数宏const doubleCount = computed(() => count.value * 2)
watch监听响应式数据的变化函数宏watch(count, (newVal) => console.log(newVal))
watchEffect自动响应依赖的副作用函数宏watchEffect(() => console.log(state.name))
toRef为响应式对象的属性创建引用函数宏const nameRef = toRef(state, 'name')
toRefs将响应式对象转换为多个独立的 ref函数宏const { name, age } = toRefs(state)
shallowRef创建浅层响应式引用(仅基本类型响应式)函数宏const obj = shallowRef({ id: 1 })
shallowReactive创建浅层响应式对象(仅第一层属性响应式)函数宏const state = shallowReactive({ data: {} })
readonly创建只读的响应式引用 / 对象函数宏const readOnlyState = readonly(state)
useSSRContext获取 SSR 上下文(服务端渲染专用)函数宏const ctx = useSSRContext()
withDefaults为 props/model 添加默认值(类型推导)编译时宏const props = withDefaults(defineProps<{ msg?: string }>(), { msg: '默认值' })
defineProps声明组件 props(类型安全)编译时宏const props = defineProps<{ title: string }>()
defineEmits声明组件事件(类型安全)编译时宏const emit = defineEmits<{ (event: 'change', value: string): void }>()
defineModel声明 v-model 双向绑定(类型安全)编译时宏const model = defineModel<{ value?: number }>()

二、函数宏(Runtime Macros)

1. ref
  • 作用:为基本类型(如 stringnumberboolean)或对象创建响应式引用。
  • 详解
    • 基本类型:通过 .value 访问值,修改值时触发响应式更新。
    • 对象类型:内部会自动转为 reactive,无需手动解包。
<script setup>
    const count = ref(0); // 基本类型
    const obj = ref({ key: 'value' }); // 对象类型(内部 reactive)
</script>
2. reactive
  • 作用:为对象创建深度响应式代理(非原始值类型)。
  • 详解
    • 适用于复杂对象,递归处理所有嵌套属性。
    • 不可用于基本类型,会直接返回原值。
<script setup>
const state = reactive({
  user: { name: 'Alice' },
  items: [1, 2, 3]
});
</script>

 3. computed

  • 作用:基于响应式依赖创建缓存的计算值。
  • 详解
    • getter 模式:返回计算值(推荐)。
    • setter 模式:支持双向设置。
<script setup>
const count = ref(0);
const doubleCount = computed(() => count.value * 2); // getter
const obj = computed({
  get() { return state.obj },
  set(value) { state.obj = value } // setter
});
</script>
4. watch
  • 作用:监听一个或多个响应式数据源,执行副作用。
  • 详解
    • 监听单个数据源:watch(source, callback)
    • 监听多个数据源:watch([source1, source2], callback)
    • 支持 deep 和 immediate 选项。
<script setup>
const state = reactive({ count: 0 });
watch(
  () => state.count, // 监听函数
  (newVal, oldVal) => {
    console.log(`Count changed from ${oldVal} to ${newVal}`);
  },
  { deep: true, immediate: true } // 选项
);
</script>
5. watchEffect
  • 作用:自动追踪依赖,响应式执行副作用(无需显式指定监听源)。
  • 详解
    • 首次运行时收集依赖,依赖变化时重新执行。
    • 适用于简单的副作用场景(如 DOM 更新、日志输出)。
<script setup>
const count = ref(0);
watchEffect(() => {
  console.log(`Current count: ${count.value}`); // 自动依赖 count
});
</script>
6. toRef & toRefs
  • 作用
    • toRef:为响应式对象的属性创建独立 ref,避免解构丢失响应式。
    • toRefs:将响应式对象的所有属性转换为 ref,方便解构。
<script setup>
const state = reactive({ name: 'Bob', age: 30 });

// 创建单个 ref
const nameRef = toRef(state, 'name');

// 转换为多个 refs
const { name, age } = toRefs(state); // 解构后保持响应式
</script>

三、编译时宏(Compile-Time Macros)

1. defineProps
  • 作用:在 <script setup> 中声明组件 props,支持类型推导。
  • 详解
    • 无需手动调用 defineProps 函数,直接通过 <script setup> 语法声明。
    • 类型信息由 TypeScript 接口或类型别名提供。
<script setup lang="ts">
const props = defineProps<{
  title: string;
  disabled?: boolean;
}>();
</script>
2. defineEmits
  • 作用:在 <script setup> 中声明组件触发的事件,支持类型安全。
  • 详解
    • 返回一个类型安全的 emit 函数,避免运行时错误。
<script setup lang="ts">
const emit = defineEmits<{
  (event: 'input', value: string): void;
  (event: 'focus'): void;
}>();
</script>
3. defineModel
  • 作用:简化 v-model 双向绑定的实现,自动处理 modelValue 和事件。
  • 详解
    • 支持单个或多个 v-model 绑定(如 v-model:title)。
<script setup lang="ts">
const model = defineModel<{
  value?: string; // 默认 v-model
}>();

// 多个 v-model
const nameModel = defineModel('name', { type: String });
</script>
4. withDefaults
  • 作用:为 defineProps 或 defineModel 添加默认值,自动推导类型。
  • 详解
    • 减少重复的类型声明,提升代码简洁性。
<script setup lang="ts">
const props = withDefaults(defineProps<{
  msg?: string;
  delay?: number;
}>(), {
  msg: 'Hello', // 自动推导为 string
  delay: 300     // 自动推导为 number
});
</script>

四、高级宏

1. shallowRef & shallowReactive
  • 作用
    • shallowRef:创建浅层响应式引用,仅基本类型值响应式,对象内部变化不追踪。
    • shallowReactive:创建浅层响应式对象,仅第一层属性响应式,嵌套对象不追踪。
<script setup>
const shallowObj = shallowRef({ id: 1 }); // 修改 obj.value.id 不会触发更新
const shallowState = shallowReactive({ data: { key: 'value' } }); // 修改 state.data.key 不会触发更新
</script>
2. readonly
  • 作用:创建只读的响应式对象或引用,禁止修改。
  • 详解
    • 适用于保护敏感数据或提供只读视图。
<script setup>
const state = reactive({ count: 0 });
const readOnlyState = readonly(state); // 无法修改 readOnlyState.count
</script>
3. useSSRContext
  • 作用:在服务端渲染(SSR)中获取上下文对象,用于状态恢复等。
<script setup>
const ctx = useSSRContext(); // 仅在 SSR 环境可用
</script>

五、宏与普通函数的区别

特性宏(如 ref普通函数(如自定义 useXXX
编译优化支持编译时优化(如静态分析)运行时执行,无编译优化
类型推导内置类型声明,推导更精准需手动声明类型
性能更高(减少运行时开销)依赖函数执行效率
使用场景响应式核心逻辑(必选)业务逻辑封装(可选)

六、实践注意事项

  1. 优先使用宏

    • 响应式状态管理:ref/reactive 替代 Vue.observable
    • 依赖监听:watch/watchEffect 替代生命周期钩子。
  2. 避免过度使用浅层响应式

    • 仅在明确需要性能优化时使用 shallowRef/shallowReactive
  3. 结合 TypeScript

    • 利用 defineProps/defineEmits 的类型推导,避免运行时错误。
  4. 合理拆分逻辑

    • 将复用逻辑封装为组合函数(如 useMousePosition),而非直接在组件中编写。

七、总结

        Vue 3 的 Composition API 宏体系通过语法糖和编译优化,大幅简化了响应式逻辑的书写,提升了开发效率和代码质量。熟练掌握这些宏的适用场景,能让组件开发更加灵活、高效,尤其在大型项目和 TypeScript 项目中优势显著。

<think>好的,我现在需要帮助用户解决在Vue3中使用defineEmits的问题,特别是在export default中的用法。首先,我得回忆一下Vue3的组合式API和选项式API的区别,因为defineEmits通常是在组合式API中使用的,比如在<script setup>语法糖里。但用户提到的是export default,这可能是在选项式API中使用,或者用户可能混淆了两种写法。 首先,Vue3中的defineEmits主要用于在组合式API中声明组件可以触发的事件。在选项式API中,通常是通过emits选项来声明事件,而不是使用defineEmits。因此,用户可能在选项式API中尝试使用defineEmits,这可能导致问题。需要确认用户的实际使用场景,但根据问题描述,用户可能是在尝试在export default中使用defineEmits,这可能是一个常见的误区。 接下来,我需要检查是否有办法在选项式API中使用defineEmits。根据Vue3的官方文档,defineEmits是专门为<script setup>设计的编译器,只能在setup函数或<script setup>中使用。因此,在传统的export default写法中,即选项式API中,应该使用emits选项来声明事件,并通过this.$emit来触发事件。 用户可能需要一个例子,说明在选项式API中如何声明和触发事件,以及在组合式API中使用defineEmits的正确方式。需要区分这两种情况,并给出相应的代码示例。同时,可能需要解释为什么在export default中直接使用defineEmits是不正确的,并引导用户使用正确的语法。 另外,用户提供的引用内容中提到了Vuex的store配置和一些Vue插件,这可能与问题无关,但需要确保回答不涉及这些引用内容,除非有直接关联。例如,引用[5]提到了模块化工程,这可能与组件化开发有关,但defineEmits属于组件通信的范畴,可以适当联系模块化最佳实践。 还需要考虑用户可能的错误尝试,比如在export default中直接调用defineEmits,这会导致语法错误,因为defineEmits在选项式API中不可用。需要指出这种错误并提供正确的做法。 最后,生成相关问题,帮助用户进一步理解Vue3的事件机制和组件通信方式,比如emit的验证、组合式API与选项式API的区别、父子组件通信方法等。</think>### Vue3中`defineEmits`在`export default`中的用法说明 在Vue3中,`defineEmits`是用于组合式APIComposition API)的编译器,主要用于**声明组件可触发的事件**。但需要注意以下两点: 1. **`defineEmits`仅在`<script setup>`语法中可用**,无法直接在`export default`选项式API中使用。 2. **选项式API中应使用`emits`选项**声明事件,并通过`this.$emit()`触发事件。 --- #### 场景一:组合式API(`<script setup>`语法) ```vue <script setup> // 声明组件可触发的事件(含参数验证) const emit = defineEmits({ &#39;submit&#39;: (payload) => { return payload.email && payload.password // 参数验证 } }) function handleSubmit() { emit(&#39;submit&#39;, { email: &#39;test@vue.com&#39;, password: &#39;123&#39; }) } </script> ``` --- #### 场景二:选项式API(`export default`) ```vue <script> export default { // 声明可触发的事件 emits: [&#39;submit&#39;], methods: { handleClick() { // 触发事件并传递参数 this.$emit(&#39;submit&#39;, { data: &#39;example&#39; }) } } } </script> ``` --- #### 常见错误与解决方案 | 错误示例 | 原因 | 修正方案 | |---------|------|---------| | `export default { defineEmits: [...] }` | `defineEmits`不是选项式API的配置项 | 改用`emits`选项声明事件 | | 在选项式API中直接调用`defineEmits()` | 编译器仅支持`<script setup>` | 迁移到组合式API或改用`this.$emit()` | --- ### 最佳实践建议 1. **参数验证**:在组合式API中可为事件添加验证逻辑,提升代码健壮性[^5] 2. **父子通信**:优先使用Props传递数据,通过事件实现反向通信 3. **TypeScript支持**:使用泛型定义事件参数类型: ```typescript const emit = defineEmits<{ (e: &#39;update&#39;, value: string): void }>() ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值