217 lines
5.2 KiB
JavaScript
217 lines
5.2 KiB
JavaScript
const NOOP = () => {
|
|
};
|
|
const isArray = Array.isArray;
|
|
const isFunction = (val) => typeof val === "function";
|
|
const isSymbol = (val) => typeof val === "symbol";
|
|
let activeEffectScope;
|
|
function recordEffectScope(effect, scope = activeEffectScope) {
|
|
if (scope && scope.active) {
|
|
scope.effects.push(effect);
|
|
}
|
|
}
|
|
const createDep = (effects) => {
|
|
const dep = new Set(effects);
|
|
dep.w = 0;
|
|
dep.n = 0;
|
|
return dep;
|
|
};
|
|
const wasTracked = (dep) => (dep.w & trackOpBit) > 0;
|
|
const newTracked = (dep) => (dep.n & trackOpBit) > 0;
|
|
const initDepMarkers = ({ deps }) => {
|
|
if (deps.length) {
|
|
for (let i = 0; i < deps.length; i++) {
|
|
deps[i].w |= trackOpBit;
|
|
}
|
|
}
|
|
};
|
|
const finalizeDepMarkers = (effect) => {
|
|
const { deps } = effect;
|
|
if (deps.length) {
|
|
let ptr = 0;
|
|
for (let i = 0; i < deps.length; i++) {
|
|
const dep = deps[i];
|
|
if (wasTracked(dep) && !newTracked(dep)) {
|
|
dep.delete(effect);
|
|
} else {
|
|
deps[ptr++] = dep;
|
|
}
|
|
dep.w &= ~trackOpBit;
|
|
dep.n &= ~trackOpBit;
|
|
}
|
|
deps.length = ptr;
|
|
}
|
|
};
|
|
let effectTrackDepth = 0;
|
|
let trackOpBit = 1;
|
|
const maxMarkerBits = 30;
|
|
let activeEffect;
|
|
class ReactiveEffect {
|
|
constructor(fn, scheduler = null, scope) {
|
|
this.fn = fn;
|
|
this.scheduler = scheduler;
|
|
this.active = true;
|
|
this.deps = [];
|
|
this.parent = void 0;
|
|
recordEffectScope(this, scope);
|
|
}
|
|
run() {
|
|
if (!this.active) {
|
|
return this.fn();
|
|
}
|
|
let parent = activeEffect;
|
|
let lastShouldTrack = shouldTrack;
|
|
while (parent) {
|
|
if (parent === this) {
|
|
return;
|
|
}
|
|
parent = parent.parent;
|
|
}
|
|
try {
|
|
this.parent = activeEffect;
|
|
activeEffect = this;
|
|
shouldTrack = true;
|
|
trackOpBit = 1 << ++effectTrackDepth;
|
|
if (effectTrackDepth <= maxMarkerBits) {
|
|
initDepMarkers(this);
|
|
} else {
|
|
cleanupEffect(this);
|
|
}
|
|
return this.fn();
|
|
} finally {
|
|
if (effectTrackDepth <= maxMarkerBits) {
|
|
finalizeDepMarkers(this);
|
|
}
|
|
trackOpBit = 1 << --effectTrackDepth;
|
|
activeEffect = this.parent;
|
|
shouldTrack = lastShouldTrack;
|
|
this.parent = void 0;
|
|
if (this.deferStop) {
|
|
this.stop();
|
|
}
|
|
}
|
|
}
|
|
stop() {
|
|
if (activeEffect === this) {
|
|
this.deferStop = true;
|
|
} else if (this.active) {
|
|
cleanupEffect(this);
|
|
if (this.onStop) {
|
|
this.onStop();
|
|
}
|
|
this.active = false;
|
|
}
|
|
}
|
|
}
|
|
function cleanupEffect(effect) {
|
|
const { deps } = effect;
|
|
if (deps.length) {
|
|
for (let i = 0; i < deps.length; i++) {
|
|
deps[i].delete(effect);
|
|
}
|
|
deps.length = 0;
|
|
}
|
|
}
|
|
let shouldTrack = true;
|
|
function trackEffects(dep, debuggerEventExtraInfo) {
|
|
let shouldTrack2 = false;
|
|
if (effectTrackDepth <= maxMarkerBits) {
|
|
if (!newTracked(dep)) {
|
|
dep.n |= trackOpBit;
|
|
shouldTrack2 = !wasTracked(dep);
|
|
}
|
|
} else {
|
|
shouldTrack2 = !dep.has(activeEffect);
|
|
}
|
|
if (shouldTrack2) {
|
|
dep.add(activeEffect);
|
|
activeEffect.deps.push(dep);
|
|
}
|
|
}
|
|
function triggerEffects(dep, debuggerEventExtraInfo) {
|
|
const effects = isArray(dep) ? dep : [...dep];
|
|
for (const effect of effects) {
|
|
if (effect.computed) {
|
|
triggerEffect(effect);
|
|
}
|
|
}
|
|
for (const effect of effects) {
|
|
if (!effect.computed) {
|
|
triggerEffect(effect);
|
|
}
|
|
}
|
|
}
|
|
function triggerEffect(effect, debuggerEventExtraInfo) {
|
|
if (effect !== activeEffect || effect.allowRecurse) {
|
|
if (effect.scheduler) {
|
|
effect.scheduler();
|
|
} else {
|
|
effect.run();
|
|
}
|
|
}
|
|
}
|
|
new Set(/* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== "arguments" && key !== "caller").map((key) => Symbol[key]).filter(isSymbol));
|
|
function toRaw(observed) {
|
|
const raw = observed && observed["__v_raw"];
|
|
return raw ? toRaw(raw) : observed;
|
|
}
|
|
function trackRefValue(ref) {
|
|
if (shouldTrack && activeEffect) {
|
|
ref = toRaw(ref);
|
|
{
|
|
trackEffects(ref.dep || (ref.dep = createDep()));
|
|
}
|
|
}
|
|
}
|
|
function triggerRefValue(ref, newVal) {
|
|
ref = toRaw(ref);
|
|
if (ref.dep) {
|
|
{
|
|
triggerEffects(ref.dep);
|
|
}
|
|
}
|
|
}
|
|
class ComputedRefImpl {
|
|
constructor(getter, _setter, isReadonly, isSSR) {
|
|
this._setter = _setter;
|
|
this.dep = void 0;
|
|
this.__v_isRef = true;
|
|
this._dirty = true;
|
|
this.effect = new ReactiveEffect(getter, () => {
|
|
if (!this._dirty) {
|
|
this._dirty = true;
|
|
triggerRefValue(this);
|
|
}
|
|
});
|
|
this.effect.computed = this;
|
|
this.effect.active = this._cacheable = !isSSR;
|
|
this["__v_isReadonly"] = isReadonly;
|
|
}
|
|
get value() {
|
|
const self = toRaw(this);
|
|
trackRefValue(self);
|
|
if (self._dirty || !self._cacheable) {
|
|
self._dirty = false;
|
|
self._value = self.effect.run();
|
|
}
|
|
return self._value;
|
|
}
|
|
set value(newValue) {
|
|
this._setter(newValue);
|
|
}
|
|
}
|
|
function computed(getterOrOptions, debugOptions, isSSR = false) {
|
|
let getter;
|
|
let setter;
|
|
const onlyGetter = isFunction(getterOrOptions);
|
|
if (onlyGetter) {
|
|
getter = getterOrOptions;
|
|
setter = NOOP;
|
|
} else {
|
|
getter = getterOrOptions.get;
|
|
setter = getterOrOptions.set;
|
|
}
|
|
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
|
|
return cRef;
|
|
}
|
|
export { computed as c };
|