1194 字
6 分钟
Vue 3 组合式 API 深度实战:从响应式原理到工程化实践

Vue 3 组合式 API 深度实战:从响应式原理到工程化实践#

🎯 前言:Vue 3 带来了组合式 API(Composition API),这是一个全新的代码组织方式,让我们能够更好地复用逻辑、组织代码。这篇文章将深入讲解 Vue 3 的核心概念和实战技巧。


一、响应式系统原理#

1.1 Proxy vs Object.defineProperty#

Vue 3 使用 Proxy 替代了 Vue 2 的 Object.defineProperty,这是一个重大的架构改进:

// Vue 2 的方式 - 无法检测新增属性
Object.defineProperty(obj, 'key', {
get() { return value },
set(newVal) { value = newVal }
});
// Vue 3 的方式 - Proxy 可以代理整个对象
const proxy = new Proxy(target, {
get(target, key) { return target[key] },
set(target, key, value) { target[key] = value; return true }
});

Proxy 的优势:

  • 可以监听动态新增的属性
  • 可以监听数组索引和长度变化
  • 可以监听 Map、Set 等集合类型
  • 性能更好,内存占用更少

1.2 ref 和 reactive#

import { ref, reactive } from 'vue';
// ref:用于基本类型
const count = ref(0);
console.log(count.value); // 访问值需要 .value
// reactive:用于对象
const state = reactive({
name: 'Vue',
version: 3
});
console.log(state.name); // 直接访问属性
// 最佳实践
const user = reactive({
profile: ref({
name: '张三',
age: 25
})
});
// 访问:user.profile.name(不需要 .value,因为 reactive 会解包)

1.3 computed 和 watch#

import { ref, computed, watch, watchEffect } from 'vue';
// computed
const firstName = ref('张');
const lastName = ref('三');
const fullName = computed(() => firstName.value + lastName.value);
// 可写的 computed
const fullNameWritable = computed({
get: () => firstName.value + lastName.value,
set: (val) => {
[firstName.value, lastName.value] = val.split(' ');
}
});
// watch - 懒执行,可以访问新旧值
watch(count, (newVal, oldVal) => {
console.log('count changed:', oldVal, '->', newVal);
});
// watchEffect - 立即执行,自动追踪依赖
watchEffect(() => {
console.log('count is:', count.value);
});

二、组合式 API 实战#

2.1 setup 函数#

<script setup>
import { ref, onMounted } from 'vue';
// 直接声明响应式数据
const count = ref(0);
const message = ref('Hello Vue 3');
// 方法
const increment = () => {
count.value++;
};
// 生命周期钩子
onMounted(() => {
console.log('Component mounted');
});
</script>
<template>
<div>
<p>{{ message }}</p>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>

2.2 逻辑复用 - Composable#

useMouse.ts
import { ref, onMounted, onUnmounted } from 'vue';
export function useMouse() {
const x = ref(0);
const y = ref(0);
function update(event: MouseEvent) {
x.value = event.pageX;
y.value = event.pageY;
}
onMounted(() => window.addEventListener('mousemove', update));
onUnmounted(() => window.removeEventListener('mousemove', update));
return { x, y };
}
// 使用
<script setup>
import { useMouse } from './useMouse';
const { x, y } = useMouse();
</script>

2.3 生命周期钩子对照#

Options APIComposition API
beforeCreate不需要,直接在 setup 中执行
created不需要,直接在 setup 中执行
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered

三、性能优化#

3.1 组件懒加载#

// 异步组件
import { defineAsyncComponent } from 'vue';
const AsyncComp = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
);
// 带加载状态的异步组件
const AsyncCompWithLoading = defineAsyncComponent({
loader: () => import('./components/HeavyComponent.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
});

3.2 v-memo 指令#

<template>
<div v-memo="[valueA, valueB]">
<!-- 只有当 valueA 或 valueB 变化时才会重新渲染 -->
<span>{{ valueA }} - {{ valueB }}</span>
</div>
</template>

3.3 虚拟列表#

<script setup>
import { ref, computed } from 'vue';
const items = ref(Array.from({ length: 10000 }, (_, i) => ({
id: i,
text: `Item ${i}`
})));
const containerHeight = 500;
const itemHeight = 50;
const scrollTop = ref(0);
const visibleItems = computed(() => {
const start = Math.floor(scrollTop.value / itemHeight);
const end = start + Math.ceil(containerHeight / itemHeight);
return items.value.slice(start, end).map((item, index) => ({
...item,
index: start + index
}));
});
</script>
<template>
<div
class="container"
:style="{ height: containerHeight + 'px', overflow: 'auto' }"
@scroll="e => scrollTop = e.target.scrollTop"
>
<div :style="{ height: items.length * itemHeight + 'px' }">
<div
v-for="item in visibleItems"
:key="item.id"
:style="{
height: itemHeight + 'px',
transform: `translateY(${item.index * itemHeight}px)`,
position: 'absolute'
}"
>
{{ item.text }}
</div>
</div>
</div>
</template>

四、TypeScript 集成#

4.1 类型声明#

// 定义 Props 类型
interface Props {
title: string;
count?: number;
items: string[];
}
// 使用 defineProps 宏
const props = withDefaults(defineProps<Props>(), {
count: 0
});
// 定义 Emits
interface Emits {
(e: 'update', value: string): void;
(e: 'delete', id: number): void;
}
const emit = defineEmits<Emits>();

4.2 泛型组件#

<script setup lang="ts" generic="T extends { id: number }">
const props = defineProps<{
items: T[];
selectedId?: number;
}>();
</script>

五、状态管理 Pinia#

stores/user.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
export const useUserStore = defineStore('user', () => {
// State
const user = ref<User | null>(null);
const isLoggedIn = ref(false);
// Getters
const fullName = computed(() => {
if (!user.value) return '';
return `${user.value.firstName} ${user.value.lastName}`;
});
// Actions
async function login(credentials: Credentials) {
const response = await api.login(credentials);
user.value = response.user;
isLoggedIn.value = true;
}
function logout() {
user.value = null;
isLoggedIn.value = false;
}
return {
user,
isLoggedIn,
fullName,
login,
logout
};
});

六、路由 Vue Router 4#

router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import { useUserStore } from '@/stores/user';
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue')
},
{
path: '/dashboard',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { requiresAuth: true },
children: [
{
path: 'profile',
component: () => import('@/views/Profile.vue')
}
]
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
// 路由守卫
router.beforeEach((to, from, next) => {
const userStore = useUserStore();
if (to.meta.requiresAuth && !userStore.isLoggedIn) {
next('/login');
} else {
next();
}
});

七、总结#

Vue 3 带来了许多激动人心的改进:

  1. 响应式系统:使用 Proxy 替代 Object.defineProperty
  2. 组合式 API:更灵活的代码组织方式
  3. TypeScript 支持:更好的类型推断和提示
  4. 性能优化:更小的包体积,更快的渲染
  5. 生态工具:Pinia、Vue Router 4 等配套工具

掌握 Vue 3 的这些特性,将帮助你构建更现代化、更高效的 Web 应用!

Vue 3 组合式 API 深度实战:从响应式原理到工程化实践
https://www.oferry.com/posts/a82/
作者
晨平安
发布于
2026-02-27
许可协议
CC BY-NC-SA 4.0
封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00