@lytjs/reactivity 5.0.1 → 6.4.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.
- package/README.md +327 -202
- package/dist/async.cjs +276 -0
- package/dist/async.cjs.map +1 -0
- package/dist/async.d.cts +32 -0
- package/dist/async.d.ts +32 -0
- package/dist/async.mjs +273 -0
- package/dist/async.mjs.map +1 -0
- package/dist/index.cjs +1679 -1
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +226 -0
- package/dist/index.d.ts +226 -0
- package/dist/index.mjs +1612 -1
- package/dist/index.mjs.map +1 -0
- package/dist/ref-CwOCKoy2.d.cts +42 -0
- package/dist/ref-CwOCKoy2.d.ts +42 -0
- package/dist/scope-BC3djHz7.d.cts +345 -0
- package/dist/scope-CTxSo201.d.ts +345 -0
- package/dist/scope.cjs +74 -0
- package/dist/scope.cjs.map +1 -0
- package/dist/scope.d.cts +2 -0
- package/dist/scope.d.ts +2 -0
- package/dist/scope.mjs +70 -0
- package/dist/scope.mjs.map +1 -0
- package/dist/signal-DWrUYmvd.d.cts +109 -0
- package/dist/signal-DWrUYmvd.d.ts +109 -0
- package/dist/signal-component.cjs +39 -0
- package/dist/signal-component.cjs.map +1 -0
- package/dist/signal-component.d.cts +31 -0
- package/dist/signal-component.d.ts +31 -0
- package/dist/signal-component.mjs +35 -0
- package/dist/signal-component.mjs.map +1 -0
- package/dist/signal.cjs +350 -1
- package/dist/signal.cjs.map +1 -0
- package/dist/signal.d.cts +1 -0
- package/dist/signal.d.ts +1 -0
- package/dist/signal.mjs +334 -1
- package/dist/signal.mjs.map +1 -0
- package/package.json +54 -31
- package/dist/types/computed.d.ts +0 -70
- package/dist/types/computed.d.ts.map +0 -1
- package/dist/types/effect.d.ts +0 -157
- package/dist/types/effect.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -21
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/reactive.d.ts +0 -98
- package/dist/types/reactive.d.ts.map +0 -1
- package/dist/types/ref.d.ts +0 -128
- package/dist/types/ref.d.ts.map +0 -1
- package/dist/types/scheduler.d.ts +0 -44
- package/dist/types/scheduler.d.ts.map +0 -1
- package/dist/types/signal-component.d.ts +0 -45
- package/dist/types/signal-component.d.ts.map +0 -1
- package/dist/types/signal.d.ts +0 -87
- package/dist/types/signal.d.ts.map +0 -1
- package/dist/types/watch.d.ts +0 -125
- package/dist/types/watch.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,202 +1,327 @@
|
|
|
1
|
-
# @lytjs/reactivity
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## 安装
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @lytjs/reactivity
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
import {
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
1
|
+
# @lytjs/reactivity
|
|
2
|
+
|
|
3
|
+
> LytJS 响应式系统,提供 ref、reactive、computed、watch、Signal 等核心响应式原语
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @lytjs/reactivity
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 核心 API
|
|
12
|
+
|
|
13
|
+
### reactive / shallowReactive / readonly / shallowReadonly
|
|
14
|
+
|
|
15
|
+
创建响应式对象,支持深层/浅层响应和只读模式
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { reactive, shallowReactive, readonly, shallowReadonly } from '@lytjs/reactivity';
|
|
19
|
+
|
|
20
|
+
const state = reactive({ count: 0 });
|
|
21
|
+
state.count++; // 自动追踪
|
|
22
|
+
|
|
23
|
+
const shallow = shallowReactive({ nested: { value: 1 } });
|
|
24
|
+
shallow.nested.value = 2; // 不会触发更新(浅层响应)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### ref / shallowRef
|
|
28
|
+
|
|
29
|
+
创建响应式引用,适用于基本类型值
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { ref, shallowRef, isShallowRef } from '@lytjs/reactivity';
|
|
33
|
+
|
|
34
|
+
const count = ref(0);
|
|
35
|
+
count.value++;
|
|
36
|
+
|
|
37
|
+
const shallow = shallowRef({ nested: { value: 1 } });
|
|
38
|
+
// isShallowRef 类型守卫
|
|
39
|
+
if (isShallowRef(shallow)) {
|
|
40
|
+
console.log('这是一个 ShallowRef');
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### isShallowRef / isComputedRef
|
|
45
|
+
|
|
46
|
+
类型守卫函数,用于在运行时判断 Ref 类型
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { ref, shallowRef, computed, isShallowRef, isComputedRef } from '@lytjs/reactivity';
|
|
50
|
+
|
|
51
|
+
const r = ref(0);
|
|
52
|
+
const sr = shallowRef({ a: 1 });
|
|
53
|
+
const c = computed(() => r.value * 2);
|
|
54
|
+
|
|
55
|
+
isShallowRef(r); // false
|
|
56
|
+
isShallowRef(sr); // true
|
|
57
|
+
isComputedRef(r); // false
|
|
58
|
+
isComputedRef(c); // true
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### computed
|
|
62
|
+
|
|
63
|
+
创建计算属性,自动追踪依赖并缓存结果
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { computed, ref } from '@lytjs/reactivity';
|
|
67
|
+
|
|
68
|
+
const count = ref(1);
|
|
69
|
+
const doubled = computed(() => count.value * 2);
|
|
70
|
+
console.log(doubled.value); // 2
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### watch / watchEffect
|
|
74
|
+
|
|
75
|
+
侦听响应式数据变化并执行副作用
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { watch, watchEffect, ref } from '@lytjs/reactivity';
|
|
79
|
+
|
|
80
|
+
const count = ref(0);
|
|
81
|
+
|
|
82
|
+
// 侦听特定源
|
|
83
|
+
watch(count, (newVal, oldVal) => {
|
|
84
|
+
console.log(`count changed from ${oldVal} to ${newVal}`);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 自动追踪依赖
|
|
88
|
+
watchEffect(() => {
|
|
89
|
+
console.log(`count is ${count.value}`);
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### effect
|
|
94
|
+
|
|
95
|
+
创建自定义响应式副作用
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { effect, stop } from '@lytjs/reactivity';
|
|
99
|
+
|
|
100
|
+
const runner = effect(() => {
|
|
101
|
+
// 副作用逻辑
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// 停止副作用
|
|
105
|
+
stop(runner);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### toRef / toRefs / unref
|
|
109
|
+
|
|
110
|
+
响应式引用工具函数
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { toRef, toRefs, unref } from '@lytjs/reactivity';
|
|
114
|
+
|
|
115
|
+
const state = reactive({ foo: 1, bar: 2 });
|
|
116
|
+
const { foo, bar } = toRefs(state);
|
|
117
|
+
|
|
118
|
+
const refFoo = toRef(state, 'foo');
|
|
119
|
+
const value = unref(refFoo); // 1
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Signal API
|
|
123
|
+
|
|
124
|
+
Signal 是一种独立的响应式原语,拥有独立的订阅/通知机制,同时桥接 effect 系统保持互操作性。
|
|
125
|
+
|
|
126
|
+
### signal
|
|
127
|
+
|
|
128
|
+
创建可写 Signal
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { signal, computed } from '@lytjs/reactivity';
|
|
132
|
+
|
|
133
|
+
const count = signal(0);
|
|
134
|
+
|
|
135
|
+
// 读取值
|
|
136
|
+
console.log(count()); // 0
|
|
137
|
+
|
|
138
|
+
// 设置值
|
|
139
|
+
count.set(1);
|
|
140
|
+
|
|
141
|
+
// 通过 updater 更新
|
|
142
|
+
count.update((prev) => prev + 1);
|
|
143
|
+
|
|
144
|
+
// 停止所有订阅通知,释放资源
|
|
145
|
+
count.dispose();
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### computed (Signal 版本)
|
|
149
|
+
|
|
150
|
+
创建计算 Signal,惰性求值、自动依赖追踪
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { signal, computed } from '@lytjs/reactivity';
|
|
154
|
+
|
|
155
|
+
const count = signal(1);
|
|
156
|
+
const doubled = computed(() => count() * 2);
|
|
157
|
+
|
|
158
|
+
console.log(doubled()); // 2
|
|
159
|
+
|
|
160
|
+
// 停止计算信号的依赖追踪
|
|
161
|
+
doubled.dispose();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### writableComputedSignal
|
|
165
|
+
|
|
166
|
+
创建可写计算 Signal
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { signal, writableComputedSignal } from '@lytjs/reactivity';
|
|
170
|
+
|
|
171
|
+
const firstName = signal('John');
|
|
172
|
+
const lastName = signal('Doe');
|
|
173
|
+
|
|
174
|
+
const fullName = writableComputedSignal(
|
|
175
|
+
() => `${firstName()} ${lastName()}`,
|
|
176
|
+
(val) => {
|
|
177
|
+
const [first, last] = val.split(' ');
|
|
178
|
+
firstName.set(first);
|
|
179
|
+
lastName.set(last);
|
|
180
|
+
},
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
fullName.set('Jane Smith');
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### signalBatch / signalUntrack
|
|
187
|
+
|
|
188
|
+
Signal 批处理和取消追踪
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import { signal, signalBatch, signalUntrack } from '@lytjs/reactivity';
|
|
192
|
+
|
|
193
|
+
const a = signal(1);
|
|
194
|
+
const b = signal(2);
|
|
195
|
+
|
|
196
|
+
// 批量更新:多次 set 只触发一次通知
|
|
197
|
+
signalBatch(() => {
|
|
198
|
+
a.set(10);
|
|
199
|
+
b.set(20);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// 取消追踪:读取 signal 不建立依赖
|
|
203
|
+
const value = signalUntrack(() => a());
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Effect 批处理
|
|
207
|
+
|
|
208
|
+
### batch / batchAsync
|
|
209
|
+
|
|
210
|
+
批量执行多个响应式更新,减少不必要的重复计算
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { batch, batchAsync, ref, effect } from '@lytjs/reactivity';
|
|
214
|
+
|
|
215
|
+
const a = ref(1);
|
|
216
|
+
const b = ref(2);
|
|
217
|
+
|
|
218
|
+
// 同步批处理
|
|
219
|
+
batch(() => {
|
|
220
|
+
a.value = 10;
|
|
221
|
+
b.value = 20;
|
|
222
|
+
// 在 batch 结束前,effect 不会触发
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// 异步批处理
|
|
226
|
+
await batchAsync(async () => {
|
|
227
|
+
a.value = await fetchValue();
|
|
228
|
+
b.value = await fetchOtherValue();
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### batchScope
|
|
233
|
+
|
|
234
|
+
更细粒度的批处理控制
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { batchScope, batchScopeAsync, batchScopeUntrack, isInBatchScope } from '@lytjs/reactivity';
|
|
238
|
+
|
|
239
|
+
// 带选项的批处理
|
|
240
|
+
batchScope(
|
|
241
|
+
() => {
|
|
242
|
+
// 检查是否在批处理作用域内
|
|
243
|
+
if (isInBatchScope()) {
|
|
244
|
+
console.log('当前在批处理作用域内');
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
{ flush: 'sync' },
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
// 异步批处理作用域
|
|
251
|
+
await batchScopeAsync(async () => {
|
|
252
|
+
// 异步操作
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// 取消追踪的批处理
|
|
256
|
+
batchScopeUntrack(() => {
|
|
257
|
+
// 内部的响应式读取不会建立依赖
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 嵌套批处理行为
|
|
262
|
+
|
|
263
|
+
`batch()` 支持嵌套调用,使用栈追踪状态
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import { batch, effect } from '@lytjs/reactivity';
|
|
267
|
+
|
|
268
|
+
batch(() => {
|
|
269
|
+
// 追踪已暂停
|
|
270
|
+
batch(() => {
|
|
271
|
+
// 追踪仍处于暂停状态
|
|
272
|
+
});
|
|
273
|
+
// 内层 batch 结束后,追踪仍处于暂停状态(由外层 batch 控制)
|
|
274
|
+
});
|
|
275
|
+
// 外层 batch 结束后,追踪恢复正常
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## 边界行为与已知限制
|
|
279
|
+
|
|
280
|
+
### `signal()` 的 undefined 参数歧义
|
|
281
|
+
|
|
282
|
+
`signal()` 通过 `arguments.length` 来区分读取操作和写入操作。当传入 `undefined` 作为参数时,由于 `arguments.length > 0` 为 `true`,它会被视为**写入操作**。
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { signal } from '@lytjs/reactivity';
|
|
286
|
+
|
|
287
|
+
const sig = signal<number | undefined>(42);
|
|
288
|
+
sig(undefined); // 写入操作:将值设为 undefined
|
|
289
|
+
sig(); // 读取操作:返回 undefined
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### `computedSignal.dispose()` 方法
|
|
293
|
+
|
|
294
|
+
调用 `dispose()` 后:
|
|
295
|
+
|
|
296
|
+
- 计算信号不再自动追踪依赖变化
|
|
297
|
+
- 后续访问将返回最后一次缓存的值
|
|
298
|
+
- 依赖该计算信号的其他 effect 不会再因依赖变化而被触发
|
|
299
|
+
|
|
300
|
+
## DevTools 集成
|
|
301
|
+
|
|
302
|
+
在开发环境下,响应式系统会自动初始化 DevTools 全局对象:
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// 浏览器控制台
|
|
306
|
+
window.__LYTJS_DEVTOOLS__.getSignals();
|
|
307
|
+
window.__LYTJS_DEVTOOLS__.getEffects();
|
|
308
|
+
window.__LYTJS_DEVTOOLS__.onSignalChange((id, value) => {
|
|
309
|
+
console.log('Signal changed:', id, value);
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## 子路径入口
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// Effect Scope
|
|
317
|
+
import { effectScope, getCurrentScope, onScopeDispose } from '@lytjs/reactivity/scope';
|
|
318
|
+
|
|
319
|
+
// 异步计算
|
|
320
|
+
import { asyncComputed, useAsyncState } from '@lytjs/reactivity/async';
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## 相关包
|
|
324
|
+
|
|
325
|
+
- [@lytjs/core](../core) - 框架核心入口,整合所有子包
|
|
326
|
+
- [@lytjs/component](../component) - 组件系统,依赖响应式系统
|
|
327
|
+
- [@lytjs/common](../common) - 公共工具库
|