命令式代码 强调过程 一步一步走流程
响应式代码 强调预设过程 后期触发 先设置后执行
A:执行代码 a->b->c
A2:预设代码 (a->b->c)
B:执行预设代码 (a->b->c)()
vue设计
设置通过 proxy.get->track->dep->effect
触发通过 proxy.set->trigger->dep->effect
概念
ref rective readonly
shallowRef ShallowReacitve shallowReadonly
triggerRef customRef
toRaw markRaw
effectScope getCurrentScope onScopeDispose
computed watch watchEffect watchSyncEffect watchPostEffect
onWacherCleanup

用例
js
import { reactive } from 'vue' // 或 @vue/reactivity
import { effect, stop } from '@vue/reactivity'
const state = reactive({ count: 0 })
// 创建副作用,返回 runner,可手动触发
const runner = effect(() => {
console.log('count =', state.count)
})
// 触发依赖变更 → 调用副作用
state.count++
// 手动再次执行(不改依赖也可)
runner()
// 停止副作用(清理依赖追踪)
stop(runner)
import { reactive } from 'vue'
import { effect, stop } from '@vue/reactivity'
const state = reactive({ n: 0 })
const queue: Function[] = []
let flushing = false
const flush = () => {
if (flushing) return
flushing = true
Promise.resolve().then(() => {
flushing = false
// 批处理执行
queue.splice(0).forEach(fn => fn())
})
}
const runner = effect(
() => {
console.log('n =', state.n)
},
{
// 调度:依赖变更时,不直接执行,而是把 job 放入队列
scheduler: (job) => {
queue.push(job)
flush()
},
onStop: () => console.log('stopped'),
// 调试:依赖收集与触发回调(可选)
onTrack: (e) => console.debug('track', e),
onTrigger: (e) => console.debug('trigger', e),
}
)
state.n++
state.n++
stop(runner)
简化流程代码
rust
scope->reactive->watch->track get->trigger set
设置副作用域 创建响应式对象 追踪 触发
js
1.scope
new EffectScope
let activeEffectScope;
class EffectScope {
constructor(detached = false) {
this.detached = detached;
/**
* @internal
*/
this._active = true;
/**
* @internal
*/
this.effects = [];
/**
* @internal
*/
this.cleanups = [];
this.parent = activeEffectScope;
if (!detached && activeEffectScope) {
this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(
this
) - 1;
}
}
2.reactive
function reactive(target) {
if (isReadonly(target)) {
return target;
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
);
}
function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {
...
const proxy = new Proxy(
target,
targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers
);
proxyMap.set(target, proxy);
return proxy;
}
class BaseReactiveHandler {
get(){
...
if (!isReadonly2) {
track(target, "get", key);
}
}
class MutableReactiveHandler extends BaseReactiveHandler {
set(target, key, value, receiver) {
...
trigger(target, "add", key, value);
}
}
4. track
function track(target, type, key) {
...
trackEffect(
activeEffect,
dep,
!!(process.env.NODE_ENV !== "production") ? {
target,
type,
key
} : void 0
);
}
function trackEffect(effect2, dep, debuggerEventExtraInfo) {
...
effect2.deps[effect2._depsLength++] = dep;
}
5. trigger
function trigger(target, type, key, newValue, oldValue, oldTarget) {
...
triggerEffects(
dep,
4,
!!(process.env.NODE_ENV !== "production") ? {
target,
type,
key,
newValue,
oldValue,
oldTarget
} : void 0
);
}
function triggerEffects(dep, dirtyLevel, debuggerEventExtraInfo) {
...
effect2.trigger();
}
3.watch
function watchEffect(effect, options) {
return doWatch(effect, null, options);
}
function doWatch(source, cb, {
immediate,
deep,
flush,
once,
onTrack,
onTrigger
} = EMPTY_OBJ) {
...
const effect = new ReactiveEffect(getter, NOOP, scheduler);
const scope = getCurrentScope();
}
简化相关代码
js
function ref(value) {
return createRef(value, false);
}
function createRef(rawValue, shallow) {
...
return new RefImpl(rawValue, shallow);
}
class RefImpl {
...
get value() {
trackRefValue(this);
return this._value;
}
set value(newVal) {
...
triggerRefValue(this, 4, newVal);
}
function trackRefValue(ref2) {
...
trackEffect(
activeEffect,
(_a = ref2.dep) != null ? _a : ref2.dep = createDep(
() => ref2.dep = void 0,
ref2 instanceof ComputedRefImpl ? ref2 : void 0
),
!!(process.env.NODE_ENV !== "production") ? {
target: ref2,
type: "get",
key: "value"
} : void 0
);
}
function trackEffect(effect2, dep, debuggerEventExtraInfo) {
...
dep.set(effect2, effect2._trackId);
effect2.deps[effect2._depsLength++] = dep;
}
}
}
function triggerRefValue(ref2, dirtyLevel = 4, newVal) {
ref2 = toRaw(ref2);
const dep = ref2.dep;
if (dep) {
triggerEffects(
dep,
dirtyLevel,
!!(process.env.NODE_ENV !== "production") ? {
target: ref2,
type: "set",
key: "value",
newValue: newVal
} : void 0
);
}
}
const queueEffectSchedulers = [];
function triggerEffects(dep, dirtyLevel, debuggerEventExtraInfo) {
var _a;
pauseScheduling();
for (const effect2 of dep.keys()) {
...
effect2.trigger();
...
}
resetScheduling();
}
function reactive(target) {
if (isReadonly(target)) {
return target;
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
);
}
function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {
...
const proxy = new Proxy(
target,
targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers
);
proxyMap.set(target, proxy);
}
class BaseReactiveHandler {
...
get(target, key, receiver) {
...
track(target, "get", key);
}
}
class MutableReactiveHandler extends BaseReactiveHandler {
...
set(target, key, value, receiver) {
...
trigger(target, "set", key, value, oldValue);
}
}
function track(target, type, key) {
....
if (shouldTrack && activeEffect) {
...
trackEffect(
activeEffect,
dep,
!!(process.env.NODE_ENV !== "production") ? {
target,
type,
key
} : void 0
);
}
}
function trigger(target, type, key, newValue, oldValue, oldTarget) {
...
pauseScheduling();
for (const dep of deps) {
if (dep) {
triggerEffects(
dep,
4,
!!(process.env.NODE_ENV !== "production") ? {
target,
type,
key,
newValue,
oldValue,
oldTarget
} : void 0
);
}
}
resetScheduling();
}
function readonly(target) {
return createReactiveObject(
target,
true,
readonlyHandlers,
readonlyCollectionHandlers,
readonlyMap
);
}
function shallowRef(value) {
return createRef(value, true);
}
function shallowReactive(target) {
return createReactiveObject(
target,
false,
shallowReactiveHandlers,
shallowCollectionHandlers,
shallowReactiveMap
);
}
function shallowReadonly(target) {
return createReactiveObject(
target,
true,
shallowReadonlyHandlers,
shallowReadonlyCollectionHandlers,
shallowReadonlyMap
);
}
function triggerRef(ref2) {
triggerRefValue(ref2, 4, !!(process.env.NODE_ENV !== "production") ? ref2.value : void 0);
}
function customRef(factory) {
return new CustomRefImpl(factory);
}
class CustomRefImpl {
constructor(factory) {
...
const { get, set } = factory(
() => trackRefValue(this),
() => triggerRefValue(this)
);
this._get = get;
this._set = set;
}
get value() {
return this._get();
}
set value(newVal) {
this._set(newVal);
}
}
function toRaw(observed) {
const raw = observed && observed["__v_raw"];
return raw ? toRaw(raw) : observed;
}
function markRaw(value) {
if (Object.isExtensible(value)) {
def(value, "__v_skip", true);
}
return value;
}
function effectScope(detached) {
return new EffectScope(detached);
}
function getCurrentScope() {
return activeEffectScope;
}
function onScopeDispose(fn) {
if (activeEffectScope) {
activeEffectScope.cleanups.push(fn);
} else if (!!(process.env.NODE_ENV !== "production")) {
warn$2(
`onScopeDispose() is called when there is no active effect scope to be associated with.`
);
}
}
const computed = (getterOrOptions, debugOptions) => {
...
const c = computed$1(getterOrOptions, debugOptions, isInSSRComponentSetup);
};
function computed$1(getterOrOptions, debugOptions, isSSR = false) {
...
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
}
class ComputedRefImpl {
....
this.effect = new ReactiveEffect(
() => getter(this._value),
() => triggerRefValue(
this,
this.effect._dirtyLevel === 2 ? 2 : 3
)
);
}
get value() {
...
if ((!self._cacheable || self.effect.dirty) && hasChanged(self._value, self._value = self.effect.run())) {
triggerRefValue(self, 4);
}
trackRefValue(self);
triggerRefValue(self, 2);
}
return self._value;
}
}
function watch(source, cb, options) {
...
return doWatch(source, cb, options);
}
function doWatch(source, cb, {
immediate,
deep,
flush,
once,
onTrack,
onTrigger
} = EMPTY_OBJ) {
...
if (flush === "sync") {
scheduler = job;
} else if (flush === "post") {
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense);
} else {
job.pre = true;
if (instance)
job.id = instance.uid;
scheduler = () => queueJob(job);
}
const effect = new ReactiveEffect(getter, NOOP, scheduler);
const scope = getCurrentScope();
}
function queueEffectWithSuspense(fn, suspense) {
...
queuePostFlushCb(fn);
}
function queuePostFlushCb(cb) {
...
queueFlush();
}
function queueFlush() {
...
currentFlushPromise = resolvedPromise.then(flushJobs);
}
function flushJobs(seen) {
...
callWithErrorHandling(job, null, 14);
}
function watchEffect(effect, options) {
return doWatch(effect, null, options);
}
function watchPostEffect(effect, options) {
return doWatch(
effect,
null,
!!(process.env.NODE_ENV !== "production") ? extend({}, options, { flush: "post" }) : { flush: "post" }
);
}
function watchSyncEffect(effect, options) {
return doWatch(
effect,
null,
!!(process.env.NODE_ENV !== "production") ? extend({}, options, { flush: "sync" }) : { flush: "sync" }
);
}
完整代码
js
function reactive(target) {
if (isReadonly(target)) {
return target;
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
);
}
function createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {
if (!isObject(target)) {
if (!!(process.env.NODE_ENV !== "production")) {
warn$2(`value cannot be made reactive: ${String(target)}`);
}
return target;
}
if (target["__v_raw"] && !(isReadonly2 && target["__v_isReactive"])) {
return target;
}
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
const targetType = getTargetType(target);
if (targetType === 0 /* INVALID */) {
return target;
}
const proxy = new Proxy(
target,
targetType === 2 /* COLLECTION */ ? collectionHandlers : baseHandlers
);
proxyMap.set(target, proxy);
return proxy;
}
const mutableHandlers = /* @__PURE__ */ new MutableReactiveHandler();
class BaseReactiveHandler {
constructor(_isReadonly = false, _isShallow = false) {
this._isReadonly = _isReadonly;
this._isShallow = _isShallow;
}
get(target, key, receiver) {
const isReadonly2 = this._isReadonly, isShallow2 = this._isShallow;
if (key === "__v_isReactive") {
return !isReadonly2;
} else if (key === "__v_isReadonly") {
return isReadonly2;
} else if (key === "__v_isShallow") {
return isShallow2;
} else if (key === "__v_raw") {
if (receiver === (isReadonly2 ? isShallow2 ? shallowReadonlyMap : readonlyMap : isShallow2 ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype
// this means the reciever is a user proxy of the reactive proxy
Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) {
return target;
}
return;
}
const targetIsArray = isArray(target);
if (!isReadonly2) {
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver);
}
if (key === "hasOwnProperty") {
return hasOwnProperty;
}
}
const res = Reflect.get(target, key, receiver);
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res;
}
if (!isReadonly2) {
track(target, "get", key);
}
if (isShallow2) {
return res;
}
if (isRef(res)) {
return targetIsArray && isIntegerKey(key) ? res : res.value;
}
if (isObject(res)) {
return isReadonly2 ? readonly(res) : reactive(res);
}
return res;
}
}
class MutableReactiveHandler extends BaseReactiveHandler {
constructor(isShallow2 = false) {
super(false, isShallow2);
}
set(target, key, value, receiver) {
let oldValue = target[key];
if (!this._isShallow) {
const isOldValueReadonly = isReadonly(oldValue);
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue);
value = toRaw(value);
}
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
if (isOldValueReadonly) {
return false;
} else {
oldValue.value = value;
return true;
}
}
}
const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key);
const result = Reflect.set(target, key, value, receiver);
if (target === toRaw(receiver)) {
if (!hadKey) {
trigger(target, "add", key, value);
} else if (hasChanged(value, oldValue)) {
trigger(target, "set", key, value, oldValue);
}
}
return result;
}
deleteProperty(target, key) {
const hadKey = hasOwn(target, key);
const oldValue = target[key];
const result = Reflect.deleteProperty(target, key);
if (result && hadKey) {
trigger(target, "delete", key, void 0, oldValue);
}
return result;
}
has(target, key) {
const result = Reflect.has(target, key);
if (!isSymbol(key) || !builtInSymbols.has(key)) {
track(target, "has", key);
}
return result;
}
ownKeys(target) {
track(
target,
"iterate",
isArray(target) ? "length" : ITERATE_KEY
);
return Reflect.ownKeys(target);
}
}
class ReadonlyReactiveHandler extends BaseReactiveHandler {
constructor(isShallow2 = false) {
super(true, isShallow2);
}
set(target, key) {
if (!!(process.env.NODE_ENV !== "production")) {
warn$2(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
);
}
return true;
}
deleteProperty(target, key) {
if (!!(process.env.NODE_ENV !== "production")) {
warn$2(
`Delete operation on key "${String(key)}" failed: target is readonly.`,
target
);
}
return true;
}
}
function track(target, type, key) {
if (shouldTrack && activeEffect) {
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, depsMap = /* @__PURE__ */ new Map());
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, dep = createDep(() => depsMap.delete(key)));
}
trackEffect(
activeEffect,
dep,
!!(process.env.NODE_ENV !== "production") ? {
target,
type,
key
} : void 0
);
}
}
}
function trackEffect(effect2, dep, debuggerEventExtraInfo) {
var _a;
if (dep.get(effect2) !== effect2._trackId) {
dep.set(effect2, effect2._trackId);
const oldDep = effect2.deps[effect2._depsLength];
if (oldDep !== dep) {
if (oldDep) {
cleanupDepEffect(oldDep, effect2);
}
effect2.deps[effect2._depsLength++] = dep;
} else {
effect2._depsLength++;
}
if (!!(process.env.NODE_ENV !== "production")) {
(_a = effect2.onTrack) == null ? void 0 : _a.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo));
}
}
}
function cleanupDepEffect(dep, effect2) {
const trackId = dep.get(effect2);
if (trackId !== void 0 && effect2._trackId !== trackId) {
dep.delete(effect2);
if (dep.size === 0) {
dep.cleanup();
}
}
}
function trigger(target, type, key, newValue, oldValue, oldTarget) {
const depsMap = targetMap.get(target);
if (!depsMap) {
return;
}
let deps = [];
if (type === "clear") {
deps = [...depsMap.values()];
} else if (key === "length" && isArray(target)) {
const newLength = Number(newValue);
depsMap.forEach((dep, key2) => {
if (key2 === "length" || !isSymbol(key2) && key2 >= newLength) {
deps.push(dep);
}
});
} else {
if (key !== void 0) {
deps.push(depsMap.get(key));
}
switch (type) {
case "add":
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY));
if (isMap(target)) {
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
}
} else if (isIntegerKey(key)) {
deps.push(depsMap.get("length"));
}
break;
case "delete":
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY));
if (isMap(target)) {
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY));
}
}
break;
case "set":
if (isMap(target)) {
deps.push(depsMap.get(ITERATE_KEY));
}
break;
}
}
pauseScheduling();
for (const dep of deps) {
if (dep) {
triggerEffects(
dep,
4,
!!(process.env.NODE_ENV !== "production") ? {
target,
type,
key,
newValue,
oldValue,
oldTarget
} : void 0
);
}
}
resetScheduling();
}
function triggerEffects(dep, dirtyLevel, debuggerEventExtraInfo) {
var _a;
pauseScheduling();
for (const effect2 of dep.keys()) {
let tracking;
if (effect2._dirtyLevel < dirtyLevel && (tracking != null ? tracking : tracking = dep.get(effect2) === effect2._trackId)) {
effect2._shouldSchedule || (effect2._shouldSchedule = effect2._dirtyLevel === 0);
effect2._dirtyLevel = dirtyLevel;
}
if (effect2._shouldSchedule && (tracking != null ? tracking : tracking = dep.get(effect2) === effect2._trackId)) {
if (!!(process.env.NODE_ENV !== "production")) {
(_a = effect2.onTrigger) == null ? void 0 : _a.call(effect2, extend({ effect: effect2 }, debuggerEventExtraInfo));
}
effect2.trigger();
if ((!effect2._runnings || effect2.allowRecurse) && effect2._dirtyLevel !== 2) {
effect2._shouldSchedule = false;
if (effect2.scheduler) {
queueEffectSchedulers.push(effect2.scheduler);
}
}
}
}
resetScheduling();
}
class EffectScope {
constructor(detached = false) {
this.detached = detached;
/**
* @internal
*/
this._active = true;
/**
* @internal
*/
this.effects = [];
/**
* @internal
*/
this.cleanups = [];
this.parent = activeEffectScope;
if (!detached && activeEffectScope) {
this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(
this
) - 1;
}
}
get active() {
return this._active;
}
run(fn) {
if (this._active) {
const currentEffectScope = activeEffectScope;
try {
activeEffectScope = this;
return fn();
} finally {
activeEffectScope = currentEffectScope;
}
} else if (!!(process.env.NODE_ENV !== "production")) {
warn$2(`cannot run an inactive effect scope.`);
}
}
/**
* This should only be called on non-detached scopes
* @internal
*/
on() {
activeEffectScope = this;
}
/**
* This should only be called on non-detached scopes
* @internal
*/
off() {
activeEffectScope = this.parent;
}
stop(fromParent) {
if (this._active) {
let i, l;
for (i = 0, l = this.effects.length; i < l; i++) {
this.effects[i].stop();
}
for (i = 0, l = this.cleanups.length; i < l; i++) {
this.cleanups[i]();
}
if (this.scopes) {
for (i = 0, l = this.scopes.length; i < l; i++) {
this.scopes[i].stop(true);
}
}
if (!this.detached && this.parent && !fromParent) {
const last = this.parent.scopes.pop();
if (last && last !== this) {
this.parent.scopes[this.index] = last;
last.index = this.index;
}
}
this.parent = void 0;
this._active = false;
}
}
}
function watchEffect(effect, options) {
return doWatch(effect, null, options);
}
function watchPostEffect(effect, options) {
return doWatch(
effect,
null,
!!(process.env.NODE_ENV !== "production") ? extend({}, options, { flush: "post" }) : { flush: "post" }
);
}
function watchSyncEffect(effect, options) {
return doWatch(
effect,
null,
!!(process.env.NODE_ENV !== "production") ? extend({}, options, { flush: "sync" }) : { flush: "sync" }
);
}
function watch(source, cb, options) {
if (!!(process.env.NODE_ENV !== "production") && !isFunction(cb)) {
warn$1(
`\`watch(fn, options?)\` signature has been moved to a separate API. Use \`watchEffect(fn, options?)\` instead. \`watch\` now only supports \`watch(source, cb, options?) signature.`
);
}
return doWatch(source, cb, options);
}
function doWatch(source, cb, {
immediate,
deep,
flush,
once,
onTrack,
onTrigger
} = EMPTY_OBJ) {
if (cb && once) {
const _cb = cb;
cb = (...args) => {
_cb(...args);
unwatch();
};
}
if (!!(process.env.NODE_ENV !== "production") && deep !== void 0 && typeof deep === "number") {
warn$1(
`watch() "deep" option with number value will be used as watch depth in future versions. Please use a boolean instead to avoid potential breakage.`
);
}
if (!!(process.env.NODE_ENV !== "production") && !cb) {
if (immediate !== void 0) {
warn$1(
`watch() "immediate" option is only respected when using the watch(source, callback, options?) signature.`
);
}
if (deep !== void 0) {
warn$1(
`watch() "deep" option is only respected when using the watch(source, callback, options?) signature.`
);
}
if (once !== void 0) {
warn$1(
`watch() "once" option is only respected when using the watch(source, callback, options?) signature.`
);
}
}
const warnInvalidSource = (s) => {
warn$1(
`Invalid watch source: `,
s,
`A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.`
);
};
const instance = currentInstance;
const reactiveGetter = (source2) => deep === true ? source2 : (
// for deep: false, only traverse root-level properties
traverse(source2, deep === false ? 1 : void 0)
);
let getter;
let forceTrigger = false;
let isMultiSource = false;
if (isRef(source)) {
getter = () => source.value;
forceTrigger = isShallow(source);
} else if (isReactive(source)) {
getter = () => reactiveGetter(source);
forceTrigger = true;
} else if (isArray(source)) {
isMultiSource = true;
forceTrigger = source.some((s) => isReactive(s) || isShallow(s));
getter = () => source.map((s) => {
if (isRef(s)) {
return s.value;
} else if (isReactive(s)) {
return reactiveGetter(s);
} else if (isFunction(s)) {
return callWithErrorHandling(s, instance, 2);
} else {
!!(process.env.NODE_ENV !== "production") && warnInvalidSource(s);
}
});
} else if (isFunction(source)) {
if (cb) {
getter = () => callWithErrorHandling(source, instance, 2);
} else {
getter = () => {
if (cleanup) {
cleanup();
}
return callWithAsyncErrorHandling(
source,
instance,
3,
[onCleanup]
);
};
}
} else {
getter = NOOP;
!!(process.env.NODE_ENV !== "production") && warnInvalidSource(source);
}
if (cb && deep) {
const baseGetter = getter;
getter = () => traverse(baseGetter());
}
let cleanup;
let onCleanup = (fn) => {
cleanup = effect.onStop = () => {
callWithErrorHandling(fn, instance, 4);
cleanup = effect.onStop = void 0;
};
};
let ssrCleanup;
if (isInSSRComponentSetup) {
onCleanup = NOOP;
if (!cb) {
getter();
} else if (immediate) {
callWithAsyncErrorHandling(cb, instance, 3, [
getter(),
isMultiSource ? [] : void 0,
onCleanup
]);
}
if (flush === "sync") {
const ctx = useSSRContext();
ssrCleanup = ctx.__watcherHandles || (ctx.__watcherHandles = []);
} else {
return NOOP;
}
}
let oldValue = isMultiSource ? new Array(source.length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE;
const job = () => {
if (!effect.active || !effect.dirty) {
return;
}
if (cb) {
const newValue = effect.run();
if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => hasChanged(v, oldValue[i])) : hasChanged(newValue, oldValue)) || false) {
if (cleanup) {
cleanup();
}
callWithAsyncErrorHandling(cb, instance, 3, [
newValue,
// pass undefined as the old value when it's changed for the first time
oldValue === INITIAL_WATCHER_VALUE ? void 0 : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue,
onCleanup
]);
oldValue = newValue;
}
} else {
effect.run();
}
};
job.allowRecurse = !!cb;
let scheduler;
if (flush === "sync") {
scheduler = job;
} else if (flush === "post") {
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense);
} else {
job.pre = true;
if (instance)
job.id = instance.uid;
scheduler = () => queueJob(job);
}
const effect = new ReactiveEffect(getter, NOOP, scheduler);
const scope = getCurrentScope();
const unwatch = () => {
effect.stop();
if (scope) {
remove(scope.effects, effect);
}
};
if (!!(process.env.NODE_ENV !== "production")) {
effect.onTrack = onTrack;
effect.onTrigger = onTrigger;
}
if (cb) {
if (immediate) {
job();
} else {
oldValue = effect.run();
}
} else if (flush === "post") {
queuePostRenderEffect(
effect.run.bind(effect),
instance && instance.suspense
);
} else {
effect.run();
}
if (ssrCleanup)
ssrCleanup.push(unwatch);
return unwatch;
}
function effect(fn, options) {
if (fn.effect instanceof ReactiveEffect) {
fn = fn.effect.fn;
}
const _effect = new ReactiveEffect(fn, NOOP, () => {
if (_effect.dirty) {
_effect.run();
}
});
if (options) {
extend(_effect, options);
if (options.scope)
recordEffectScope(_effect, options.scope);
}
if (!options || !options.lazy) {
_effect.run();
}
const runner = _effect.run.bind(_effect);
runner.effect = _effect;
return runner;
}
const computed = (getterOrOptions, debugOptions) => {
const c = computed$1(getterOrOptions, debugOptions, isInSSRComponentSetup);
if (!!(process.env.NODE_ENV !== "production")) {
const i = getCurrentInstance();
if (i && i.appContext.config.warnRecursiveComputed) {
c._warnRecursive = true;
}
}
return c;
};
function computed$1(getterOrOptions, debugOptions, isSSR = false) {
let getter;
let setter;
const onlyGetter = isFunction(getterOrOptions);
if (onlyGetter) {
getter = getterOrOptions;
setter = !!(process.env.NODE_ENV !== "production") ? () => {
warn$2("Write operation failed: computed value is readonly");
} : NOOP;
} else {
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR);
if (!!(process.env.NODE_ENV !== "production") && debugOptions && !isSSR) {
cRef.effect.onTrack = debugOptions.onTrack;
cRef.effect.onTrigger = debugOptions.onTrigger;
}
return cRef;
}
class ComputedRefImpl {
constructor(getter, _setter, isReadonly, isSSR) {
this.getter = getter;
this._setter = _setter;
this.dep = void 0;
this.__v_isRef = true;
this["__v_isReadonly"] = false;
this.effect = new ReactiveEffect(
() => getter(this._value),
() => triggerRefValue(
this,
this.effect._dirtyLevel === 2 ? 2 : 3
)
);
this.effect.computed = this;
this.effect.active = this._cacheable = !isSSR;
this["__v_isReadonly"] = isReadonly;
}
get value() {
const self = toRaw(this);
if ((!self._cacheable || self.effect.dirty) && hasChanged(self._value, self._value = self.effect.run())) {
triggerRefValue(self, 4);
}
trackRefValue(self);
if (self.effect._dirtyLevel >= 2) {
if (!!(process.env.NODE_ENV !== "production") && this._warnRecursive) {
warn$2(COMPUTED_SIDE_EFFECT_WARN, `
getter: `, this.getter);
}
triggerRefValue(self, 2);
}
return self._value;
}
set value(newValue) {
this._setter(newValue);
}
// #region polyfill _dirty for backward compatibility third party code for Vue <= 3.3.x
get _dirty() {
return this.effect.dirty;
}
set _dirty(v) {
this.effect.dirty = v;
}
// #endregion
}
const setupRenderEffect = (instance, initialVNode, container, anchor, parentSuspense, namespace, optimized) => {
const componentUpdateFn = () => {
if (!instance.isMounted) {
let vnodeHook;
const { el, props } = initialVNode;
const { bm, m, parent } = instance;
const isAsyncWrapperVNode = isAsyncWrapper(initialVNode);
toggleRecurse(instance, false);
if (bm) {
invokeArrayFns(bm);
}
if (!isAsyncWrapperVNode && (vnodeHook = props && props.onVnodeBeforeMount)) {
invokeVNodeHook(vnodeHook, parent, initialVNode);
}
toggleRecurse(instance, true);
if (el && hydrateNode) {
const hydrateSubTree = () => {
if (!!(process.env.NODE_ENV !== "production")) {
startMeasure(instance, `render`);
}
instance.subTree = renderComponentRoot(instance);
if (!!(process.env.NODE_ENV !== "production")) {
endMeasure(instance, `render`);
}
if (!!(process.env.NODE_ENV !== "production")) {
startMeasure(instance, `hydrate`);
}
hydrateNode(
el,
instance.subTree,
instance,
parentSuspense,
null
);
if (!!(process.env.NODE_ENV !== "production")) {
endMeasure(instance, `hydrate`);
}
};
if (isAsyncWrapperVNode) {
initialVNode.type.__asyncLoader().then(
// note: we are moving the render call into an async callback,
// which means it won't track dependencies - but it's ok because
// a server-rendered async wrapper is already in resolved state
// and it will never need to change.
() => !instance.isUnmounted && hydrateSubTree()
);
} else {
hydrateSubTree();
}
} else {
if (!!(process.env.NODE_ENV !== "production")) {
startMeasure(instance, `render`);
}
const subTree = instance.subTree = renderComponentRoot(instance);
if (!!(process.env.NODE_ENV !== "production")) {
endMeasure(instance, `render`);
}
if (!!(process.env.NODE_ENV !== "production")) {
startMeasure(instance, `patch`);
}
patch(
null,
subTree,
container,
anchor,
instance,
parentSuspense,
namespace
);
if (!!(process.env.NODE_ENV !== "production")) {
endMeasure(instance, `patch`);
}
initialVNode.el = subTree.el;
}
if (m) {
queuePostRenderEffect(m, parentSuspense);
}
if (!isAsyncWrapperVNode && (vnodeHook = props && props.onVnodeMounted)) {
const scopedInitialVNode = initialVNode;
queuePostRenderEffect(
() => invokeVNodeHook(vnodeHook, parent, scopedInitialVNode),
parentSuspense
);
}
if (initialVNode.shapeFlag & 256 || parent && isAsyncWrapper(parent.vnode) && parent.vnode.shapeFlag & 256) {
instance.ba && invokeKeepAliveHooks(instance.ba);
instance.a && queuePostRenderEffect(instance.a, parentSuspense);
}
instance.isMounted = true;
if (!!(process.env.NODE_ENV !== "production") || __VUE_PROD_DEVTOOLS__) {
devtoolsComponentAdded(instance);
}
initialVNode = container = anchor = null;
} else {
let { next, bu, u, parent, vnode } = instance;
{
const nonHydratedAsyncRoot = locateNonHydratedAsyncRoot(instance);
if (nonHydratedAsyncRoot) {
if (next) {
next.el = vnode.el;
updateComponentPreRender(instance, next, optimized);
}
nonHydratedAsyncRoot.asyncDep.then(() => {
if (!instance.isUnmounted) {
componentUpdateFn();
}
});
return;
}
}
let originNext = next;
let vnodeHook;
if (!!(process.env.NODE_ENV !== "production")) {
pushWarningContext(next || instance.vnode);
}
toggleRecurse(instance, false);
if (next) {
next.el = vnode.el;
updateComponentPreRender(instance, next, optimized);
} else {
next = vnode;
}
if (bu) {
invokeArrayFns(bu);
}
if (vnodeHook = next.props && next.props.onVnodeBeforeUpdate) {
invokeVNodeHook(vnodeHook, parent, next, vnode);
}
toggleRecurse(instance, true);
if (!!(process.env.NODE_ENV !== "production")) {
startMeasure(instance, `render`);
}
const nextTree = renderComponentRoot(instance);
if (!!(process.env.NODE_ENV !== "production")) {
endMeasure(instance, `render`);
}
const prevTree = instance.subTree;
instance.subTree = nextTree;
if (!!(process.env.NODE_ENV !== "production")) {
startMeasure(instance, `patch`);
}
patch(
prevTree,
nextTree,
// parent may have changed if it's in a teleport
hostParentNode(prevTree.el),
// anchor may have changed if it's in a fragment
getNextHostNode(prevTree),
instance,
parentSuspense,
namespace
);
if (!!(process.env.NODE_ENV !== "production")) {
endMeasure(instance, `patch`);
}
next.el = nextTree.el;
if (originNext === null) {
updateHOCHostEl(instance, nextTree.el);
}
if (u) {
queuePostRenderEffect(u, parentSuspense);
}
if (vnodeHook = next.props && next.props.onVnodeUpdated) {
queuePostRenderEffect(
() => invokeVNodeHook(vnodeHook, parent, next, vnode),
parentSuspense
);
}
if (!!(process.env.NODE_ENV !== "production") || __VUE_PROD_DEVTOOLS__) {
devtoolsComponentUpdated(instance);
}
if (!!(process.env.NODE_ENV !== "production")) {
popWarningContext();
}
}
};
const effect = instance.effect = new ReactiveEffect(
componentUpdateFn,
NOOP,
() => queueJob(update),
instance.scope
// track it in component's effect scope
);
const update = instance.update = () => {
if (effect.dirty) {
effect.run();
}
};
update.id = instance.uid;
toggleRecurse(instance, true);
if (!!(process.env.NODE_ENV !== "production")) {
effect.onTrack = instance.rtc ? (e) => invokeArrayFns(instance.rtc, e) : void 0;
effect.onTrigger = instance.rtg ? (e) => invokeArrayFns(instance.rtg, e) : void 0;
update.ownerInstance = instance;
}
update();
};