set: ComputedSetter<T>
}
-export const COMPUTED_SIDE_EFFECT_WARN =
- `Computed is still dirty after getter evaluation,` +
- ` likely because a computed is mutating its own dependency in its getter.` +
- ` State mutations in computed getters should be avoided. ` +
- ` Check the docs for more details: https://vuejs.org/guide/essentials/computed.html#getters-should-be-side-effect-free`
-
-export class ComputedRefImpl<T> {
- public dep?: Dep = undefined
-
- private _value!: T
- public readonly effect: ReactiveEffect<T>
-
- public readonly __v_isRef = true
- public readonly [ReactiveFlags.IS_READONLY]: boolean = false
-
- public _cacheable: boolean
+/**
+ * @internal
+ */
+export class ComputedRefImpl<T = any> implements Subscriber {
+ // A computed is a ref
+ _value: any = undefined
+ readonly dep = new Dep(this)
+ readonly __v_isRef = true;
+ readonly [ReactiveFlags.IS_READONLY]: boolean
+ // A computed is also a subscriber that tracks other deps
+ deps?: Link = undefined
+ depsTail?: Link = undefined
+ // track variaous states
+ flags = EffectFlags.DIRTY
+ // last seen global version
+ globalVersion = globalVersion - 1
+ // for backwards compat
+ effect = this
+
+ // dev only
+ onTrack?: (event: DebuggerEvent) => void
+ // dev only
+ onTrigger?: (event: DebuggerEvent) => void
+ /**
+ * Dev only
+ */
+ _warnRecursive?: boolean
+
constructor(
- private getter: ComputedGetter<T>,
- private readonly _setter: ComputedSetter<T>,
- isReadonly: boolean,
- isSSR: boolean,
+ public fn: ComputedGetter<T>,
+ private readonly setter: ComputedSetter<T> | undefined,
+ public isSSR: boolean,
) {
- this.effect = new ReactiveEffect(
- () => getter(this._value),
- () =>
- triggerRefValue(
- this,
- this.effect._dirtyLevel === DirtyLevels.MaybeDirty_ComputedSideEffect
- ? DirtyLevels.MaybeDirty_ComputedSideEffect
- : DirtyLevels.MaybeDirty,
- ),
- )
- this.effect.computed = this
- this.effect.active = this._cacheable = !isSSR
- this[ReactiveFlags.IS_READONLY] = isReadonly
+ this.__v_isReadonly = !setter
}
- get value() {
- // the computed ref may get wrapped by other proxies e.g. readonly() #3376
- const self = toRaw(this)
- if (
- (!self._cacheable || self.effect.dirty) &&
- hasChanged(self._value, (self._value = self.effect.run()!))
- ) {
- triggerRefValue(self, DirtyLevels.Dirty)
- }
- trackRefValue(self)
- if (self.effect._dirtyLevel >= DirtyLevels.MaybeDirty_ComputedSideEffect) {
- if (__DEV__ && (__TEST__ || this._warnRecursive)) {
- warn(COMPUTED_SIDE_EFFECT_WARN, `\n\ngetter: `, this.getter)
- }
- triggerRefValue(self, DirtyLevels.MaybeDirty_ComputedSideEffect)
+ notify() {
+ // avoid infinite self recursion
+ if (activeSub !== this) {
+ this.flags |= EffectFlags.DIRTY
+ this.dep.notify()
+ } else if (__DEV__) {
+ // TODO warn
}
- return self._value
- }
-
- set value(newValue: T) {
- this._setter(newValue)
}
- // #region polyfill _dirty for backward compatibility third party code for Vue <= 3.3.x
- get _dirty() {
- return this.effect.dirty
+ get value() {
+ const link = __DEV__
+ ? this.dep.track({
+ target: this,
+ type: TrackOpTypes.GET,
+ key: 'value',
+ })
+ : this.dep.track()
+ refreshComputed(this)
+ // sync version after evaluation
+ if (link) {
+ link.version = this.dep.version
+ }
+ return this._value
}
- set _dirty(v) {
- this.effect.dirty = v
+ set value(newValue) {
+ if (this.setter) {
+ this.setter(newValue)
+ } else if (__DEV__) {
+ warn('Write operation failed: computed value is readonly')
+ }
}
- // #endregion
}
/**