提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

268 lines
6.3 KiB

import { createReactiveSystem } from './system.mjs';
let cycle = 0;
let batchDepth = 0;
let notifyIndex = 0;
let queuedLength = 0;
let activeSub;
const queued = [];
const { link, unlink, propagate, checkDirty, shallowPropagate, } = createReactiveSystem({
update(node) {
if (node.depsTail !== undefined) {
return updateComputed(node);
}
else {
return updateSignal(node);
}
},
notify(effect) {
let insertIndex = queuedLength;
let firstInsertedIndex = insertIndex;
do {
effect.flags &= ~2;
queued[insertIndex++] = effect;
effect = effect.subs?.sub;
if (effect === undefined || !(effect.flags & 2)) {
break;
}
} while (true);
queuedLength = insertIndex;
while (firstInsertedIndex < --insertIndex) {
const left = queued[firstInsertedIndex];
queued[firstInsertedIndex++] = queued[insertIndex];
queued[insertIndex] = left;
}
},
unwatched(node) {
if (!(node.flags & 1)) {
effectScopeOper.call(node);
}
else if (node.depsTail !== undefined) {
node.depsTail = undefined;
node.flags = 17;
purgeDeps(node);
}
},
});
export function getActiveSub() {
return activeSub;
}
export function setActiveSub(sub) {
const prevSub = activeSub;
activeSub = sub;
return prevSub;
}
export function getBatchDepth() {
return batchDepth;
}
export function startBatch() {
++batchDepth;
}
export function endBatch() {
if (!--batchDepth) {
flush();
}
}
export function isSignal(fn) {
return fn.name === 'bound ' + signalOper.name;
}
export function isComputed(fn) {
return fn.name === 'bound ' + computedOper.name;
}
export function isEffect(fn) {
return fn.name === 'bound ' + effectOper.name;
}
export function isEffectScope(fn) {
return fn.name === 'bound ' + effectScopeOper.name;
}
export function signal(initialValue) {
return signalOper.bind({
currentValue: initialValue,
pendingValue: initialValue,
subs: undefined,
subsTail: undefined,
flags: 1,
});
}
export function computed(getter) {
return computedOper.bind({
value: undefined,
subs: undefined,
subsTail: undefined,
deps: undefined,
depsTail: undefined,
flags: 0,
getter: getter,
});
}
export function effect(fn) {
const e = {
fn,
subs: undefined,
subsTail: undefined,
deps: undefined,
depsTail: undefined,
flags: 2,
};
const prevSub = setActiveSub(e);
if (prevSub !== undefined) {
link(e, prevSub, 0);
}
try {
e.fn();
}
finally {
activeSub = prevSub;
}
return effectOper.bind(e);
}
export function effectScope(fn) {
const e = {
deps: undefined,
depsTail: undefined,
subs: undefined,
subsTail: undefined,
flags: 0,
};
const prevSub = setActiveSub(e);
if (prevSub !== undefined) {
link(e, prevSub, 0);
}
try {
fn();
}
finally {
activeSub = prevSub;
}
return effectScopeOper.bind(e);
}
function updateComputed(c) {
++cycle;
c.depsTail = undefined;
c.flags = 5;
const prevSub = setActiveSub(c);
try {
const oldValue = c.value;
return oldValue !== (c.value = c.getter(oldValue));
}
finally {
activeSub = prevSub;
c.flags &= ~4;
purgeDeps(c);
}
}
function updateSignal(s) {
s.flags = 1;
return s.currentValue !== (s.currentValue = s.pendingValue);
}
function run(e) {
const flags = e.flags;
if (flags & 16
|| (flags & 32
&& checkDirty(e.deps, e))) {
++cycle;
e.depsTail = undefined;
e.flags = 6;
const prevSub = setActiveSub(e);
try {
e.fn();
}
finally {
activeSub = prevSub;
e.flags &= ~4;
purgeDeps(e);
}
}
else {
e.flags = 2;
}
}
function flush() {
while (notifyIndex < queuedLength) {
const effect = queued[notifyIndex];
queued[notifyIndex++] = undefined;
run(effect);
}
notifyIndex = 0;
queuedLength = 0;
}
function computedOper() {
const flags = this.flags;
if (flags & 16
|| (flags & 32
&& (checkDirty(this.deps, this)
|| (this.flags = flags & ~32, false)))) {
if (updateComputed(this)) {
const subs = this.subs;
if (subs !== undefined) {
shallowPropagate(subs);
}
}
}
else if (!flags) {
this.flags = 1;
const prevSub = setActiveSub(this);
try {
this.value = this.getter();
}
finally {
activeSub = prevSub;
}
}
const sub = activeSub;
if (sub !== undefined) {
link(this, sub, cycle);
}
return this.value;
}
function signalOper(...value) {
if (value.length) {
if (this.pendingValue !== (this.pendingValue = value[0])) {
this.flags = 17;
const subs = this.subs;
if (subs !== undefined) {
propagate(subs);
if (!batchDepth) {
flush();
}
}
}
}
else {
if (this.flags & 16) {
if (updateSignal(this)) {
const subs = this.subs;
if (subs !== undefined) {
shallowPropagate(subs);
}
}
}
let sub = activeSub;
while (sub !== undefined) {
if (sub.flags & 3) {
link(this, sub, cycle);
break;
}
sub = sub.subs?.sub;
}
return this.currentValue;
}
}
function effectOper() {
effectScopeOper.call(this);
}
function effectScopeOper() {
this.depsTail = undefined;
this.flags = 0;
purgeDeps(this);
const sub = this.subs;
if (sub !== undefined) {
unlink(sub);
}
}
function purgeDeps(sub) {
const depsTail = sub.depsTail;
let dep = depsTail !== undefined ? depsTail.nextDep : sub.deps;
while (dep !== undefined) {
dep = unlink(dep, sub);
}
}