2026-05-14 17:12:01 +08:00
|
|
|
// src/observer.js
|
|
|
|
|
let _activeBinding = null;
|
|
|
|
|
let _noWriteBack = null;
|
|
|
|
|
let _updateBindingFn = null;
|
2026-05-15 00:57:35 +08:00
|
|
|
let _updateDepth = 0;
|
|
|
|
|
const MAX_UPDATE_DEPTH = 100;
|
2026-05-14 17:12:01 +08:00
|
|
|
|
|
|
|
|
export function getActiveBinding() { return _activeBinding; }
|
|
|
|
|
export function setActiveBinding(val) { _activeBinding = val; }
|
|
|
|
|
export function getNoWriteBack() { return _noWriteBack; }
|
|
|
|
|
export function setNoWriteBack(val) { _noWriteBack = val; }
|
|
|
|
|
|
|
|
|
|
export function onNotifyUpdate(fn) {
|
|
|
|
|
_updateBindingFn = fn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function NewState(defaults = {}, getter = null, setter = null) {
|
|
|
|
|
const _defaults = {};
|
|
|
|
|
const _stateMappings = new Map();
|
|
|
|
|
const _watchers = new Map();
|
|
|
|
|
const _watchFunc = (k, cb) => {
|
|
|
|
|
if (!_watchers.has(k)) _watchers.set(k, new Set());
|
|
|
|
|
!cb ? _watchers.get(k).clear() : _watchers.get(k).add(cb);
|
|
|
|
|
};
|
|
|
|
|
const _unwatchFunc = (k, cb) => {
|
|
|
|
|
if (!_watchers.has(k)) _watchers.set(k, new Set());
|
|
|
|
|
_watchers.get(k).delete(cb);
|
|
|
|
|
};
|
|
|
|
|
const _getter = getter || (k => _defaults[k]);
|
|
|
|
|
const _setter = setter || ((k, v) => _defaults[k] = v);
|
|
|
|
|
Object.assign(_defaults, defaults);
|
|
|
|
|
|
|
|
|
|
return new Proxy(_defaults, {
|
|
|
|
|
get(target, key) {
|
|
|
|
|
if (key === '__watch') return _watchFunc;
|
|
|
|
|
if (key === '__unwatch') return _unwatchFunc;
|
|
|
|
|
if (_activeBinding) {
|
|
|
|
|
if (!_stateMappings.has(key)) _stateMappings.set(key, new Set());
|
2026-05-15 00:57:35 +08:00
|
|
|
const bindingSet = _stateMappings.get(key);
|
|
|
|
|
bindingSet.add(_activeBinding);
|
|
|
|
|
if (!_activeBinding._sets) _activeBinding._sets = new Set();
|
|
|
|
|
_activeBinding._sets.add(bindingSet);
|
2026-05-14 17:12:01 +08:00
|
|
|
}
|
|
|
|
|
return _getter(key);
|
|
|
|
|
},
|
|
|
|
|
set(target, key, value) {
|
|
|
|
|
if (_getter(key) !== value) {
|
|
|
|
|
_setter(key, value);
|
|
|
|
|
}
|
|
|
|
|
if (_watchers.has(key)) {
|
|
|
|
|
_watchers.get(key).forEach(cb => {
|
|
|
|
|
const r = cb(value);
|
|
|
|
|
if (r !== undefined) {
|
|
|
|
|
value = r;
|
|
|
|
|
target[key] = value;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (_watchers.has(null)) {
|
|
|
|
|
_watchers.get(null).forEach(cb => cb(value));
|
|
|
|
|
}
|
|
|
|
|
if (_stateMappings.has(key)) {
|
2026-05-15 00:57:35 +08:00
|
|
|
if (_updateDepth > MAX_UPDATE_DEPTH) return console.error('Recursive update detected at key:', key), true;
|
|
|
|
|
_updateDepth++;
|
|
|
|
|
try {
|
|
|
|
|
const bindings = _stateMappings.get(key);
|
|
|
|
|
for (const binding of bindings) {
|
|
|
|
|
if (!binding.node.isConnected) {
|
|
|
|
|
bindings.delete(binding);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (_noWriteBack !== binding.node && _updateBindingFn) {
|
|
|
|
|
_updateBindingFn(binding);
|
|
|
|
|
}
|
2026-05-14 17:12:01 +08:00
|
|
|
}
|
2026-05-15 00:57:35 +08:00
|
|
|
} finally {
|
|
|
|
|
_updateDepth--;
|
2026-05-14 17:12:01 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|