在 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
- 作用:为基本类型(如
string
、number
、boolean
)或对象创建响应式引用。 - 详解:
- 基本类型:通过
.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 ) |
---|---|---|
编译优化 | 支持编译时优化(如静态分析) | 运行时执行,无编译优化 |
类型推导 | 内置类型声明,推导更精准 | 需手动声明类型 |
性能 | 更高(减少运行时开销) | 依赖函数执行效率 |
使用场景 | 响应式核心逻辑(必选) | 业务逻辑封装(可选) |
六、实践注意事项
-
优先使用宏:
- 响应式状态管理:
ref
/reactive
替代Vue.observable
。 - 依赖监听:
watch
/watchEffect
替代生命周期钩子。
- 响应式状态管理:
-
避免过度使用浅层响应式:
- 仅在明确需要性能优化时使用
shallowRef
/shallowReactive
。
- 仅在明确需要性能优化时使用
-
结合 TypeScript:
- 利用
defineProps
/defineEmits
的类型推导,避免运行时错误。
- 利用
-
合理拆分逻辑:
- 将复用逻辑封装为组合函数(如
useMousePosition
),而非直接在组件中编写。
- 将复用逻辑封装为组合函数(如
七、总结
Vue 3 的 Composition API 宏体系通过语法糖和编译优化,大幅简化了响应式逻辑的书写,提升了开发效率和代码质量。熟练掌握这些宏的适用场景,能让组件开发更加灵活、高效,尤其在大型项目和 TypeScript 项目中优势显著。