先来回忆下计算的简单使用方法
使用getter函数
computed(() => count.value + 1)
使用具有 get 和 set 函数的对象
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>探究计算属性</title>
</head>
<body>
<div id="app"></div>
<script src="../node_modules/@vue/reactivity/dist/reactivity.global.js"></script>
<script>
const { reactive, effect, computed } = VueReactivity
const state = reactive({
count: 1
})
// 当依赖值发生变化的时候会重新执行计算属性
const calCount = computed(() => state.count + 1)
console.log(calCount);
effect(() => {
app.innerHTML = calCount.value
})
setTimeout(() => {
state.count += 5
}, 1000)
</script>
</body>
</html>
控制台输出
下面是源代码的一个简版,具体代码可以看官方github源代码 (opens new window)
import { Ref } from './ref'
import { effect, ReactiveEffect, track, trigger } from './effect'
import { TriggerOpTypes, TrackOpTypes } from './operations'
import { isFunction, NOOP } from '../shared/index'
const __DEV__ = 1
export interface WritableComputedRef<T> extends Ref<T> {
readonly effect: ReactiveEffect<T>
}
export interface ComputedRef<T = any> extends WritableComputedRef<T> {
readonly value: T
}
export type ComputedGetter<T> = (ctx?: any) => T
export type ComputedSetter<T> = (v: T) => void
export interface WritableComputedOptions<T> {
get: ComputedGetter<T>
set: ComputedSetter<T>
}
class ComputedRefImpl<T> {
private _value!: T
private _dirty = true
public readonly effect: ReactiveEffect<T>
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean
) {
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
if (!this._dirty) {
this._dirty = true
trigger(this, TriggerOpTypes.SET, 'value')
}
}
})
}
get value() {
if (this._dirty) {
this._value = this.effect()
this._dirty = false
}
track(this, TrackOpTypes.GET, 'value')
return this._value
}
set value(newValue: T) {
this._setter(newValue)
}
}
export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
export function computed<T>(
options: WritableComputedOptions<T>
): WritableComputedRef<T>
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
return new ComputedRefImpl(
getter,
setter,
isFunction(getterOrOptions) || !getterOrOptions.set
) as any
}