计算属性computed的实现
计算属性的核心概念
Vue3的computed
是响应式系统的关键部分,它基于依赖的响应式数据自动计算并缓存结果。当依赖项变化时,计算属性会重新计算,否则直接返回缓存值。这种机制避免了不必要的重复计算,提升了性能。
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
计算属性的实现原理
计算属性的实现主要依赖ComputedRefImpl
类,它继承自Ref
,具有响应式特性。核心逻辑围绕getter
函数、依赖收集和缓存机制展开。
class ComputedRefImpl<T> {
private _value!: T
private _dirty = true
private _dep?: Dep
public readonly effect: ReactiveEffect<T>
constructor(getter: ComputedGetter<T>) {
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
})
this.effect.computed = this
}
get value() {
trackRefValue(this)
if (this._dirty) {
this._dirty = false
this._value = this.effect.run()!
}
return this._value
}
}
依赖收集与触发更新
计算属性通过trackRefValue
和triggerRefValue
实现依赖管理。当访问计算属性时,会收集当前活跃的effect作为依赖;当依赖项变化时,调度器函数会被触发。
// 简化版依赖收集
function trackRefValue(ref: ComputedRefImpl<any>) {
if (activeEffect) {
trackEffects(ref.dep || (ref.dep = createDep()))
}
}
// 简化版触发更新
function triggerRefValue(ref: ComputedRefImpl<any>) {
if (ref.dep) {
triggerEffects(ref.dep)
}
}
缓存机制与脏检查
_dirty
标志位控制缓存逻辑。初始为true
表示需要重新计算,计算后设为false
。当依赖变化时,调度器将_dirty
重置为true
,触发下次访问时的重新计算。
// 示例:缓存行为验证
const price = ref(10)
const tax = computed(() => price.value * 0.2)
console.log(tax.value) // 计算并缓存结果2.0
price.value = 20 // 标记为dirty但不立即计算
console.log(tax.value) // 重新计算得到4.0
计算属性的高级用法
计算属性支持设置setter,实现可写计算属性。内部通过customRef
或shallowRef
处理更复杂的场景。
const fullName = computed({
get() {
return `${firstName.value} ${lastName.value}`
},
set(newValue) {
[firstName.value, lastName.value] = newValue.split(' ')
}
})
与watch的协同工作
计算属性常与watch
配合使用,但两者有本质区别。计算属性是声明式衍生值,而watch
是命令式副作用。
const user = reactive({ firstName: 'John', lastName: 'Doe' })
// 计算属性
const fullName = computed(() => `${user.firstName} ${user.lastName}`)
// watch
watch(() => user.firstName, (newVal) => {
console.log(`First name changed to ${newVal}`)
})
性能优化策略
计算属性的惰性求值特性带来性能优势。Vue3通过effect
的调度器实现智能更新,避免不必要的计算。
// 调度器优化示例
const scheduler = () => {
if (!this._dirty) {
this._dirty = true
triggerRefValue(this)
}
}
this.effect = new ReactiveEffect(getter, scheduler)
与Vue2实现的对比
Vue3的计算属性实现有显著改进:
- 基于Proxy的响应式系统
- 更细粒度的依赖追踪
- 组合式API下的更好集成
- 类型支持更完善
// Vue2实现对比
Vue2: {
computed: {
double() {
return this.count * 2
}
}
}
// Vue3实现
const double = computed(() => count.value * 2)
实际应用场景分析
计算属性适用于:
- 复杂数据转换
- 过滤/排序列表
- 表单验证状态
- 条件性样式计算
// 购物车总价计算
const cart = reactive({
items: [
{ price: 10, quantity: 2 },
{ price: 15, quantity: 1 }
],
discount: 0.1
})
const total = computed(() => {
return cart.items.reduce((sum, item) =>
sum + item.price * item.quantity, 0
) * (1 - cart.discount)
})
源码结构剖析
计算属性相关源码主要分布在:
packages/reactivity/src/computed.ts
- 核心实现packages/reactivity/src/effect.ts
- 依赖管理packages/reactivity/src/reactive.ts
- 基础响应式API
关键函数调用链:
computed()
→ new ComputedRefImpl()
→ ReactiveEffect
→ track/trigger
调试技巧与实践
开发时可通过这些方式调试计算属性:
- 使用
onTrack
和onTrigger
钩子 - 在Chrome DevTools中检查
_dirty
状态 - 添加日志输出依赖变化
const debugComputed = computed(() => {
// 计算逻辑
}, {
onTrack(e) {
console.log('依赖被追踪', e)
},
onTrigger(e) {
console.log('依赖触发更新', e)
}
})
边界情况处理
计算属性需要处理特殊场景:
- 循环依赖检测
- 同步修改依赖项
- 计算过程中抛出错误
- SSR环境下的行为
// 错误处理示例
const riskyComputed = computed(() => {
try {
return someOperation()
} catch (e) {
console.error('计算失败', e)
return fallbackValue
}
})
与React的useMemo对比
虽然概念相似,但Vue的计算属性有显著差异:
- 自动依赖追踪
- 更深的集成度
- 响应式系统原生支持
- 缓存策略更智能
// React对比
function Component() {
const [count, setCount] = useState(0)
const double = useMemo(() => count * 2, [count])
// ...
}
// Vue等效
const count = ref(0)
const double = computed(() => count.value * 2)
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:数组的特殊响应处理