@planet-matrix/mobius-model 0.1.3 → 0.3.0

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +21 -0
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2 -2
  6. package/dist/index.js.map +12 -6
  7. package/dist/reactor/index.d.ts +3 -0
  8. package/dist/reactor/index.d.ts.map +1 -0
  9. package/dist/reactor/reactor-core/flags.d.ts.map +1 -0
  10. package/dist/reactor/reactor-core/index.d.ts.map +1 -0
  11. package/dist/reactor/reactor-core/primitive.d.ts +276 -0
  12. package/dist/reactor/reactor-core/primitive.d.ts.map +1 -0
  13. package/dist/{signal/signal-core → reactor/reactor-core}/reactive-system.d.ts +102 -22
  14. package/dist/reactor/reactor-core/reactive-system.d.ts.map +1 -0
  15. package/dist/reactor/reactor-operators/branch.d.ts +19 -0
  16. package/dist/reactor/reactor-operators/branch.d.ts.map +1 -0
  17. package/dist/reactor/reactor-operators/convert.d.ts +30 -0
  18. package/dist/reactor/reactor-operators/convert.d.ts.map +1 -0
  19. package/dist/reactor/reactor-operators/create.d.ts +26 -0
  20. package/dist/reactor/reactor-operators/create.d.ts.map +1 -0
  21. package/dist/reactor/reactor-operators/filter.d.ts +269 -0
  22. package/dist/reactor/reactor-operators/filter.d.ts.map +1 -0
  23. package/dist/reactor/reactor-operators/index.d.ts +8 -0
  24. package/dist/reactor/reactor-operators/index.d.ts.map +1 -0
  25. package/dist/reactor/reactor-operators/join.d.ts +48 -0
  26. package/dist/reactor/reactor-operators/join.d.ts.map +1 -0
  27. package/dist/reactor/reactor-operators/map.d.ts +165 -0
  28. package/dist/reactor/reactor-operators/map.d.ts.map +1 -0
  29. package/dist/reactor/reactor-operators/utility.d.ts +48 -0
  30. package/dist/reactor/reactor-operators/utility.d.ts.map +1 -0
  31. package/package.json +9 -12
  32. package/src/index.ts +1 -1
  33. package/src/reactor/README.md +18 -0
  34. package/src/reactor/index.ts +2 -0
  35. package/src/reactor/reactor-core/primitive.ts +1046 -0
  36. package/src/{signal/signal-core → reactor/reactor-core}/reactive-system.ts +392 -93
  37. package/src/reactor/reactor-operators/branch.ts +66 -0
  38. package/src/reactor/reactor-operators/convert.ts +70 -0
  39. package/src/reactor/reactor-operators/create.ts +66 -0
  40. package/src/reactor/reactor-operators/filter.ts +988 -0
  41. package/src/reactor/reactor-operators/index.ts +7 -0
  42. package/src/reactor/reactor-operators/join.ts +174 -0
  43. package/src/reactor/reactor-operators/map.ts +599 -0
  44. package/src/reactor/reactor-operators/utility.ts +102 -0
  45. package/tests/unit/{signal/computed.spec.ts → reactor/alien-signals-computed.spec.ts} +15 -10
  46. package/tests/unit/reactor/alien-signals-effect-scope.spec.ts +86 -0
  47. package/tests/unit/reactor/alien-signals-effect.spec.ts +395 -0
  48. package/tests/unit/reactor/alien-signals-topology.spec.ts +361 -0
  49. package/tests/unit/reactor/alien-signals-trigger.spec.ts +75 -0
  50. package/tests/unit/reactor/alien-signals-untrack.spec.ts +91 -0
  51. package/tests/unit/reactor/preact-signal.spec.ts +73 -0
  52. package/tests/unit/reactor/reactor-core.spec.ts +219 -0
  53. package/tests/unit/reactor/reactor-operators-branch.spec.ts +33 -0
  54. package/tests/unit/reactor/reactor-operators-convert.spec.ts +31 -0
  55. package/tests/unit/reactor/reactor-operators-create.spec.ts +47 -0
  56. package/tests/unit/reactor/reactor-operators-filter.spec.ts +604 -0
  57. package/tests/unit/reactor/reactor-operators-join.spec.ts +94 -0
  58. package/tests/unit/reactor/reactor-operators-map.spec.ts +327 -0
  59. package/tests/unit/reactor/reactor-operators-utility.spec.ts +55 -0
  60. package/dist/signal/index.d.ts +0 -3
  61. package/dist/signal/index.d.ts.map +0 -1
  62. package/dist/signal/signal-core/flags.d.ts.map +0 -1
  63. package/dist/signal/signal-core/index.d.ts.map +0 -1
  64. package/dist/signal/signal-core/primitive.d.ts +0 -67
  65. package/dist/signal/signal-core/primitive.d.ts.map +0 -1
  66. package/dist/signal/signal-core/reactive-system.d.ts.map +0 -1
  67. package/dist/signal/signal-operators/index.d.ts +0 -4
  68. package/dist/signal/signal-operators/index.d.ts.map +0 -1
  69. package/src/signal/index.ts +0 -2
  70. package/src/signal/signal-core/README.md +0 -4
  71. package/src/signal/signal-core/primitive.ts +0 -275
  72. package/src/signal/signal-operators/index.ts +0 -19
  73. package/tests/unit/signal/effect.spec.ts +0 -108
  74. /package/dist/{signal/signal-core → reactor/reactor-core}/flags.d.ts +0 -0
  75. /package/dist/{signal/signal-core → reactor/reactor-core}/index.d.ts +0 -0
  76. /package/src/{signal/signal-core → reactor/reactor-core}/flags.ts +0 -0
  77. /package/src/{signal/signal-core → reactor/reactor-core}/index.ts +0 -0
@@ -38,11 +38,11 @@ export interface Link {
38
38
  */
39
39
  dep: Node;
40
40
  /**
41
- * 连接中的上游节点的下游连接链中的上一个连接。(下一个下游连接)
41
+ * 连接中的上游节点的下游连接链中的上一个连接。(上一个下游连接)
42
42
  */
43
43
  prevSubLink?: Link | undefined;
44
44
  /**
45
- * 连接中的上游节点的下游连接链中的下一个连接。(上一个下游连接)
45
+ * 连接中的上游节点的下游连接链中的下一个连接。(下一个下游连接)
46
46
  */
47
47
  nextSubLink?: Link | undefined;
48
48
 
@@ -61,6 +61,31 @@ export interface Link {
61
61
  }
62
62
 
63
63
  export interface CreateReactiveSystemOptions {
64
+ /**
65
+ * 当节点的上游节点发生变化时此回调会执行。
66
+ */
67
+ onDepChanged?: (sub: Node) => void;
68
+ /**
69
+ * 当节点的下游节点发生变化时此回调会执行。
70
+ */
71
+ onSubChanged?: (dep: Node) => void;
72
+ /**
73
+ * 当节点拥有第一个上游节点时此回调会执行。
74
+ */
75
+ onHasDep?: (sub: Node, dep: Node) => void;
76
+ /**
77
+ * 当节点不再拥有任何上游节点时此回调会执行。
78
+ */
79
+ onNoDep?: (sub: Node, dep: Node) => void;
80
+ /**
81
+ * 当节点拥有第一个下游节点时此回调会执行。
82
+ */
83
+ onHasSub?: (dep: Node, sub: Node) => void;
84
+ /**
85
+ * 当节点不再拥有任何下游节点时此回调会执行。
86
+ */
87
+ onNoSub?: (dep: Node, sub: Node) => void;
88
+
64
89
  /**
65
90
  * 当节点的值需要更新时此回调会执行,返回值表示更新前后值是否变化。
66
91
  */
@@ -71,12 +96,24 @@ export interface CreateReactiveSystemOptions {
71
96
  * 注意:只有包含 Watching 标记的节点会触发此方法。
72
97
  */
73
98
  notify(node: Node): void;
74
- /**
75
- * 当节点不再拥有任何下游节点时此回调会执行。
76
- */
77
- unwatched(node: Node): void;
78
99
  }
79
100
  export interface ReactiveSystem {
101
+ /**
102
+ * 获取节点的上游连接。
103
+ */
104
+ getDepLinksOfNode(node: Node): Link[];
105
+ /**
106
+ * 获取节点的下游连接。
107
+ */
108
+ getSubLinksOfNode(node: Node): Link[];
109
+ /**
110
+ * 获取节点的上游节点。
111
+ */
112
+ getDepsOfNode(node: Node): Node[];
113
+ /**
114
+ * 获取节点的下游节点。
115
+ */
116
+ getSubsOfNode(node: Node): Node[];
80
117
  /**
81
118
  * 判断连接是否是节点的上游连接。
82
119
  */
@@ -86,34 +123,53 @@ export interface ReactiveSystem {
86
123
  */
87
124
  isSubLinkOfNode(link: Link, node: Node): boolean;
88
125
  /**
89
- * 获取两个节点之间的连接。
126
+ * 判断节点是否是目标节点的上游节点。
127
+ */
128
+ isDepOfNode(node: Node, targetNode: Node): boolean;
129
+ /**
130
+ * 判断节点是否是目标节点的下游节点。
131
+ */
132
+ isSubOfNode(node: Node, targetNode: Node): boolean;
133
+ /**
134
+ * 获取两个节点之间的连接(从上游节点的下游连接链中查找)。
90
135
  */
91
- getLinkOfNodes(dep: Node, sub: Node): Link | undefined;
136
+ getLinkBetweenFromDep(dep: Node, sub: Node): Link | undefined;
92
137
  /**
93
- * 判断两个节点之间是否存在连接。
138
+ * 获取两个节点之间的连接(从下游节点的上游连接链中查找)。
94
139
  */
95
- isLinked(nodeA: Node, nodeB: Node): boolean;
140
+ getLinkBetweenFromSub(dep: Node, sub: Node): Link | undefined;
141
+ /**
142
+ * 判断两个节点之间是否存在连接(从上游节点的下游连接链中查找)。
143
+ */
144
+ hasLinkBetweenFromDep(dep: Node, sub: Node): boolean;
145
+ /**
146
+ * 判断两个节点之间是否存在连接(从下游节点的上游连接链中查找)。
147
+ */
148
+ hasLinkBetweenFromSub(dep: Node, sub: Node): boolean;
96
149
  /**
97
150
  * 为两个节点建立连接。
98
151
  */
99
- linkNode(dep: Node, sub: Node): void;
152
+ addLinkBetween(dep: Node, sub: Node): Link;
100
153
  /**
101
- * 为两个节点解除连接。
154
+ * 为两个节点移除连接(从上游节点的下游连接链中查找)。
102
155
  */
103
- unlinkNode(nodeA: Node, nodeB: Node): void;
156
+ removeLinkBetweenFromDep(dep: Node, sub: Node): void;
104
157
  /**
105
- * 解除连接。
158
+ * 为两个节点移除连接(从下游节点的上游连接链中查找)。
106
159
  */
107
- unlink(link: Link): void;
160
+ removeLinkBetweenFromSub(dep: Node, sub: Node): void;
108
161
  /**
109
- * 为节点解除所有上游连接。
162
+ * 移除连接。
110
163
  */
111
- unlinkAllDepLinksOfNode(node: Node): void;
164
+ removeLink(link: Link): void;
112
165
  /**
113
- * 为节点解除所有下游连接。
166
+ * 为节点移除所有上游连接。
114
167
  */
115
- unlinkAllSubLinksOfNode(node: Node): void;
116
-
168
+ removeAllDepLinksOfNode(node: Node): Node[];
169
+ /**
170
+ * 为节点移除所有下游连接。
171
+ */
172
+ removeAllSubLinksOfNode(node: Node): Node[];
117
173
 
118
174
  /**
119
175
  * 获取当前正在进行依赖收集的节点。
@@ -122,7 +178,11 @@ export interface ReactiveSystem {
122
178
  /**
123
179
  * 设置当前正在进行依赖收集的节点。
124
180
  */
125
- setActiveNodeAsSub(nodeAsSub: Node): void;
181
+ setActiveNodeAsSub(nodeAsSub: Node | undefined): void;
182
+ /**
183
+ * 设置当前正在进行依赖收集的节点为空。
184
+ */
185
+ setNoActiveNodeAsSub(): undefined;
126
186
  /**
127
187
  * 重置当前正在进行依赖收集的节点。
128
188
  */
@@ -130,7 +190,15 @@ export interface ReactiveSystem {
130
190
  /**
131
191
  * 将指定节点作为当前正在进行依赖收集的节点执行指定函数。
132
192
  */
133
- withActiveNodeAsSub<T>(nodeAsSub: Node, fn: () => T): T;
193
+ withActiveNodeAsSub<T>(nodeAsSub: Node | undefined, fn: () => T): T;
194
+ /**
195
+ * 以无当前正在进行依赖收集的节点的状态执行指定函数。
196
+ */
197
+ withoutActiveNodeAsSub<T>(fn: () => T): T;
198
+ /**
199
+ * 断言当前没有正在进行依赖收集的节点。
200
+ */
201
+ assertWithoutActiveNodeAsSub(): void;
134
202
  /**
135
203
  * 开始对节点(作为下游节点)进行依赖收集。
136
204
  */
@@ -143,14 +211,26 @@ export interface ReactiveSystem {
143
211
  * 对节点(作为下游节点)进行依赖收集。
144
212
  */
145
213
  withTracking<T>(node: Node, fn: () => T): T;
214
+ /**
215
+ * 以无当前正在进行依赖收集的节点的状态执行指定函数。
216
+ */
217
+ withoutTracking<T>(fn: () => T): T;
218
+ /**
219
+ * 断言当前没有正在进行依赖收集的节点。
220
+ */
221
+ assertWithoutTracking(): void;
146
222
  /**
147
223
  * 为两个节点建立连接(依赖收集优化版)。
148
224
  */
149
- linkNodeOptimizedForTracking(dep: Node, sub: Node): void;
225
+ addLinkBetweenOptimizedForTracking(dep: Node, sub: Node): Link;
150
226
  /**
151
227
  * 将节点作为当前正在进行依赖收集的节点的上游节点。
152
228
  */
153
229
  track(node: Node): void;
230
+ /**
231
+ * 将节点作为当前正在进行依赖收集的节点的上游节点。
232
+ */
233
+ trackNearestMutableOrWatching(node: Node): void;
154
234
 
155
235
  /**
156
236
  * 当节点的值发生变化后,调用此方法更新直接下游节点的标记。
@@ -166,60 +246,192 @@ export interface ReactiveSystem {
166
246
  resolvePending(node: Node): void;
167
247
  }
168
248
  export const createReactiveSystem = (options: CreateReactiveSystemOptions): ReactiveSystem => {
169
- const { update, notify, unwatched } = options;
249
+ const {
250
+ onDepChanged,
251
+ onSubChanged,
252
+ onHasDep,
253
+ onNoDep,
254
+ onHasSub,
255
+ onNoSub,
256
+ update,
257
+ notify
258
+ } = options;
259
+
260
+ /**
261
+ * 获取目标节点的所有上游连接。
262
+ *
263
+ * 依赖收集过程中会尝试复用现存连接,该机制会导致在未完全完成依赖收集前,连接链中可能出现
264
+ * 未经验证的连接。此方法只会返回经过验证的连接。
265
+ */
266
+ const getDepLinksOfNode = (node: Node): Link[] => {
267
+ const depLinks: Link[] = [];
268
+ let currentDepLink = node.headDepLink;
269
+ const tailDepLink = node.tailDepLink;
270
+ while (currentDepLink !== undefined) {
271
+ depLinks.push(currentDepLink);
272
+ const isValidLastDepLink = currentDepLink === tailDepLink;
273
+ if (isValidLastDepLink === true) {
274
+ break;
275
+ } else {
276
+ currentDepLink = currentDepLink.nextDepLink;
277
+ }
278
+ }
279
+ return depLinks;
280
+ }
281
+
282
+ /**
283
+ * 获取目标节点的所有下游连接。
284
+ *
285
+ * 依赖收集过程中会尝试复用现存连接,该机制会导致在未完全完成依赖收集前,连接链中可能出现
286
+ * 未经验证的连接。此方法只会返回经过验证的连接。
287
+ */
288
+ const getSubLinksOfNode = (node: Node): Link[] => {
289
+ const subLinks: Link[] = [];
290
+ let currentSubLink = node.headSubLink;
291
+ const tailSubLink = node.tailSubLink;
292
+ while (currentSubLink !== undefined) {
293
+ subLinks.push(currentSubLink);
294
+ const isValidLastSubLink = currentSubLink === tailSubLink;
295
+ if (isValidLastSubLink === true) {
296
+ break;
297
+ } else {
298
+ currentSubLink = currentSubLink.nextSubLink;
299
+ }
300
+ }
301
+ return subLinks;
302
+ }
303
+
304
+ const getDepsOfNode = (node: Node): Node[] => {
305
+ const depLinks = getDepLinksOfNode(node);
306
+ const deps = depLinks.map(link => link.dep);
307
+ return deps;
308
+ }
309
+
310
+ const getSubsOfNode = (node: Node): Node[] => {
311
+ const subLinks = getSubLinksOfNode(node);
312
+ const subs = subLinks.map(link => link.sub);
313
+ return subs;
314
+ }
170
315
 
171
316
  const isDepLinkOfNode = (link: Link, node: Node): boolean => {
172
- let tailDepLinkOfNode = node.tailDepLink;
173
- while (tailDepLinkOfNode !== undefined) {
174
- if (tailDepLinkOfNode === link) {
317
+ let currentDepLink = node.headDepLink;
318
+ const tailDepLink = node.tailDepLink;
319
+ while (currentDepLink !== undefined) {
320
+ if (currentDepLink === link) {
175
321
  return true;
176
322
  }
177
- tailDepLinkOfNode = tailDepLinkOfNode.prevDepLink;
323
+ const isValidLastDepLink = currentDepLink === tailDepLink;
324
+ if (isValidLastDepLink === true) {
325
+ break;
326
+ } else {
327
+ currentDepLink = currentDepLink.nextDepLink;
328
+ }
178
329
  }
179
330
  return false;
180
331
  }
181
332
 
182
333
  const isSubLinkOfNode = (link: Link, node: Node): boolean => {
183
- let tailSubLinkOfNode = node.tailSubLink;
184
- while (tailSubLinkOfNode !== undefined) {
185
- if (tailSubLinkOfNode === link) {
334
+ let currentSubLink = node.headSubLink;
335
+ const tailSubLink = node.tailSubLink;
336
+ while (currentSubLink !== undefined) {
337
+ if (currentSubLink === link) {
186
338
  return true;
187
339
  }
188
- tailSubLinkOfNode = tailSubLinkOfNode.prevSubLink;
340
+ const isValidLastSubLink = currentSubLink === tailSubLink;
341
+ if (isValidLastSubLink === true) {
342
+ break;
343
+ } else {
344
+ currentSubLink = currentSubLink.nextSubLink;
345
+ }
189
346
  }
190
347
  return false;
191
348
  }
192
349
 
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;
350
+ const isDepOfNode = (node: Node, targetNode: Node): boolean => {
351
+ let currentDepLink = targetNode.headDepLink;
352
+ const tailDepLink = targetNode.tailDepLink;
353
+ while (currentDepLink !== undefined) {
354
+ if (currentDepLink.dep === node) {
355
+ return true;
356
+ }
357
+ const isValidLastDepLink = currentDepLink === tailDepLink;
358
+ if (isValidLastDepLink === true) {
359
+ break;
360
+ } else {
361
+ currentDepLink = currentDepLink.nextDepLink;
198
362
  }
199
- subToCheck = subToCheck.prevSubLink;
200
363
  }
201
- return undefined;
364
+ return false;
202
365
  }
203
366
 
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;
367
+ const isSubOfNode = (node: Node, targetNode: Node): boolean => {
368
+ let currentSubLink = targetNode.headSubLink;
369
+ const tailSubLink = targetNode.tailSubLink;
370
+ while (currentSubLink !== undefined) {
371
+ if (currentSubLink.sub === node) {
372
+ return true;
373
+ }
374
+ const isValidLastSubLink = currentSubLink === tailSubLink;
375
+ if (isValidLastSubLink === true) {
376
+ break;
377
+ } else {
378
+ currentSubLink = currentSubLink.nextSubLink;
379
+ }
212
380
  }
213
381
  return false;
214
382
  }
215
383
 
216
- const linkNode = (dep: Node, sub: Node): void => {
384
+ const getLinkBetweenFromDep = (dep: Node, sub: Node): Link | undefined => {
385
+ let currentSubLink = dep.headSubLink;
386
+ const tailSubLink = dep.tailSubLink;
387
+ while (currentSubLink !== undefined) {
388
+ if (currentSubLink.sub === sub) {
389
+ return currentSubLink;
390
+ }
391
+ const isValidLastSubLink = currentSubLink === tailSubLink;
392
+ if (isValidLastSubLink === true) {
393
+ break;
394
+ } else {
395
+ currentSubLink = currentSubLink.nextSubLink;
396
+ }
397
+ }
398
+ return undefined;
399
+ }
400
+
401
+ const getLinkBetweenFromSub = (dep: Node, sub: Node): Link | undefined => {
402
+ let currentDepLink = sub.headDepLink;
403
+ const tailDepLink = sub.tailDepLink;
404
+ while (currentDepLink !== undefined) {
405
+ if (currentDepLink.dep === dep) {
406
+ return currentDepLink;
407
+ }
408
+ const isValidLastDepLink = currentDepLink === tailDepLink;
409
+ if (isValidLastDepLink === true) {
410
+ break;
411
+ } else {
412
+ currentDepLink = currentDepLink.nextDepLink;
413
+ }
414
+ }
415
+ return undefined;
416
+ }
417
+
418
+ const hasLinkBetweenFromDep = (dep: Node, sub: Node): boolean => {
419
+ const link = getLinkBetweenFromDep(dep, sub);
420
+ return link !== undefined;
421
+ }
422
+
423
+ const hasLinkBetweenFromSub = (dep: Node, sub: Node): boolean => {
424
+ const link = getLinkBetweenFromSub(dep, sub);
425
+ return link !== undefined;
426
+ }
427
+
428
+ const addLinkBetween = (dep: Node, sub: Node): Link => {
217
429
  /**
218
430
  * 检查是否存在重复连接。
219
431
  */
220
- const isLinked = getLinkOfNodes(dep, sub) !== undefined;
221
- if (isLinked === true) {
222
- return;
432
+ const existLink = getLinkBetweenFromSub(dep, sub);
433
+ if (existLink !== undefined) {
434
+ return existLink;
223
435
  }
224
436
 
225
437
  /**
@@ -252,20 +464,39 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
252
464
  dep.headSubLink = newLink;
253
465
  }
254
466
  dep.tailSubLink = newLink;
467
+
468
+ /**
469
+ * 连接全部更新完成之后,触发回调,优先通知上游节点。
470
+ */
471
+ onSubChanged?.(dep);
472
+ const isFirstSub = tailSubLinkOfDep === undefined;
473
+ if (isFirstSub === true) {
474
+ onHasSub?.(dep, sub);
475
+ }
476
+ onDepChanged?.(sub);
477
+ const isFirstDep = tailDepLinkOfSub === undefined;
478
+ if (isFirstDep === true) {
479
+ onHasDep?.(sub, dep);
480
+ }
481
+
482
+ return newLink;
255
483
  }
256
484
 
257
- const unlinkNode = (nodeA: Node, nodeB: Node): void => {
258
- const linkAB = getLinkOfNodes(nodeA, nodeB);
259
- if (linkAB !== undefined) {
260
- unlink(linkAB);
485
+ const removeLinkBetweenFromDep = (dep: Node, sub: Node): void => {
486
+ const linkFromDep = getLinkBetweenFromDep(dep, sub);
487
+ if (linkFromDep !== undefined) {
488
+ removeLink(linkFromDep);
261
489
  }
262
- const linkBA = getLinkOfNodes(nodeB, nodeA);
263
- if (linkBA !== undefined) {
264
- unlink(linkBA);
490
+ }
491
+
492
+ const removeLinkBetweenFromSub = (dep: Node, sub: Node): void => {
493
+ const linkFromSub = getLinkBetweenFromSub(dep, sub);
494
+ if (linkFromSub !== undefined) {
495
+ removeLink(linkFromSub);
265
496
  }
266
497
  }
267
498
 
268
- const unlink = (link: Link): void => {
499
+ const removeLink = (link: Link): void => {
269
500
  const prevDepLink = link.prevDepLink;
270
501
  const dep = link.dep;
271
502
  const nextDepLink = link.nextDepLink;
@@ -312,13 +543,15 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
312
543
  }
313
544
 
314
545
  /**
315
- * 处理完连接链之后,检查当前连接中的上游节点是否还有下游节点。
316
- *
317
- * - 如果有:什么都不做。
318
- * - 如果没有:触发回调。
546
+ * 连接全部更新完成之后,触发回调,优先通知上游节点。
319
547
  */
548
+ onSubChanged?.(dep);
320
549
  if (dep.headSubLink === undefined && dep.tailSubLink === undefined) {
321
- unwatched(dep);
550
+ onNoSub?.(dep, sub);
551
+ }
552
+ onDepChanged?.(sub);
553
+ if (sub.headDepLink === undefined && sub.tailDepLink === undefined) {
554
+ onNoDep?.(sub, dep);
322
555
  }
323
556
 
324
557
  /**
@@ -334,22 +567,28 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
334
567
  link.nextSubLink = undefined;
335
568
  }
336
569
 
337
- const unlinkAllDepLinksOfNode = (node: Node): void => {
570
+ const removeAllDepLinksOfNode = (node: Node): Node[] => {
571
+ const depNodes = [];
338
572
  let currentDepLink = node.headDepLink;
339
573
  while (currentDepLink !== undefined) {
574
+ depNodes.push(currentDepLink.dep);
340
575
  const nextDepLink = currentDepLink.nextDepLink;
341
- unlink(currentDepLink);
576
+ removeLink(currentDepLink);
342
577
  currentDepLink = nextDepLink;
343
578
  }
579
+ return depNodes;
344
580
  }
345
581
 
346
- const unlinkAllSubLinksOfNode = (node: Node): void => {
582
+ const removeAllSubLinksOfNode = (node: Node): Node[] => {
583
+ const subNodes = [];
347
584
  let currentSubLink = node.headSubLink;
348
585
  while (currentSubLink !== undefined) {
586
+ subNodes.push(currentSubLink.sub);
349
587
  const nextSubLink = currentSubLink.nextSubLink;
350
- unlink(currentSubLink);
588
+ removeLink(currentSubLink);
351
589
  currentSubLink = nextSubLink;
352
590
  }
591
+ return subNodes;
353
592
  }
354
593
 
355
594
  let prevActiveNodeAsSubStack: Array<Node | undefined> = [];
@@ -357,14 +596,20 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
357
596
  const getActiveNodeAsSub = (): Node | undefined => {
358
597
  return activeNodeAsSub;
359
598
  }
360
- const setActiveNodeAsSub = (nodeAsSub: Node): void => {
599
+ const setActiveNodeAsSub = (nodeAsSub: Node | undefined): Node | undefined => {
361
600
  prevActiveNodeAsSubStack.push(activeNodeAsSub);
362
601
  activeNodeAsSub = nodeAsSub;
602
+ return activeNodeAsSub;
363
603
  }
364
- const resetActiveNodeAsSub = (): void => {
604
+ const setNoActiveNodeAsSub = (): undefined => {
605
+ // oxlint-disable-next-line no-unsafe-type-assertion
606
+ return setActiveNodeAsSub(undefined) as undefined;
607
+ }
608
+ const resetActiveNodeAsSub = (): Node | undefined => {
365
609
  activeNodeAsSub = prevActiveNodeAsSubStack.pop();
610
+ return activeNodeAsSub;
366
611
  }
367
- const withActiveNodeAsSub = <T>(nodeAsSub: Node, fn: () => T): T => {
612
+ const withActiveNodeAsSub = <T>(nodeAsSub: Node | undefined, fn: () => T): T => {
368
613
  setActiveNodeAsSub(nodeAsSub);
369
614
  try {
370
615
  return fn();
@@ -372,6 +617,15 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
372
617
  resetActiveNodeAsSub();
373
618
  }
374
619
  }
620
+ const withoutActiveNodeAsSub = <T>(fn: () => T): T => {
621
+ return withActiveNodeAsSub(undefined, fn);
622
+ }
623
+ const assertWithoutActiveNodeAsSub = (): void => {
624
+ const currentActiveNodeAsSub = getActiveNodeAsSub();
625
+ if (currentActiveNodeAsSub !== undefined) {
626
+ throw new Error(`Expected no active node as sub, but got one.`);
627
+ }
628
+ }
375
629
 
376
630
  const startTracking = (nodeAsSub: Node): void => {
377
631
  setActiveNodeAsSub(nodeAsSub);
@@ -387,7 +641,7 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
387
641
  let toRemove: Link | undefined = firstRedundantDepLink;
388
642
  while (toRemove !== undefined) {
389
643
  const nextToRemove: Link | undefined = toRemove.nextDepLink;
390
- unlink(toRemove);
644
+ removeLink(toRemove);
391
645
  toRemove = nextToRemove;
392
646
  }
393
647
  }
@@ -404,18 +658,30 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
404
658
  endTracking(node);
405
659
  }
406
660
  }
661
+ const withoutTracking = <T>(fn: () => T): T => {
662
+ return withoutActiveNodeAsSub(() => {
663
+ return fn();
664
+ });
665
+ }
666
+ const assertWithoutTracking = (): void => {
667
+ try {
668
+ assertWithoutActiveNodeAsSub();
669
+ } catch {
670
+ throw new Error(`Expected no active node as sub for tracking, but got one.`);
671
+ }
672
+ }
407
673
  /**
408
674
  * 依赖收集的目标是:在完成一次依赖收集之后,对于所有在依赖收集执行过程中被访问过的节点(上游节点),
409
675
  * 都与进行依赖收集的节点(下游节点)建立且仅建立一个连接。
410
676
  */
411
- const linkNodeOptimizedForTracking = (dep: Node, sub: Node): void => {
677
+ const addLinkBetweenOptimizedForTracking = (dep: Node, sub: Node): Link => {
412
678
  let existingDepLinkToCompare: Link | undefined;
413
679
 
414
680
  const subFlags = sub.flags;
415
681
  if (subFlags.hasTracking() === true && subFlags.hasTrackingReusing() === false) {
416
682
  subFlags.setTrackingReusing();
417
683
  sub.tailDepLink = sub.headDepLink;
418
- existingDepLinkToCompare = sub.tailDepLink
684
+ existingDepLinkToCompare = sub.tailDepLink;
419
685
  } else {
420
686
  existingDepLinkToCompare = sub.tailDepLink?.nextDepLink;
421
687
  }
@@ -423,16 +689,34 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
423
689
  /**
424
690
  * 检查是否可以复用现存连接。
425
691
  */
692
+
426
693
  if (existingDepLinkToCompare !== undefined && existingDepLinkToCompare.dep === dep) {
427
- return;
694
+ // 如果可以,则复用该连接
695
+ sub.tailDepLink = existingDepLinkToCompare;
696
+ // 触发回调
697
+ onDepChanged?.(sub);
698
+ return existingDepLinkToCompare;
699
+ } else {
700
+ // 否则,建立新连接
701
+ const newLink = addLinkBetween(dep, sub);
702
+ return newLink;
428
703
  }
429
-
430
- linkNode(dep, sub);
431
704
  }
432
705
  const track = (node: Node): void => {
433
- const activeNodeAsSub = getActiveNodeAsSub();
434
- if (activeNodeAsSub !== undefined) {
435
- linkNodeOptimizedForTracking(node, activeNodeAsSub);
706
+ let targetSub = getActiveNodeAsSub();
707
+ if (targetSub !== undefined) {
708
+ addLinkBetweenOptimizedForTracking(node, targetSub);
709
+ }
710
+ }
711
+ const trackNearestMutableOrWatching = (node: Node): void => {
712
+ let targetSub = getActiveNodeAsSub();
713
+ while (targetSub !== undefined) {
714
+ const flags = targetSub.flags;
715
+ if (flags.hasMutable() === true || flags.hasWatching() === true) {
716
+ addLinkBetweenOptimizedForTracking(node, targetSub);
717
+ break;
718
+ }
719
+ targetSub = targetSub.headSubLink?.sub;
436
720
  }
437
721
  }
438
722
 
@@ -496,8 +780,8 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
496
780
  * 此时,添加 Pending 标记,根据其它标记决定是否需要 Notify,根据其它标记决定是否需要 Downward。
497
781
  */
498
782
  flags.setPending();
499
- // shouldNotify = shouldNotify;
500
- // shouldDownward = shouldDownward;
783
+ shouldNotify = shouldNotify;
784
+ shouldDownward = shouldDownward;
501
785
  } else if (flags.hasTracking() === false) {
502
786
  /**
503
787
  * 进入此分支的 flags:
@@ -508,7 +792,7 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
508
792
  *
509
793
  * 此时,什么都不做,不需要修改标记,不需要 Notify,不需要 Downward。
510
794
  */
511
- // flags = flags;
795
+ flags = flags;
512
796
  shouldNotify = false;
513
797
  shouldDownward = false;
514
798
  } else if (flags.hasDirty() === false && flags.hasPending() === false) {
@@ -523,7 +807,7 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
523
807
  */
524
808
  flags.setPending();
525
809
  shouldNotify = false;
526
- // shouldDownward = shouldDownward;
810
+ shouldDownward = shouldDownward;
527
811
  } else {
528
812
  /**
529
813
  * 进入此分支的 flags:
@@ -534,7 +818,7 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
534
818
  *
535
819
  * 此时,不需要修改标记,不需要 Notify,不需要 Downward。
536
820
  */
537
- // flags = flags;
821
+ flags = flags;
538
822
  shouldNotify = false;
539
823
  shouldDownward = false;
540
824
  }
@@ -631,25 +915,40 @@ export const createReactiveSystem = (options: CreateReactiveSystemOptions): Reac
631
915
  }
632
916
 
633
917
  return {
918
+ getDepLinksOfNode,
919
+ getSubLinksOfNode,
920
+ getDepsOfNode,
921
+ getSubsOfNode,
634
922
  isDepLinkOfNode,
635
923
  isSubLinkOfNode,
636
- getLinkOfNodes,
637
- isLinked,
638
- linkNode,
639
- unlinkNode,
640
- unlink,
641
- unlinkAllDepLinksOfNode,
642
- unlinkAllSubLinksOfNode,
924
+ isDepOfNode,
925
+ isSubOfNode,
926
+ getLinkBetweenFromDep,
927
+ getLinkBetweenFromSub,
928
+ hasLinkBetweenFromDep,
929
+ hasLinkBetweenFromSub,
930
+ addLinkBetween,
931
+ removeLinkBetweenFromDep,
932
+ removeLinkBetweenFromSub,
933
+ removeLink,
934
+ removeAllDepLinksOfNode,
935
+ removeAllSubLinksOfNode,
643
936
 
644
937
  getActiveNodeAsSub,
645
938
  setActiveNodeAsSub,
939
+ setNoActiveNodeAsSub,
646
940
  resetActiveNodeAsSub,
647
941
  withActiveNodeAsSub,
942
+ withoutActiveNodeAsSub,
943
+ assertWithoutActiveNodeAsSub,
648
944
  startTracking,
649
945
  endTracking,
650
946
  withTracking,
651
- linkNodeOptimizedForTracking,
947
+ withoutTracking,
948
+ assertWithoutTracking,
949
+ addLinkBetweenOptimizedForTracking,
652
950
  track,
951
+ trackNearestMutableOrWatching,
653
952
 
654
953
  shallowPropagate,
655
954
  deeeeepPropagate,