Composition API 与 Hooks 对比
Vue 3 Composition API 和 React Hooks 的异同点分析
问题
对比 Vue 3 的 Composition API 和 React Hooks,分析它们的设计理念、使用方式和差异。
解答
基本使用对比
React Hooks
import { useState, useEffect, useMemo, useCallback } from 'react';
function Counter() {
// 状态声明
const [count, setCount] = useState(0);
const [name, setName] = useState('React');
// 副作用
useEffect(() => {
document.title = `Count: ${count}`;
// 清理函数
return () => {
console.log('cleanup');
};
}, [count]); // 依赖数组
// 计算值(需要手动声明依赖)
const doubled = useMemo(() => count * 2, [count]);
// 缓存函数
const increment = useCallback(() => {
setCount(c => c + 1);
}, []);
return (
<div>
<p>{name}: {count} (doubled: {doubled})</p>
<button onClick={increment}>+1</button>
</div>
);
}
Vue Composition API
<script setup>
import { ref, reactive, computed, watch, onMounted, onUnmounted } from 'vue';
// 状态声明
const count = ref(0);
const name = ref('Vue');
// 或使用 reactive
const state = reactive({
count: 0,
name: 'Vue'
});
// 副作用
watch(count, (newVal, oldVal) => {
document.title = `Count: ${newVal}`;
});
// 生命周期
onMounted(() => {
console.log('mounted');
});
onUnmounted(() => {
console.log('cleanup');
});
// 计算值(自动追踪依赖)
const doubled = computed(() => count.value * 2);
// 普通函数,无需缓存
const increment = () => {
count.value++;
};
</script>
<template>
<div>
<p>{{ name }}: {{ count }} (doubled: {{ doubled }})</p>
<button @click="increment">+1</button>
</div>
</template>
响应式原理对比
// React: 不可变数据 + 重新执行组件函数
function ReactComponent() {
const [list, setList] = useState([1, 2, 3]);
const addItem = () => {
// 必须创建新数组
setList([...list, 4]);
// 错误:list.push(4) 不会触发更新
};
// 每次状态变化,整个函数重新执行
console.log('render');
return <div>{list.join(',')}</div>;
}
// Vue: 可变数据 + Proxy 代理
const list = ref([1, 2, 3]);
const addItem = () => {
// 直接修改即可
list.value.push(4);
};
// setup 只执行一次,通过 Proxy 追踪变化
依赖追踪对比
// React: 手动声明依赖
function ReactExample() {
const [a, setA] = useState(1);
const [b, setB] = useState(2);
// 必须手动列出依赖,遗漏会导致 bug
const sum = useMemo(() => a + b, [a, b]);
useEffect(() => {
console.log(a, b);
}, [a, b]); // 忘记加 b 会导致闭包陷阱
return <div>{sum}</div>;
}
// Vue: 自动追踪依赖
const a = ref(1);
const b = ref(2);
// 自动追踪,无需手动声明
const sum = computed(() => a.value + b.value);
watch([a, b], ([newA, newB]) => {
console.log(newA, newB);
});
// 或使用 watchEffect 自动收集
watchEffect(() => {
console.log(a.value, b.value); // 自动追踪 a 和 b
});
调用限制对比
// React Hooks 有严格的调用规则
function ReactComponent() {
// ❌ 错误:不能在条件语句中使用
if (condition) {
const [state, setState] = useState(0);
}
// ❌ 错误:不能在循环中使用
for (let i = 0; i < 3; i++) {
useEffect(() => {});
}
// ❌ 错误:不能在普通函数中使用
function helper() {
const [x, setX] = useState(0);
}
// ✅ 正确:只能在组件顶层使用
const [count, setCount] = useState(0);
}
// Vue Composition API 更灵活
const count = ref(0);
// ✅ 可以在条件语句中使用
if (condition) {
const extra = ref('extra');
}
// ✅ 可以在任何地方调用
function useCounter() {
const count = ref(0);
const increment = () => count.value++;
return { count, increment };
}
// ✅ 可以在 setup 外部定义和使用
const { count, increment } = useCounter();
逻辑复用对比
// React: 自定义 Hook
function useMousePosition() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handler = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener('mousemove', handler);
return () => window.removeEventListener('mousemove', handler);
}, []);
return position;
}
// 使用
function Component() {
const { x, y } = useMousePosition();
return <div>{x}, {y}</div>;
}
// Vue: 组合式函数
function useMousePosition() {
const x = ref(0);
const y = ref(0);
const handler = (e) => {
x.value = e.clientX;
y.value = e.clientY;
};
onMounted(() => window.addEventListener('mousemove', handler));
onUnmounted(() => window.removeEventListener('mousemove', handler));
return { x, y };
}
// 使用
const { x, y } = useMousePosition();
主要差异总结
| 特性 | React Hooks | Vue Composition API |
|---|---|---|
| 响应式 | 不可变数据,setState 触发 | Proxy 代理,自动追踪 |
| 执行时机 | 每次渲染都执行 | setup 只执行一次 |
| 依赖追踪 | 手动声明依赖数组 | 自动收集依赖 |
| 调用限制 | 只能在顶层调用 | 无位置限制 |
| 函数缓存 | 需要 useCallback | 不需要,函数引用稳定 |
| 闭包问题 | 容易产生过期闭包 | 通过 .value 访问最新值 |
关键点
- 响应式机制不同:React 基于不可变数据和重渲染,Vue 基于 Proxy 代理和依赖追踪
- 执行模式不同:React 组件函数每次渲染都执行,Vue 的 setup 只执行一次
- 依赖管理不同:React 需要手动声明依赖数组,Vue 自动追踪依赖
- 调用限制不同:React Hooks 必须在组件顶层调用,Vue 没有此限制
- 心智模型不同:React 偏函数式(纯函数 + 不可变),Vue 偏响应式(可变数据 + 自动更新)
目录