vue与react(自定义)中计算属性对比
一、核心概念对比
特性 | Vue (Computed) | React (useMemo/useCallback) |
---|---|---|
定义方式 | 声明式(基于依赖自动缓存) | 命令式(手动声明依赖数组) |
缓存机制 | 自动缓存(依赖不变则复用) | 手动控制(依赖数组变化时重新计算) |
响应式触发 | 依赖变更自动触发 | 需严格定义依赖数组 |
语法复杂度 | 低(模板中直接使用) | 中(需配合Hooks使用) |
适用场景 | 模板渲染优化、派生状态 | 性能优化、复杂计算避免重复执行 |
二、原理剖析
1. Vue Computed 原理
实现方式:基于响应式系统的依赖追踪
// Vue 3 源码简化版
function computed(getter) {
let value;
let dirty = true; // 标记是否需要重新计算
const runner = effect(getter, {
lazy: true,
scheduler() {
dirty = true; // 依赖变化时标记为脏数据
}
});
return {
get value() {
if (dirty) {
value = runner(); // 重新计算
dirty = false;
}
return value;
}
};
}
关键点:
- 依赖变更时通过
scheduler
标记数据为dirty
- 下次访问时按需重新计算(惰性求值)
2. React useMemo 原理
实现方式:依赖数组的浅比较(Object.is)
// React 源码简化逻辑
function useMemo(factory, deps) {
const hook = getCurrentHook();
const nextDeps = deps || [];
if (hook.memoizedState) {
const [prevValue, prevDeps] = hook.memoizedState;
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevValue; // 依赖未变化时返回缓存值
}
}
const newValue = factory();
hook.memoizedState = [newValue, nextDeps];
return newValue;
}
关键点:
- 每次渲染时对比依赖数组
- 依赖变化时重新执行工厂函数
三、代码实战对比
场景1:基础计算属性
Vue 实现
<template>
<div>
FullName: {{ fullName }} <!-- 自动缓存 -->
</div>
</template>
<script>
export default {
data() {
return { firstName: '张', lastName: '三' };
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName;
}
}
};
</script>
React 实现
function UserCard({ firstName, lastName }) {
const fullName = useMemo(
() => `${firstName} ${lastName}`,
[firstName, lastName] // 需手动声明依赖
);
return <div>FullName: {fullName}</div>;
}
场景2:复杂数据过滤
Vue 实现
<template>
<ul>
<li v-for="user in activeUsers" :key="user.id">
{{ user.name }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
users: [
{ id: 1, name: 'Alice', isActive: true },
{ id: 2, name: 'Bob', isActive: false }
]
};
},
computed: {
activeUsers() {
return this.users.filter(u => u.isActive); // 自动缓存结果
}
}
};
</script>
React 实现
function UserList({ users }) {
const activeUsers = useMemo(
() => users.filter(u => u.isActive),
[users] // 注意:如果users引用不变但内容变化,需深度比较
);
return (
<ul>
{activeUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
四、复杂场景处理
场景1:依赖动态变化
Vue 的自动依赖追踪
vueCopy Code
<script>
export default {
data() {
return { a: 1, b: 2, useA: true };
},
computed: {
result() {
return this.useA ? this.a : this.b; // 自动识别依赖切换
}
}
};
</script>
React 的显式依赖管理
function Calculator({ a, b, useA }) {
const result = useMemo(
() => (useA ? a : b),
[useA, useA ? a : b] // 必须明确所有可能依赖
);
return <div>Result: {result}</div>;
}
场景2:计算属性链式调用
Vue 的链式计算
<script>
export default {
data() {
return { price: 100, taxRate: 0.1 };
},
computed: {
tax() {
return this.price * this.taxRate;
},
total() { // 可依赖其他计算属性
return this.price + this.tax;
}
}
};
</script>
React 的逐层缓存
function Invoice({ price, taxRate }) {
const tax = useMemo(() => price * taxRate, [price, taxRate]);
const total = useMemo(() => price + tax, [price, tax]); // 需手动传递依赖
return <div>Total: {total}</div>;
}
五、性能优化对比
Vue 的优势
- 自动缓存:无需手动维护依赖,适合模板中的派生数据
- 细粒度更新:依赖变更时精准触发相关组件更新
React 的优势
- 灵活控制:可结合
useCallback
缓存函数,避免子组件无效渲染 - 跨组件复用:通过自定义Hook共享计算逻辑
// React 自定义Hook复用
function useTotal(price, taxRate) {
return useMemo(() => {
const tax = price * taxRate;
return price + tax;
}, [price, taxRate]);
}
// 多个组件共享同一逻辑
function Cart() {
const total = useTotal(100, 0.1);
return <div>Total: {total}</div>;
}
六、使用场景推荐
框架 | 推荐场景 |
---|---|
Vue | 1. 模板中的复杂表达式优化 2. 需要自动追踪依赖的派生数据 3. 表单联动计算 |
React | 1. 需要手动控制缓存时机 2. 跨组件共享计算逻辑 3. 结合Context的复杂状态派生 |
- Vue Computed:适合声明式UI场景, "省心" 但灵活性较低
- React useMemo:适合精细控制, "强大" 但需手动维护依赖
- 终极选择:Vue适合快速开发,React适合大型应用状态管理