@planet-matrix/mobius-model 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.oxlintrc.json +5 -0
- package/CHANGELOG.md +13 -0
- package/README.md +5 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +13 -0
- package/dist/signal-core/flags.d.ts +99 -0
- package/dist/signal-core/flags.d.ts.map +1 -0
- package/dist/signal-core/index.d.ts +4 -0
- package/dist/signal-core/index.d.ts.map +1 -0
- package/dist/signal-core/primitive.d.ts +67 -0
- package/dist/signal-core/primitive.d.ts.map +1 -0
- package/dist/signal-core/reactive-system.d.ts +161 -0
- package/dist/signal-core/reactive-system.d.ts.map +1 -0
- package/dist/signal-operators/index.d.ts +4 -0
- package/dist/signal-operators/index.d.ts.map +1 -0
- package/package.json +44 -0
- package/scripts/build.ts +53 -0
- package/src/index.ts +2 -0
- package/src/signal-core/README.md +4 -0
- package/src/signal-core/flags.ts +275 -0
- package/src/signal-core/index.ts +3 -0
- package/src/signal-core/primitive.ts +275 -0
- package/src/signal-core/reactive-system.ts +658 -0
- package/src/signal-operators/index.ts +19 -0
- package/tests/unit/signals/computed.spec.ts +92 -0
- package/tests/unit/signals/effect.spec.ts +108 -0
- package/tsconfig.json +3 -0
- package/vite.config.ts +8 -0
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
import type { Flags } from './flags'
|
|
2
|
+
|
|
3
|
+
export interface Node {
|
|
4
|
+
/**
|
|
5
|
+
* 当前节点的上游连接链中的第一个连接。
|
|
6
|
+
*/
|
|
7
|
+
headDepLink?: Link | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* 当前节点的上游连接链中的最后一个连接。
|
|
10
|
+
*/
|
|
11
|
+
tailDepLink?: Link | undefined;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 当前节点的下游连接链中的第一个连接。
|
|
15
|
+
*/
|
|
16
|
+
headSubLink?: Link | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* 当前节点的下游连接链中的最后一个连接。
|
|
19
|
+
*/
|
|
20
|
+
tailSubLink?: Link | undefined;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 当前节点的标记。
|
|
24
|
+
*/
|
|
25
|
+
flags: Flags;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 连接用于表示某个节点(下游节点)依赖了某个节点(上游节点)。
|
|
29
|
+
*
|
|
30
|
+
* 多个连接连在一起形成连接链,连接链只有两种情况:
|
|
31
|
+
*
|
|
32
|
+
* 1. 下游连接链:链中所有连接的上游节点相同,通过该链可以找到该上游节点的所有下游节点。
|
|
33
|
+
* 2. 上游连接链:链中所有连接的下游节点相同,通过该链可以找到该下游节点的所有上游节点。
|
|
34
|
+
*/
|
|
35
|
+
export interface Link {
|
|
36
|
+
/**
|
|
37
|
+
* 连接中的上游节点。
|
|
38
|
+
*/
|
|
39
|
+
dep: Node;
|
|
40
|
+
/**
|
|
41
|
+
* 连接中的上游节点的下游连接链中的上一个连接。(下一个下游连接)
|
|
42
|
+
*/
|
|
43
|
+
prevSubLink?: Link | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* 连接中的上游节点的下游连接链中的下一个连接。(上一个下游连接)
|
|
46
|
+
*/
|
|
47
|
+
nextSubLink?: Link | undefined;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 连接中的下游节点。
|
|
51
|
+
*/
|
|
52
|
+
sub: Node;
|
|
53
|
+
/**
|
|
54
|
+
* 连接中的下游节点的上游连接链中的上一个连接。(上一个上游连接)
|
|
55
|
+
*/
|
|
56
|
+
prevDepLink?: Link | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* 连接中的下游节点的上游连接链中的下一个连接。(下一个上游连接)
|
|
59
|
+
*/
|
|
60
|
+
nextDepLink?: Link | undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface CreateReactiveSystemOptions {
|
|
64
|
+
/**
|
|
65
|
+
* 当节点的值需要更新时此回调会执行,返回值表示更新前后值是否变化。
|
|
66
|
+
*/
|
|
67
|
+
update(sub: Node): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* 当节点的上游节点的值发生变化后此回调会执行。
|
|
70
|
+
*
|
|
71
|
+
* 注意:只有包含 Watching 标记的节点会触发此方法。
|
|
72
|
+
*/
|
|
73
|
+
notify(node: Node): void;
|
|
74
|
+
/**
|
|
75
|
+
* 当节点不再拥有任何下游节点时此回调会执行。
|
|
76
|
+
*/
|
|
77
|
+
unwatched(node: Node): void;
|
|
78
|
+
}
|
|
79
|
+
export interface ReactiveSystem {
|
|
80
|
+
/**
|
|
81
|
+
* 判断连接是否是节点的上游连接。
|
|
82
|
+
*/
|
|
83
|
+
isDepLinkOfNode(link: Link, node: Node): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* 判断连接是否是节点的下游连接。
|
|
86
|
+
*/
|
|
87
|
+
isSubLinkOfNode(link: Link, node: Node): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* 获取两个节点之间的连接。
|
|
90
|
+
*/
|
|
91
|
+
getLinkOfNodes(dep: Node, sub: Node): Link | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* 判断两个节点之间是否存在连接。
|
|
94
|
+
*/
|
|
95
|
+
isLinked(nodeA: Node, nodeB: Node): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* 为两个节点建立连接。
|
|
98
|
+
*/
|
|
99
|
+
linkNode(dep: Node, sub: Node): void;
|
|
100
|
+
/**
|
|
101
|
+
* 为两个节点解除连接。
|
|
102
|
+
*/
|
|
103
|
+
unlinkNode(nodeA: Node, nodeB: Node): void;
|
|
104
|
+
/**
|
|
105
|
+
* 解除连接。
|
|
106
|
+
*/
|
|
107
|
+
unlink(link: Link): void;
|
|
108
|
+
/**
|
|
109
|
+
* 为节点解除所有上游连接。
|
|
110
|
+
*/
|
|
111
|
+
unlinkAllDepLinksOfNode(node: Node): void;
|
|
112
|
+
/**
|
|
113
|
+
* 为节点解除所有下游连接。
|
|
114
|
+
*/
|
|
115
|
+
unlinkAllSubLinksOfNode(node: Node): void;
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 获取当前正在进行依赖收集的节点。
|
|
120
|
+
*/
|
|
121
|
+
getActiveNodeAsSub(): Node | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* 设置当前正在进行依赖收集的节点。
|
|
124
|
+
*/
|
|
125
|
+
setActiveNodeAsSub(nodeAsSub: Node): void;
|
|
126
|
+
/**
|
|
127
|
+
* 重置当前正在进行依赖收集的节点。
|
|
128
|
+
*/
|
|
129
|
+
resetActiveNodeAsSub(): void;
|
|
130
|
+
/**
|
|
131
|
+
* 将指定节点作为当前正在进行依赖收集的节点执行指定函数。
|
|
132
|
+
*/
|
|
133
|
+
withActiveNodeAsSub<T>(nodeAsSub: Node, fn: () => T): T;
|
|
134
|
+
/**
|
|
135
|
+
* 开始对节点(作为下游节点)进行依赖收集。
|
|
136
|
+
*/
|
|
137
|
+
startTracking(node: Node): void;
|
|
138
|
+
/**
|
|
139
|
+
* 结束对节点(作为下游节点)进行依赖收集。
|
|
140
|
+
*/
|
|
141
|
+
endTracking(node: Node): void;
|
|
142
|
+
/**
|
|
143
|
+
* 对节点(作为下游节点)进行依赖收集。
|
|
144
|
+
*/
|
|
145
|
+
withTracking<T>(node: Node, fn: () => T): T;
|
|
146
|
+
/**
|
|
147
|
+
* 为两个节点建立连接(依赖收集优化版)。
|
|
148
|
+
*/
|
|
149
|
+
linkNodeOptimizedForTracking(dep: Node, sub: Node): void;
|
|
150
|
+
/**
|
|
151
|
+
* 将节点作为当前正在进行依赖收集的节点的上游节点。
|
|
152
|
+
*/
|
|
153
|
+
track(node: Node): void;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 当节点的值发生变化后,调用此方法更新直接下游节点的标记。
|
|
157
|
+
*/
|
|
158
|
+
shallowPropagate(node: Node): void;
|
|
159
|
+
/**
|
|
160
|
+
* 当节点的值发生变化后,调用此方法更新所有下游节点的标记。
|
|
161
|
+
*/
|
|
162
|
+
deeeeepPropagate(node: Node): void;
|
|
163
|
+
/**
|
|
164
|
+
* 解决节点的 Pending 标记。
|
|
165
|
+
*/
|
|
166
|
+
resolvePending(node: Node): void;
|
|
167
|
+
}
|
|
168
|
+
export const createReactiveSystem = (options: CreateReactiveSystemOptions): ReactiveSystem => {
|
|
169
|
+
const { update, notify, unwatched } = options;
|
|
170
|
+
|
|
171
|
+
const isDepLinkOfNode = (link: Link, node: Node): boolean => {
|
|
172
|
+
let tailDepLinkOfNode = node.tailDepLink;
|
|
173
|
+
while (tailDepLinkOfNode !== undefined) {
|
|
174
|
+
if (tailDepLinkOfNode === link) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
tailDepLinkOfNode = tailDepLinkOfNode.prevDepLink;
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const isSubLinkOfNode = (link: Link, node: Node): boolean => {
|
|
183
|
+
let tailSubLinkOfNode = node.tailSubLink;
|
|
184
|
+
while (tailSubLinkOfNode !== undefined) {
|
|
185
|
+
if (tailSubLinkOfNode === link) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
tailSubLinkOfNode = tailSubLinkOfNode.prevSubLink;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const getLinkOfNodes = (dep: Node, sub: Node): Link | undefined => {
|
|
194
|
+
let subToCheck = dep.tailSubLink;
|
|
195
|
+
while (subToCheck !== undefined) {
|
|
196
|
+
if (subToCheck.sub === sub) {
|
|
197
|
+
return subToCheck;
|
|
198
|
+
}
|
|
199
|
+
subToCheck = subToCheck.prevSubLink;
|
|
200
|
+
}
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const isLinked = (nodeA: Node, nodeB: Node): boolean => {
|
|
205
|
+
const aIsDepOfB = getLinkOfNodes(nodeA, nodeB) !== undefined;
|
|
206
|
+
if (aIsDepOfB === true) {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
const bIsDepOfA = getLinkOfNodes(nodeB, nodeA) !== undefined;
|
|
210
|
+
if (bIsDepOfA === true) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const linkNode = (dep: Node, sub: Node): void => {
|
|
217
|
+
/**
|
|
218
|
+
* 检查是否存在重复连接。
|
|
219
|
+
*/
|
|
220
|
+
const isLinked = getLinkOfNodes(dep, sub) !== undefined;
|
|
221
|
+
if (isLinked === true) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 建立新连接。
|
|
227
|
+
*/
|
|
228
|
+
const newLink: Link = {
|
|
229
|
+
dep,
|
|
230
|
+
prevSubLink: undefined,
|
|
231
|
+
nextSubLink: undefined,
|
|
232
|
+
|
|
233
|
+
sub,
|
|
234
|
+
prevDepLink: undefined,
|
|
235
|
+
nextDepLink: undefined,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const tailDepLinkOfSub = sub.tailDepLink;
|
|
239
|
+
if (tailDepLinkOfSub !== undefined) {
|
|
240
|
+
tailDepLinkOfSub.nextDepLink = newLink;
|
|
241
|
+
newLink.prevDepLink = tailDepLinkOfSub;
|
|
242
|
+
} else {
|
|
243
|
+
sub.headDepLink = newLink;
|
|
244
|
+
}
|
|
245
|
+
sub.tailDepLink = newLink;
|
|
246
|
+
|
|
247
|
+
const tailSubLinkOfDep = dep.tailSubLink;
|
|
248
|
+
if (tailSubLinkOfDep !== undefined) {
|
|
249
|
+
tailSubLinkOfDep.nextSubLink = newLink;
|
|
250
|
+
newLink.prevSubLink = tailSubLinkOfDep;
|
|
251
|
+
} else {
|
|
252
|
+
dep.headSubLink = newLink;
|
|
253
|
+
}
|
|
254
|
+
dep.tailSubLink = newLink;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const unlinkNode = (nodeA: Node, nodeB: Node): void => {
|
|
258
|
+
const linkAB = getLinkOfNodes(nodeA, nodeB);
|
|
259
|
+
if (linkAB !== undefined) {
|
|
260
|
+
unlink(linkAB);
|
|
261
|
+
}
|
|
262
|
+
const linkBA = getLinkOfNodes(nodeB, nodeA);
|
|
263
|
+
if (linkBA !== undefined) {
|
|
264
|
+
unlink(linkBA);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const unlink = (link: Link): void => {
|
|
269
|
+
const prevDepLink = link.prevDepLink;
|
|
270
|
+
const dep = link.dep;
|
|
271
|
+
const nextDepLink = link.nextDepLink;
|
|
272
|
+
const prevSubLink = link.prevSubLink;
|
|
273
|
+
const sub = link.sub;
|
|
274
|
+
const nextSubLink = link.nextSubLink;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* 处理上游连接链。
|
|
278
|
+
*/
|
|
279
|
+
|
|
280
|
+
if (nextDepLink !== undefined) {
|
|
281
|
+
// 如果当前连接的下一个连接存在,则将当前连接的下一个连接的上一个连接指向当前连接的上一个连接
|
|
282
|
+
nextDepLink.prevDepLink = prevDepLink;
|
|
283
|
+
} else {
|
|
284
|
+
// 如果当前连接的下一个连接不存在,则将当前连接的上一个连接作为最后一个连接
|
|
285
|
+
sub.tailDepLink = prevDepLink;
|
|
286
|
+
}
|
|
287
|
+
if (prevDepLink !== undefined) {
|
|
288
|
+
// 如果当前连接的上一个连接存在,则将当前连接的上一个连接的下一个连接指向当前连接的下一个连接
|
|
289
|
+
prevDepLink.nextDepLink = nextDepLink;
|
|
290
|
+
} else {
|
|
291
|
+
// 如果当前连接的上一个连接不存在,则将当前连接的下一个连接作为第一个连接
|
|
292
|
+
sub.headDepLink = nextDepLink;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* 处理下游连接链。
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
if (nextSubLink !== undefined) {
|
|
300
|
+
// 如果当前连接的下一个连接存在,则将当前连接的下一个连接的上一个连接指向当前连接的上一个连接
|
|
301
|
+
nextSubLink.prevSubLink = prevSubLink;
|
|
302
|
+
} else {
|
|
303
|
+
// 如果当前连接的下一个连接不存在,则将当前连接的上一个连接作为最后一个连接
|
|
304
|
+
dep.tailSubLink = prevSubLink;
|
|
305
|
+
}
|
|
306
|
+
if (prevSubLink !== undefined) {
|
|
307
|
+
// 如果当前连接的上一个连接存在,则将当前连接的上一个连接的下一个连接指向当前连接的下一个连接
|
|
308
|
+
prevSubLink.nextSubLink = nextSubLink;
|
|
309
|
+
} else {
|
|
310
|
+
// 如果当前连接的上一个连接不存在,则将当前连接的下一个连接作为第一个连接
|
|
311
|
+
dep.headSubLink = nextSubLink;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* 处理完连接链之后,检查当前连接中的上游节点是否还有下游节点。
|
|
316
|
+
*
|
|
317
|
+
* - 如果有:什么都不做。
|
|
318
|
+
* - 如果没有:触发回调。
|
|
319
|
+
*/
|
|
320
|
+
if (dep.headSubLink === undefined && dep.tailSubLink === undefined) {
|
|
321
|
+
unwatched(dep);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* 彻底断引用,防止内存泄漏。
|
|
326
|
+
*/
|
|
327
|
+
link.prevDepLink = undefined;
|
|
328
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
329
|
+
link.dep = undefined as unknown as Node; // type cast
|
|
330
|
+
link.nextDepLink = undefined;
|
|
331
|
+
link.prevSubLink = undefined;
|
|
332
|
+
// oxlint-disable-next-line no-unsafe-type-assertion
|
|
333
|
+
link.sub = undefined as unknown as Node; // type cast
|
|
334
|
+
link.nextSubLink = undefined;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const unlinkAllDepLinksOfNode = (node: Node): void => {
|
|
338
|
+
let currentDepLink = node.headDepLink;
|
|
339
|
+
while (currentDepLink !== undefined) {
|
|
340
|
+
const nextDepLink = currentDepLink.nextDepLink;
|
|
341
|
+
unlink(currentDepLink);
|
|
342
|
+
currentDepLink = nextDepLink;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const unlinkAllSubLinksOfNode = (node: Node): void => {
|
|
347
|
+
let currentSubLink = node.headSubLink;
|
|
348
|
+
while (currentSubLink !== undefined) {
|
|
349
|
+
const nextSubLink = currentSubLink.nextSubLink;
|
|
350
|
+
unlink(currentSubLink);
|
|
351
|
+
currentSubLink = nextSubLink;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
let prevActiveNodeAsSubStack: Array<Node | undefined> = [];
|
|
356
|
+
let activeNodeAsSub: Node | undefined = undefined;
|
|
357
|
+
const getActiveNodeAsSub = (): Node | undefined => {
|
|
358
|
+
return activeNodeAsSub;
|
|
359
|
+
}
|
|
360
|
+
const setActiveNodeAsSub = (nodeAsSub: Node): void => {
|
|
361
|
+
prevActiveNodeAsSubStack.push(activeNodeAsSub);
|
|
362
|
+
activeNodeAsSub = nodeAsSub;
|
|
363
|
+
}
|
|
364
|
+
const resetActiveNodeAsSub = (): void => {
|
|
365
|
+
activeNodeAsSub = prevActiveNodeAsSubStack.pop();
|
|
366
|
+
}
|
|
367
|
+
const withActiveNodeAsSub = <T>(nodeAsSub: Node, fn: () => T): T => {
|
|
368
|
+
setActiveNodeAsSub(nodeAsSub);
|
|
369
|
+
try {
|
|
370
|
+
return fn();
|
|
371
|
+
} finally {
|
|
372
|
+
resetActiveNodeAsSub();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const startTracking = (nodeAsSub: Node): void => {
|
|
377
|
+
setActiveNodeAsSub(nodeAsSub);
|
|
378
|
+
|
|
379
|
+
const flags = nodeAsSub.flags;
|
|
380
|
+
flags.unsetPending().unsetDirty().setTracking();
|
|
381
|
+
}
|
|
382
|
+
const endTracking = (nodeAsSub: Node): void => {
|
|
383
|
+
const flags = nodeAsSub.flags;
|
|
384
|
+
if (flags.hasTrackingReusing() === true) {
|
|
385
|
+
const firstRedundantDepLink = nodeAsSub?.tailDepLink?.nextDepLink;
|
|
386
|
+
if (firstRedundantDepLink !== undefined) {
|
|
387
|
+
let toRemove: Link | undefined = firstRedundantDepLink;
|
|
388
|
+
while (toRemove !== undefined) {
|
|
389
|
+
const nextToRemove: Link | undefined = toRemove.nextDepLink;
|
|
390
|
+
unlink(toRemove);
|
|
391
|
+
toRemove = nextToRemove;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
flags.unsetTrackingReusing().unsetTracking();
|
|
396
|
+
|
|
397
|
+
resetActiveNodeAsSub();
|
|
398
|
+
}
|
|
399
|
+
const withTracking = <T>(node: Node, fn: () => T): T => {
|
|
400
|
+
startTracking(node);
|
|
401
|
+
try {
|
|
402
|
+
return fn();
|
|
403
|
+
} finally {
|
|
404
|
+
endTracking(node);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* 依赖收集的目标是:在完成一次依赖收集之后,对于所有在依赖收集执行过程中被访问过的节点(上游节点),
|
|
409
|
+
* 都与进行依赖收集的节点(下游节点)建立且仅建立一个连接。
|
|
410
|
+
*/
|
|
411
|
+
const linkNodeOptimizedForTracking = (dep: Node, sub: Node): void => {
|
|
412
|
+
let existingDepLinkToCompare: Link | undefined;
|
|
413
|
+
|
|
414
|
+
const subFlags = sub.flags;
|
|
415
|
+
if (subFlags.hasTracking() === true && subFlags.hasTrackingReusing() === false) {
|
|
416
|
+
subFlags.setTrackingReusing();
|
|
417
|
+
sub.tailDepLink = sub.headDepLink;
|
|
418
|
+
existingDepLinkToCompare = sub.tailDepLink
|
|
419
|
+
} else {
|
|
420
|
+
existingDepLinkToCompare = sub.tailDepLink?.nextDepLink;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* 检查是否可以复用现存连接。
|
|
425
|
+
*/
|
|
426
|
+
if (existingDepLinkToCompare !== undefined && existingDepLinkToCompare.dep === dep) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
linkNode(dep, sub);
|
|
431
|
+
}
|
|
432
|
+
const track = (node: Node): void => {
|
|
433
|
+
const activeNodeAsSub = getActiveNodeAsSub();
|
|
434
|
+
if (activeNodeAsSub !== undefined) {
|
|
435
|
+
linkNodeOptimizedForTracking(node, activeNodeAsSub);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const shallowPropagate = (node: Node): void => {
|
|
440
|
+
// 从头到尾遍历目标节点下游连接链中的所有下游节点,更新其标记。
|
|
441
|
+
|
|
442
|
+
let currentSubLink: Link | undefined = node.headSubLink;
|
|
443
|
+
while (currentSubLink !== undefined) {
|
|
444
|
+
const sub = currentSubLink.sub;
|
|
445
|
+
const flags = sub.flags;
|
|
446
|
+
let isChangeToDirty = false;
|
|
447
|
+
|
|
448
|
+
if (flags.hasPending() === true && flags.hasDirty() === false) {
|
|
449
|
+
// 如果包含 Pending 标记,且不包含 Dirty 标记,为其添加 Dirty 标记,移除 Pending 标记
|
|
450
|
+
flags.setDirty().unsetPending();
|
|
451
|
+
isChangeToDirty = true;
|
|
452
|
+
} else {
|
|
453
|
+
// 如果是其它情况,则处理下一个连接
|
|
454
|
+
currentSubLink = currentSubLink.nextSubLink;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (isChangeToDirty === true) {
|
|
459
|
+
// 如果检查过程中被标记为 Dirty,则酌情触发 notify 回调
|
|
460
|
+
const shouldNotify = flags.hasWatching() === true && flags.hasTracking() === false;
|
|
461
|
+
if (shouldNotify === true) {
|
|
462
|
+
notify(sub);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// 当前连接处理完成,处理下一个连接
|
|
467
|
+
currentSubLink = currentSubLink.nextSubLink;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
interface InternalStack<T> {
|
|
472
|
+
value: T;
|
|
473
|
+
prev: InternalStack<T> | undefined;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const deeeeepPropagate = (node: Node): void => {
|
|
477
|
+
let currentSubLink = node.headSubLink;
|
|
478
|
+
let nextSubLink = currentSubLink?.nextSubLink;
|
|
479
|
+
let unhandledSubLinkStack: InternalStack<Link | undefined> | undefined = undefined;
|
|
480
|
+
|
|
481
|
+
while (currentSubLink !== undefined) {
|
|
482
|
+
const sub = currentSubLink.sub;
|
|
483
|
+
|
|
484
|
+
let flags = sub.flags;
|
|
485
|
+
let shouldNotify = flags.hasWatching();
|
|
486
|
+
let shouldDownward = flags.hasMutable();
|
|
487
|
+
|
|
488
|
+
if (flags.hasTracking() === false && flags.hasDirty() === false && flags.hasPending() === false) {
|
|
489
|
+
/**
|
|
490
|
+
* 进入此分支的 flags:
|
|
491
|
+
* 1. 不包含 Tracking 标记。
|
|
492
|
+
* 2. 不包含 Dirty 标记或 Pending 标记。
|
|
493
|
+
*
|
|
494
|
+
* 说明:上游节点的变更首次传递到该节点。
|
|
495
|
+
*
|
|
496
|
+
* 此时,添加 Pending 标记,根据其它标记决定是否需要 Notify,根据其它标记决定是否需要 Downward。
|
|
497
|
+
*/
|
|
498
|
+
flags.setPending();
|
|
499
|
+
// shouldNotify = shouldNotify;
|
|
500
|
+
// shouldDownward = shouldDownward;
|
|
501
|
+
} else if (flags.hasTracking() === false) {
|
|
502
|
+
/**
|
|
503
|
+
* 进入此分支的 flags:
|
|
504
|
+
* 1. 不包含 Tracking 标记。
|
|
505
|
+
* 2. 包含 Dirty 标记或 Pending 标记。
|
|
506
|
+
*
|
|
507
|
+
* 说明:上游节点的变更已经传递到该节点。
|
|
508
|
+
*
|
|
509
|
+
* 此时,什么都不做,不需要修改标记,不需要 Notify,不需要 Downward。
|
|
510
|
+
*/
|
|
511
|
+
// flags = flags;
|
|
512
|
+
shouldNotify = false;
|
|
513
|
+
shouldDownward = false;
|
|
514
|
+
} else if (flags.hasDirty() === false && flags.hasPending() === false) {
|
|
515
|
+
/**
|
|
516
|
+
* 进入此分支的 flags:
|
|
517
|
+
* 1. 包含 Tracking 标记。
|
|
518
|
+
* 2. 不包含 Dirty 标记或 Pending 标记。
|
|
519
|
+
*
|
|
520
|
+
* 说明:上游节点的变更首次传递到该节点,且该节点正在进行依赖收集。
|
|
521
|
+
*
|
|
522
|
+
* 此时,添加 Pending 标记,不需要 Notify,根据其它标记决定是否需要 Downward。
|
|
523
|
+
*/
|
|
524
|
+
flags.setPending();
|
|
525
|
+
shouldNotify = false;
|
|
526
|
+
// shouldDownward = shouldDownward;
|
|
527
|
+
} else {
|
|
528
|
+
/**
|
|
529
|
+
* 进入此分支的 flags:
|
|
530
|
+
* 1. 包含 Tracking 标记。
|
|
531
|
+
* 2. 包含 Dirty 标记或 Pending 标记。
|
|
532
|
+
*
|
|
533
|
+
* 说明:上游节点的变更已经传递到该节点,且该节点正在进行依赖收集。
|
|
534
|
+
*
|
|
535
|
+
* 此时,不需要修改标记,不需要 Notify,不需要 Downward。
|
|
536
|
+
*/
|
|
537
|
+
// flags = flags;
|
|
538
|
+
shouldNotify = false;
|
|
539
|
+
shouldDownward = false;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (shouldNotify === true) {
|
|
543
|
+
notify(sub);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (shouldDownward === true) {
|
|
547
|
+
const headSubLinkOfSub = sub.headSubLink;
|
|
548
|
+
if (headSubLinkOfSub !== undefined) {
|
|
549
|
+
currentSubLink = headSubLinkOfSub;
|
|
550
|
+
const nextSubLinkOfSub = headSubLinkOfSub.nextSubLink;
|
|
551
|
+
if (nextSubLinkOfSub !== undefined) {
|
|
552
|
+
unhandledSubLinkStack = { value: nextSubLink, prev: unhandledSubLinkStack };
|
|
553
|
+
nextSubLink = nextSubLinkOfSub;
|
|
554
|
+
}
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
currentSubLink = nextSubLink
|
|
560
|
+
if (currentSubLink !== undefined) {
|
|
561
|
+
nextSubLink = currentSubLink.nextSubLink;
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (unhandledSubLinkStack !== undefined) {
|
|
566
|
+
currentSubLink = unhandledSubLinkStack.value;
|
|
567
|
+
unhandledSubLinkStack = unhandledSubLinkStack.prev;
|
|
568
|
+
if (currentSubLink !== undefined) {
|
|
569
|
+
nextSubLink = currentSubLink.nextSubLink;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const resolvePending = (node: Node): void => {
|
|
576
|
+
let currentDepLink = node.headDepLink;
|
|
577
|
+
let unhandledDepLinkStack: InternalStack<Link> | undefined = undefined;
|
|
578
|
+
|
|
579
|
+
while (currentDepLink !== undefined) {
|
|
580
|
+
const currentSub: Node = currentDepLink.sub;
|
|
581
|
+
const currentDep: Node = currentDepLink.dep;
|
|
582
|
+
const subFlags = currentSub.flags;
|
|
583
|
+
const depFlags = currentDep.flags;
|
|
584
|
+
|
|
585
|
+
if (depFlags.hasMutable() === true && depFlags.hasDirty() === true) {
|
|
586
|
+
/**
|
|
587
|
+
* 当前下游节点不包含 Dirty 标记,当前上游节点包含 Dirty 标记。
|
|
588
|
+
*
|
|
589
|
+
* 更新当前上游节点,若值有变化,则将状态向直接下游节点传播。
|
|
590
|
+
*/
|
|
591
|
+
const valueChanged = update(currentDep);
|
|
592
|
+
if (valueChanged === true) {
|
|
593
|
+
shallowPropagate(currentDep);
|
|
594
|
+
}
|
|
595
|
+
} else if (depFlags.hasMutable() === true && depFlags.hasPending() === true) {
|
|
596
|
+
/**
|
|
597
|
+
* 当前下游节点不包含 Dirty 标记,当前上游节点包含 Pending 标记。
|
|
598
|
+
*
|
|
599
|
+
* 继续检查当前上游节点的上游节点。
|
|
600
|
+
*/
|
|
601
|
+
unhandledDepLinkStack = { value: currentDepLink, prev: unhandledDepLinkStack };
|
|
602
|
+
currentDepLink = currentDep.headDepLink;
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (subFlags.hasPending() === true) {
|
|
607
|
+
const nextDepLink = currentDepLink.nextDepLink;
|
|
608
|
+
if (nextDepLink !== undefined) {
|
|
609
|
+
currentDepLink = nextDepLink;
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (unhandledDepLinkStack !== undefined) {
|
|
615
|
+
currentDepLink = unhandledDepLinkStack.value;
|
|
616
|
+
unhandledDepLinkStack = unhandledDepLinkStack.prev;
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* 当一个连接先被存起来,后续又被取出来处理时,说明其所有上游节点都已经处理完成,
|
|
620
|
+
* 因此可以将其 Pending 标记移除。
|
|
621
|
+
*/
|
|
622
|
+
currentDepLink.dep.flags.unsetPending();
|
|
623
|
+
} else {
|
|
624
|
+
/**
|
|
625
|
+
* 所有连接都处理完成,可以安全移除当前连接的下游节点的 Pending 标记。
|
|
626
|
+
*/
|
|
627
|
+
currentDepLink.sub.flags.unsetPending();
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return {
|
|
634
|
+
isDepLinkOfNode,
|
|
635
|
+
isSubLinkOfNode,
|
|
636
|
+
getLinkOfNodes,
|
|
637
|
+
isLinked,
|
|
638
|
+
linkNode,
|
|
639
|
+
unlinkNode,
|
|
640
|
+
unlink,
|
|
641
|
+
unlinkAllDepLinksOfNode,
|
|
642
|
+
unlinkAllSubLinksOfNode,
|
|
643
|
+
|
|
644
|
+
getActiveNodeAsSub,
|
|
645
|
+
setActiveNodeAsSub,
|
|
646
|
+
resetActiveNodeAsSub,
|
|
647
|
+
withActiveNodeAsSub,
|
|
648
|
+
startTracking,
|
|
649
|
+
endTracking,
|
|
650
|
+
withTracking,
|
|
651
|
+
linkNodeOptimizedForTracking,
|
|
652
|
+
track,
|
|
653
|
+
|
|
654
|
+
shallowPropagate,
|
|
655
|
+
deeeeepPropagate,
|
|
656
|
+
resolvePending,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Signal } from "Source/index"
|
|
2
|
+
import { effect, signal } from "Source/index"
|
|
3
|
+
|
|
4
|
+
export const withHistory = <T>(orginal: Signal<T>): Signal<T[]> => {
|
|
5
|
+
const history: T[] = []
|
|
6
|
+
effect(() => {
|
|
7
|
+
history.push(orginal.get())
|
|
8
|
+
})
|
|
9
|
+
const result = signal<T[]>(history)
|
|
10
|
+
return result
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const zip = <T, U>(signal1: Signal<T>, signal2: Signal<U>): Signal<[T, U]> => {
|
|
14
|
+
const result = signal<[T, U]>([signal1.get(), signal2.get()])
|
|
15
|
+
effect(() => {
|
|
16
|
+
result.set([signal1.get(), signal2.get()])
|
|
17
|
+
})
|
|
18
|
+
return result
|
|
19
|
+
}
|