从 Options API 到 Composition API
Vue 3 引入 Composition API,本质上是为了解决大型组件中“相关逻辑被迫按 data/methods/computed 分散”的问题。在 Options API 里,一个功能的代码被拆散到不同选项;组件一变大,逻辑就难以追踪。Composition API 让你把同一功能的响应式状态、计算、副作用聚拢到一起,并通过组合式函数(composables)实现跨组件复用。
Vue 3.5 进一步优化了响应式系统,内存占用更低、性能更好,useTemplateRef、useId 等新 API 让开发体验更顺滑。
响应式核心:ref 与 reactive
Vue 3 的响应式基于两个原语:ref 和 reactive。
import { ref, reactive } from "vue";
const count = ref(0); // 基本类型用 ref
console.log(count.value); // 访问需 .value
count.value++;
const state = reactive({ // 对象用 reactive
user: { name: "Tom" },
list: [],
});
state.user.name = "Jerry"; // 直接访问,无需 .value它们的区别和取舍:
reactive 只能用于对象/数组,返回 Proxy 代理,访问无须 .value,但解构会失去响应性。ref 适用于任何类型(包括基本类型),通过 .value 访问。在模板中会自动解包,无需 .value。一个实用原则:基本类型用 ref,对象也优先用 ref,这样可以保持一致的访问方式,避免 reactive 解构丢响应性的坑。当然,整块表单状态用 reactive 也完全可以。
import { ref, reactive, toRefs } from "vue";
// reactive 解构丢响应性,需 toRefs
const state = reactive({ count: 0, name: "Vue" });
const { count, name } = toRefs(state); // 现在 count/name 是 refcomputed 与 watch
computed 是派生状态,带缓存;watch 监听变化执行副作用。
import { ref, computed, watch } from "vue";
const price = ref(100);
const quantity = ref(2);
// 计算属性,自动缓存
const total = computed(() => price.value * quantity.value);
// 监听多个源
watch([price, quantity], ([newPrice, newQty]) => {
console.log(`价格或数量变化: ${newPrice} x ${newQty}`);
}, { immediate: false });
// watchEffect 自动收集依赖
watchEffect(() => {
console.log("当前总价:", total.value);
});watch 显式指定监听源,能拿到新旧值;watchEffect 自动追踪内部用到的响应式数据,立即执行一次,适合“依赖谁就监听谁”的场景。
组合式函数:逻辑复用的标准范式
组合式函数是 Composition API 的精华,命名约定以 use 开头。它替代了 Vue 2 时代的 mixin,避免了命名冲突和来源不清晰的问题。
import { ref, onMounted, onUnmounted } from "vue";
// 鼠标位置追踪
export function useMouse() {
const x = ref(0);
const y = ref(0);
function update(e) {
x.value = e.pageX;
y.value = e.pageY;
}
onMounted(() => window.addEventListener("mousemove", update));
onUnmounted(() => window.removeEventListener("mousemove", update));
return { x, y };
}在组件中使用:
<script setup>
import { useMouse } from "./composables/useMouse";
const { x, y } = useMouse();
</script>
<template>
<p>鼠标位置: {{ x }}, {{ y }}</p>
</template>组合式函数的优势:来源清晰、可类型推导、易于测试、可在多个组件复用。一个良好组织的项目,应当把可复用逻辑都抽成 composables。
<script setup> 语法糖
<script setup> 是 Vue 3 推荐的编写组件方式,编译后更高效,且天然支持顶层 await。
<script setup>
import { ref, computed } from "vue";
import { useFetch } from "./composables/useFetch";
const { data, error } = await useFetch("/api/user");
const title = ref("用户列表");
const count = computed(() => data.value?.length ?? 0);
// 定义 props 与 emits
const props = defineProps({ id: Number });
const emit = defineEmits(["refresh"]);
</script>状态管理:Pinia
Pinia 是 Vue 官方推荐的状态管理库,比 Vuex 更简洁、对 TS 更友好。它去掉了 mutation,直接在 actions 里修改状态。
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", {
state: () => ({
users: [],
loading: false,
}),
getters: {
count: (state) => state.users.length,
},
actions: {
async fetchUsers() {
this.loading = true;
try {
const res = await fetch("/api/users");
this.users = await res.json();
} finally {
this.loading = false;
}
},
},
});在组合式 API 里也可以用 setup 风格的 store,配合 storeToRefs 解构保持响应性:
import { storeToRefs } from "pinia";
import { useUserStore } from "./stores/user";
const store = useUserStore();
const { users, count } = storeToRefs(store); // 保持响应性
store.fetchUsers(); // action 可直接解构调用性能优化技巧
shallowRef / shallowReactive:只对顶层响应,避免深层数据的递归代理开销,适合大列表。v-memo:缓存子树,只有指定依赖变化才重新渲染。markRaw:标记某些对象永不响应(如第三方实例),避免无谓代理。computed:替代在模板里写复杂表达式,享受缓存红利。<template>
<!-- v-memo: 仅当 item.id 变化时才重渲染这一项 -->
<div v-for="item in list" :key="item.id" v-memo="[item.id]">
{{ item.name }}
</div>
</template>常见陷阱
toRefs。watch 里监听 reactive 对象属性:要用函数返回值 () => state.count,或直接监听 ref。reactive({ count: ref(0) }) 访问 state.count 直接是值,无需 .value。小结
Composition API 的价值在于“按功能组织代码 + 函数级复用”。掌握 ref/reactive、computed/watch 和组合式函数,你就拿到了 Vue 3 的核心生产力。配合 <script setup> 的简洁语法和 Pinia 的状态管理,无论是中小型应用还是大型后台,都能写出结构清晰、易于维护的代码。Vue 3.5 的响应式优化和新增 API,则让这套体系在性能和体验上更上一层楼。
💬 评论区 (0)
暂无评论,快来抢沙发吧!