j20 0.0.21 → 0.0.23

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/dist/index.js CHANGED
@@ -1,14 +1,7 @@
1
- // 当前正在执行的effect或computed
2
1
  let currentReaction = null;
3
- // 批处理栈,用于延迟 effect 的执行
4
2
  let batchDepth = 0;
5
- // 待执行的 effect 队列
6
3
  let pendingEffects = new Set();
7
- // 存储所有信号的依赖关系
8
4
  const depMap = new WeakMap();
9
- /**
10
- * Signal类 - 用于存储响应式数据
11
- */
12
5
  class Signal {
13
6
  SIGNAL = true;
14
7
  _value;
@@ -18,7 +11,6 @@ class Signal {
18
11
  this._value = value;
19
12
  }
20
13
  get value() {
21
- // 如果有当前正在执行的reaction,建立依赖关系
22
14
  if (currentReaction) {
23
15
  this.track(currentReaction);
24
16
  }
@@ -29,21 +21,19 @@ class Signal {
29
21
  return;
30
22
  this._value = newValue;
31
23
  this._version++;
32
- // 通知所有依赖项
33
- this._deps.forEach((reaction) => {
24
+ if (this._deps.size === 0)
25
+ return;
26
+ const deps = Array.from(this._deps);
27
+ deps.forEach((reaction) => {
34
28
  if (reaction instanceof Computed) {
35
29
  reaction.markDirty();
36
30
  }
37
31
  else if (reaction.isActive()) {
38
- // 对于effect,使用版本检查优化
39
- if (reaction._depVersions.has(this) || reaction.hasDepsChanged()) {
40
- if (batchDepth > 0) {
41
- // 在批处理模式下,延迟执行 effect
42
- pendingEffects.add(reaction);
43
- }
44
- else {
45
- reaction.execute();
46
- }
32
+ if (batchDepth > 0) {
33
+ pendingEffects.add(reaction);
34
+ }
35
+ else {
36
+ reaction.execute();
47
37
  }
48
38
  }
49
39
  });
@@ -51,25 +41,19 @@ class Signal {
51
41
  get version() {
52
42
  return this._version;
53
43
  }
54
- // 建立依赖关系
55
44
  track(reaction) {
56
45
  if (!this._deps.has(reaction)) {
57
46
  this._deps.add(reaction);
58
- // 维护反向依赖关系
59
47
  let reactions = depMap.get(this);
60
48
  if (!reactions) {
61
49
  reactions = new Set();
62
50
  depMap.set(this, reactions);
63
51
  }
64
52
  reactions.add(reaction);
65
- // 在reaction中记录依赖
66
- reaction.track(this);
67
53
  }
54
+ reaction.track(this);
68
55
  }
69
56
  }
70
- /**
71
- * Computed类 - 用于计算派生值
72
- */
73
57
  class Computed {
74
58
  SIGNAL = true;
75
59
  computeFn;
@@ -81,7 +65,6 @@ class Computed {
81
65
  sources = new Set();
82
66
  constructor(computeFn) {
83
67
  this.computeFn = computeFn;
84
- // 使用symbol作为初始值占位符
85
68
  this._value = undefined;
86
69
  }
87
70
  get value() {
@@ -91,12 +74,11 @@ class Computed {
91
74
  if (this.dirty) {
92
75
  this.compute();
93
76
  }
94
- // 建立依赖关系
95
77
  if (currentReaction) {
96
78
  if (!this._deps.has(currentReaction)) {
97
79
  this._deps.add(currentReaction);
98
- currentReaction.track(this);
99
80
  }
81
+ currentReaction.track(this);
100
82
  }
101
83
  return this._value;
102
84
  }
@@ -107,17 +89,14 @@ class Computed {
107
89
  if (this.disposed) {
108
90
  throw new Error("Cannot compute disposed computed");
109
91
  }
110
- // 检查循环依赖
111
- if (computationStack.includes(this)) {
92
+ if (computationStack.has(this)) {
112
93
  throw new Error("Circular dependency detected in computed values");
113
94
  }
114
95
  this.dirty = false;
115
- // 保存当前状态
116
96
  const prevReaction = currentReaction;
117
97
  const prevSources = this.sources;
118
98
  this.sources = new Set();
119
- // 将当前computed加入计算栈
120
- computationStack.push(this);
99
+ computationStack.add(this);
121
100
  currentReaction = this;
122
101
  try {
123
102
  this._value = this.computeFn();
@@ -128,19 +107,15 @@ class Computed {
128
107
  throw e;
129
108
  }
130
109
  finally {
131
- // 清理工作
132
- const index = computationStack.indexOf(this);
133
- if (index !== -1) {
134
- computationStack.splice(index, 1);
135
- }
110
+ computationStack.delete(this);
136
111
  currentReaction = prevReaction;
137
- // 清理不再依赖的sources
138
112
  prevSources.forEach((source) => {
139
113
  if (!this.sources.has(source)) {
140
114
  const reactions = depMap.get(source);
141
115
  if (reactions) {
142
116
  reactions.delete(this);
143
117
  }
118
+ source._deps.delete(this);
144
119
  }
145
120
  });
146
121
  }
@@ -149,14 +124,15 @@ class Computed {
149
124
  if (this.disposed || this.dirty)
150
125
  return;
151
126
  this.dirty = true;
152
- // 通知依赖项
153
- this._deps.forEach((reaction) => {
127
+ if (this._deps.size === 0)
128
+ return;
129
+ const deps = Array.from(this._deps);
130
+ deps.forEach((reaction) => {
154
131
  if (reaction instanceof Computed) {
155
132
  reaction.markDirty();
156
133
  }
157
134
  else if (reaction.isActive()) {
158
135
  if (batchDepth > 0) {
159
- // 在批处理模式下,延迟执行 effect
160
136
  pendingEffects.add(reaction);
161
137
  }
162
138
  else {
@@ -168,52 +144,37 @@ class Computed {
168
144
  track(source) {
169
145
  if (this.disposed)
170
146
  return;
171
- if (!this.sources.has(source)) {
172
- this.sources.add(source);
173
- let reactions = depMap.get(source);
174
- if (!reactions) {
175
- reactions = new Set();
176
- depMap.set(source, reactions);
177
- }
178
- reactions.add(this);
179
- }
147
+ this.sources.add(source);
180
148
  }
181
149
  dispose() {
182
150
  if (this.disposed)
183
151
  return;
184
152
  this.disposed = true;
185
- // 清理所有依赖关系
186
153
  this.sources.forEach((source) => {
187
154
  const reactions = depMap.get(source);
188
155
  if (reactions) {
189
156
  reactions.delete(this);
190
157
  }
158
+ source._deps.delete(this);
191
159
  });
192
160
  this.sources.clear();
193
161
  this._deps.clear();
194
- // 清理引用
195
162
  this._value = undefined;
196
163
  this.computeFn = undefined;
197
164
  }
198
165
  }
199
- // 用于检测循环依赖
200
- const computationStack = [];
201
- /**
202
- * Effect类 - 用于执行副作用
203
- */
166
+ const computationStack = new Set();
204
167
  class Effect {
205
168
  effectFn;
206
169
  cleanupFn = null;
207
170
  disposed = false;
208
171
  _deps = new Set();
209
- _depVersions = new Map();
210
172
  constructor(effectFn) {
211
173
  this.effectFn = effectFn;
212
174
  }
213
175
  execute() {
214
176
  if (this.disposed)
215
177
  return;
216
- // 执行清理函数
217
178
  if (this.cleanupFn) {
218
179
  try {
219
180
  this.cleanupFn();
@@ -223,7 +184,14 @@ class Effect {
223
184
  }
224
185
  this.cleanupFn = null;
225
186
  }
226
- // 执行effect并收集依赖
187
+ this._deps.forEach((dep) => {
188
+ const reactions = depMap.get(dep);
189
+ if (reactions) {
190
+ reactions.delete(this);
191
+ }
192
+ dep._deps.delete(this);
193
+ });
194
+ this._deps.clear();
227
195
  const prevReaction = currentReaction;
228
196
  currentReaction = this;
229
197
  try {
@@ -240,16 +208,14 @@ class Effect {
240
208
  if (this.disposed)
241
209
  return;
242
210
  this.disposed = true;
243
- // 清理所有依赖关系
244
211
  this._deps.forEach((dep) => {
245
212
  const reactions = depMap.get(dep);
246
213
  if (reactions) {
247
214
  reactions.delete(this);
248
215
  }
216
+ dep._deps.delete(this);
249
217
  });
250
218
  this._deps.clear();
251
- this._depVersions.clear();
252
- // 执行清理函数
253
219
  if (this.cleanupFn) {
254
220
  try {
255
221
  this.cleanupFn();
@@ -266,44 +232,20 @@ class Effect {
266
232
  track(source) {
267
233
  if (this.disposed)
268
234
  return;
269
- if (!this._deps.has(source)) {
270
- this._deps.add(source);
271
- // 记录依赖的版本号
272
- if ("version" in source) {
273
- this._depVersions.set(source, source.version);
274
- }
275
- let reactions = depMap.get(source);
276
- if (!reactions) {
277
- reactions = new Set();
278
- depMap.set(source, reactions);
279
- }
280
- reactions.add(this);
281
- }
282
- }
283
- hasDepsChanged() {
284
- for (const [dep, version] of this._depVersions) {
285
- if ("version" in dep && dep.version !== version) {
286
- return true;
287
- }
288
- }
289
- return false;
235
+ this._deps.add(source);
290
236
  }
291
237
  }
292
- // 创建信号
293
238
  function signal(initialValue) {
294
239
  return new Signal(initialValue);
295
240
  }
296
- // 创建计算值
297
241
  function computed(computeFn) {
298
242
  return new Computed(computeFn);
299
243
  }
300
- // 创建副作用
301
244
  function effect$1(effectFn) {
302
245
  const effectInstance = new Effect(effectFn);
303
246
  effectInstance.execute();
304
247
  return effectInstance;
305
248
  }
306
- // 临时取消追踪
307
249
  function untrack(fn) {
308
250
  const prevReaction = currentReaction;
309
251
  currentReaction = null;
@@ -314,7 +256,6 @@ function untrack(fn) {
314
256
  currentReaction = prevReaction;
315
257
  }
316
258
  }
317
- // 批处理 - 延迟 effect 的执行直到批处理结束
318
259
  function batch(fn) {
319
260
  batchDepth++;
320
261
  try {
@@ -322,14 +263,17 @@ function batch(fn) {
322
263
  }
323
264
  finally {
324
265
  batchDepth--;
325
- // 当批处理结束时,执行所有待处理的 effect
326
266
  if (batchDepth === 0) {
327
267
  const effects = Array.from(pendingEffects);
328
268
  pendingEffects.clear();
329
- // 执行所有待处理的 effect
330
269
  effects.forEach((effect) => {
331
270
  if (effect.isActive()) {
332
- effect.execute();
271
+ try {
272
+ effect.execute();
273
+ }
274
+ catch (e) {
275
+ console.error(e);
276
+ }
333
277
  }
334
278
  });
335
279
  }
@@ -373,8 +317,8 @@ const getChildren = (propChildren) => {
373
317
  }
374
318
  return arr;
375
319
  };
376
- let count$1 = 0;
377
- const generateId = () => (++count$1).toString(32);
320
+ let count = 0;
321
+ const generateId = () => (++count).toString(32);
378
322
  const styleObjectToString = (style) => {
379
323
  let styleString = "";
380
324
  for (const key in style) {
@@ -479,19 +423,38 @@ const createDom = (tag, props, children) => {
479
423
  const BRAND = "j20";
480
424
 
481
425
  const isSignal = (a) => a?.SIGNAL === true;
426
+ const createStyleSheet = (css) => {
427
+ const sheet = new CSSStyleSheet();
428
+ sheet.replaceSync(css);
429
+ return sheet;
430
+ };
482
431
  const addStyleSheet = (node, styleSheet) => {
483
- if (node.adoptedStyleSheets &&
432
+ if ("addStyleSheet" in node) {
433
+ node.addStyleSheet(styleSheet);
434
+ }
435
+ else if (node.adoptedStyleSheets &&
484
436
  !node.adoptedStyleSheets.includes(styleSheet)) {
485
437
  node.adoptedStyleSheets = [...node.adoptedStyleSheets, styleSheet];
486
438
  }
487
439
  };
488
440
  const removeStyleSheet = (node, styleSheet) => {
489
- if (node.adoptedStyleSheets && node.adoptedStyleSheets.includes(styleSheet)) {
441
+ if ("removeStyleSheet" in node) {
442
+ node.removeStyleSheet(styleSheet);
443
+ }
444
+ else if (node.adoptedStyleSheets &&
445
+ node.adoptedStyleSheets.includes(styleSheet)) {
490
446
  const newSet = new Set(node.adoptedStyleSheets);
491
447
  newSet.delete(styleSheet);
492
448
  node.adoptedStyleSheets = Array.from(newSet);
493
449
  }
494
450
  };
451
+ const cssHash = (str) => {
452
+ let h = 5381;
453
+ for (let i = 0; i < str.length; i++) {
454
+ h = (h * 33) ^ str.charCodeAt(i);
455
+ }
456
+ return (h >>> 0).toString(36); // 转为短字符串
457
+ };
495
458
 
496
459
  const registerWebComponent = (Comp) => {
497
460
  if (!Comp.customElement) {
@@ -737,8 +700,11 @@ const instanceCreateElement = (instance, runner) => {
737
700
  return fragment;
738
701
  };
739
702
  const instanceCreate = (runner, parent) => {
703
+ parent && hostStack.push(parent.host);
740
704
  const instance = instanceInit(parent);
741
- return [instance, instanceCreateElement(instance, runner)];
705
+ const result = [instance, instanceCreateElement(instance, runner)];
706
+ parent && hostStack.pop();
707
+ return result;
742
708
  };
743
709
  const instanceGetElements = (instance) => {
744
710
  const [start, end] = instance.range;
@@ -913,95 +879,63 @@ const onMount = (callback) => {
913
879
 
914
880
  const onDestroy = (callback) => effect(() => callback);
915
881
 
916
- let count = BigInt(0);
917
- const getId = () => (++count).toString(32);
918
882
  const refWeakMap = new WeakMap();
919
883
  const createCss = (css) => {
920
- const id = getId();
884
+ let id = undefined;
921
885
  return () => {
922
- const styleId = id;
886
+ if (!id)
887
+ id = cssHash(css);
888
+ const classNameId = `j_${id}`;
923
889
  const replacedCss = css.replace(/\.(\w[\w-]*)\s*?\{/g, (_, className) => {
924
- return `.${className}_${styleId} {`;
890
+ return `.${className}_${classNameId} {`;
925
891
  });
926
- styleSheet(id, replacedCss);
892
+ styleSheet(replacedCss, id);
927
893
  return new Proxy({}, {
928
894
  get: (_, prop) => {
929
- return `${prop}_${styleId}`;
895
+ return `${prop}_${classNameId}`;
930
896
  },
931
897
  });
932
898
  };
933
899
  };
934
- const addReference = (host, id, add) => {
900
+ const addReference = (host, id, css) => {
935
901
  if (!refWeakMap.get(host)) {
936
902
  refWeakMap.set(host, []);
937
903
  }
938
904
  const states = refWeakMap.get(host) ?? [];
939
905
  const state = states.find((state) => state.id === id);
940
906
  if (!state) {
941
- refWeakMap.set(host, [
942
- ...states,
943
- {
944
- id,
945
- refCount: 1,
946
- },
947
- ]);
948
- add();
907
+ const newState = {
908
+ id,
909
+ refCount: 1,
910
+ styleSheet: createStyleSheet(css),
911
+ };
912
+ refWeakMap.set(host, [...states, newState]);
913
+ addStyleSheet(host, newState.styleSheet);
949
914
  }
950
915
  else {
951
916
  state.refCount++;
952
917
  }
953
918
  };
954
- const removeReference = (host, id, remove) => {
919
+ const removeReference = (host, id) => {
955
920
  const states = refWeakMap.get(host) ?? [];
956
921
  const state = states.find((state) => state.id === id);
957
922
  if (state) {
958
923
  state.refCount--;
959
924
  if (state.refCount === 0) {
960
- remove();
961
925
  refWeakMap.set(host, states.filter((state) => state.id !== id));
926
+ removeStyleSheet(host, state.styleSheet);
962
927
  }
963
928
  }
964
929
  };
965
- const styleSheet = (id, css) => {
966
- let instance = getCurrentInstance();
967
- const sheet = new CSSStyleSheet();
968
- css && sheet.replaceSync(css);
969
- let root;
970
- while (instance) {
971
- if (instance.host) {
972
- const host = instance.host;
973
- addReference(host, id, () => {
974
- host.addStyleSheet(sheet);
975
- });
976
- onDestroy(() => {
977
- removeReference(host, id, () => host.removeStyleSheet(sheet));
978
- });
979
- break;
980
- }
981
- if (instance.root) {
982
- root = instance.root;
983
- break;
984
- }
985
- instance = instance?.parent;
986
- }
987
- if (root) {
988
- let node = root;
989
- do {
990
- if (node instanceof ShadowRoot || node === document) {
991
- break;
992
- }
993
- node = node.parentNode;
994
- } while (node);
995
- addReference(node, id, () => {
996
- addStyleSheet(node, sheet);
997
- });
998
- onDestroy(() => {
999
- removeReference(node, id, () => {
1000
- removeStyleSheet(node, sheet);
1001
- });
1002
- });
1003
- }
1004
- return sheet;
930
+ const styleSheet = (css, id) => {
931
+ const cssId = id ?? cssHash(css);
932
+ const host = getCurrentHost();
933
+ if (!host)
934
+ throw new Error("host not found");
935
+ addReference(host, cssId, css);
936
+ onDestroy(() => {
937
+ removeReference(host, cssId);
938
+ });
1005
939
  };
1006
940
 
1007
941
  function insertAfter(parentNode, newNode, targetNode) {
@@ -1411,13 +1345,17 @@ const createElement = (tag, props, children) => {
1411
1345
  };
1412
1346
 
1413
1347
  const createRoot = (boot, rootElement) => {
1414
- const rootInstance = {
1415
- root: rootElement,
1416
- id: generateId(),
1417
- range: [document.createTextNode(""), document.createTextNode("")],
1418
- };
1419
- let [, fragment] = instanceCreate(() => createElement(boot, null, null), rootInstance);
1420
- rootElement.append(rootInstance.range[0], fragment, rootInstance.range[1]);
1348
+ let rootHost = rootElement;
1349
+ while (true) {
1350
+ if (rootHost instanceof ShadowRoot || rootHost === document) {
1351
+ break;
1352
+ }
1353
+ rootHost = rootHost.parentNode;
1354
+ }
1355
+ hostStack.push(rootHost);
1356
+ let [rootInstance, fragment] = instanceCreate(() => createElement(boot, null, null));
1357
+ hostStack.pop();
1358
+ rootElement.append(fragment);
1421
1359
  return rootInstance;
1422
1360
  };
1423
1361