@rue-js/runtime-vapor 0.0.27 → 0.0.28
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/package.json +5 -4
- package/pkg/package.json +17 -0
- package/pkg/rue_runtime_vapor.d.ts +1982 -0
- package/pkg/rue_runtime_vapor.js +4 -0
- package/pkg/rue_runtime_vapor_bg.js +1921 -0
- package/pkg/rue_runtime_vapor_bg.wasm +0 -0
- package/pkg/rue_runtime_vapor_bg.wasm.d.ts +98 -0
|
@@ -0,0 +1,1982 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* useEffect:模拟 React 的效果钩子
|
|
6
|
+
*
|
|
7
|
+
* - 依赖数组采用 `watch([...])` 的统一侦听底层实现
|
|
8
|
+
* - 逐项浅比较(`Object.is`);可通过 `options.equals(prev, next)` 自定义比较
|
|
9
|
+
* - 返回清理函数将通过 `onCleanup()` 注册,在下一次依赖变化重跑前或侦听被处置时执行
|
|
10
|
+
*
|
|
11
|
+
* 示例:
|
|
12
|
+
* ```ts
|
|
13
|
+
* useEffect(() => {
|
|
14
|
+
* const timer = setInterval(() => {}, 1000)
|
|
15
|
+
* return () => clearInterval(timer)
|
|
16
|
+
* }, [])
|
|
17
|
+
*
|
|
18
|
+
* useEffect(() => {
|
|
19
|
+
* console.log('count =', count.value)
|
|
20
|
+
* }, [() => count.value])
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function useEffect(
|
|
24
|
+
effect: () => void | (() => void),
|
|
25
|
+
deps?: any[] | null,
|
|
26
|
+
options?: { equals?: Equals<T>; scheduler?: (run: () => void) => void } | null,
|
|
27
|
+
): void;
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* useSignal:等价于 `useState(initial, { kind: 'signal', ...options })`
|
|
33
|
+
*
|
|
34
|
+
* 用途:
|
|
35
|
+
* - 以“底层信号句柄”的形式管理状态,适合需要精细控制 `get/set/update/setPath/peek` 的场景
|
|
36
|
+
* - 支持 `equals(prev, next)` 自定义等值比较,返回 `true` 表示不触发订阅者
|
|
37
|
+
*
|
|
38
|
+
* 示例:
|
|
39
|
+
* const [sig, setSig] = useSignal({ a: 1 })
|
|
40
|
+
* console.log(sig.get()) // { a: 1 }
|
|
41
|
+
* setSig({ a: 2 }) // 触发订阅者
|
|
42
|
+
* setSig(h => ({ a: h.peek().a + 1 })) // { a: 3 }
|
|
43
|
+
* sig.setPath('a', 4)
|
|
44
|
+
* console.log(sig.get()) // { a: 4 }
|
|
45
|
+
*/
|
|
46
|
+
export function useSignal<T extends Primitive>(
|
|
47
|
+
initial: T | (() => T),
|
|
48
|
+
options?: UseStateOptions<T>,
|
|
49
|
+
): [SignalHandle<Widen<T>>, (v: Widen<T> | ((sig: SignalHandle<Widen<T>>) => Widen<T> | void)) => void]
|
|
50
|
+
export function useSignal<T extends object | Function>(
|
|
51
|
+
initial: T | (() => T),
|
|
52
|
+
options?: UseStateOptions<T>,
|
|
53
|
+
): [SignalHandle<T>, (v: T | ((sig: SignalHandle<T>) => T | void)) => void]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 调试工具:判断对象是否为 reactive 代理
|
|
59
|
+
*/
|
|
60
|
+
export function isReactive(obj: any): boolean;
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* useCallback:根据依赖数组缓存回调函数的引用
|
|
66
|
+
*/
|
|
67
|
+
export function useCallback<T extends (...args: any[]) => any>(fn: T, deps: any[]): T;
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* unref:若参数是 ref,返回其 .value;否则原样返回
|
|
73
|
+
*/
|
|
74
|
+
export function unref<T = any>(obj: any): T;
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 创建带选项的信号
|
|
80
|
+
* options.equals: Function(prev, next) -> bool,返回 true 表示值相等(不触发)
|
|
81
|
+
* 示例(JavaScript):
|
|
82
|
+
* ```javascript
|
|
83
|
+
* const count = signal(0);
|
|
84
|
+
* createEffect(() => {
|
|
85
|
+
* console.log('count =', count.get());
|
|
86
|
+
* });
|
|
87
|
+
* count.set(1); // 触发 effect
|
|
88
|
+
*
|
|
89
|
+
* const eq = (prev, next) => prev === next;
|
|
90
|
+
* const s = signal(0, { equals: eq });
|
|
91
|
+
* s.set(0); // 不触发,因为 equals 返回 true(相等)
|
|
92
|
+
* s.set(2); // 触发订阅者
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export function signal<T = any>(
|
|
96
|
+
initial: T,
|
|
97
|
+
options?: { equals?: Equals<T> },
|
|
98
|
+
forceGlobal?: boolean,
|
|
99
|
+
): SignalHandle<T>;
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 调试工具:获取 reactive 代理背后的原始数据快照
|
|
105
|
+
* - 对 reactive 对象:通过隐藏的 `__signal__` 获取当前值(不收集依赖)
|
|
106
|
+
* - 对 ref:返回其 `.value`
|
|
107
|
+
* - 其他:原样返回
|
|
108
|
+
*/
|
|
109
|
+
export function toRaw<T = any>(obj: any): T;
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 创建 Ref:返回一个带有 `value` 字段的响应式代理对象
|
|
115
|
+
*
|
|
116
|
+
* 用法(JavaScript / TypeScript):
|
|
117
|
+
* ```ts
|
|
118
|
+
* // 基本使用:读写 value,自动依赖收集
|
|
119
|
+
* const r = ref(0)
|
|
120
|
+
* console.log(r.value) // 0
|
|
121
|
+
* r.value = 1 // 触发订阅者
|
|
122
|
+
*
|
|
123
|
+
* // 与 watchEffect 配合(依赖自动收集)
|
|
124
|
+
* const stop = watchEffect(() => {
|
|
125
|
+
* console.log('ref value =', r.value)
|
|
126
|
+
* })
|
|
127
|
+
* r.value = 2 // 触发前面的 watchEffect
|
|
128
|
+
* stop() // 停止响应
|
|
129
|
+
*
|
|
130
|
+
* // peek:查看当前值,不收集依赖(不会订阅当前副作用)
|
|
131
|
+
* const cur = r.peek() // 仅返回值,不产生订阅
|
|
132
|
+
*
|
|
133
|
+
* // update:基于当前值计算并写回
|
|
134
|
+
* r.update(prev => prev + 1) // 等价于 r.value = (prev + 1)
|
|
135
|
+
*
|
|
136
|
+
* // 自定义等值比较:避免无意义的触发
|
|
137
|
+
* const r2 = ref({ a: 1 }, { equals: (p, n) => _.isEqual(p?.value, n?.value) })
|
|
138
|
+
* r2.value = { a: 1 } // 不触发(相等)
|
|
139
|
+
*
|
|
140
|
+
* // 与组件/DOM 结合(Vapor 模式下自动更新)
|
|
141
|
+
* // <span>{r.value}</span> 会被编译为原生 DOM + 响应式更新
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export function ref<T = any>(
|
|
145
|
+
initial: T,
|
|
146
|
+
options?: { equals?: Equals<T> } | null,
|
|
147
|
+
forceGlobal?: boolean,
|
|
148
|
+
): { value: T };
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* useRef:在 Hook 插槽上持久化 { current } 容器
|
|
154
|
+
*/
|
|
155
|
+
export function useRef<T = any>(initial?: T): { current: T | undefined };
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 创建计算属性
|
|
161
|
+
* - 参数:可为函数 `() => any`,或对象 `{ get: () => any }`
|
|
162
|
+
* 返回:一个只读信号句柄(通过 get/peek 读取,内部通过 effect 驱动更新)
|
|
163
|
+
* 示例(JavaScript):
|
|
164
|
+
* ```javascript
|
|
165
|
+
* const count = signal(1);
|
|
166
|
+
* const double = computed(() => count.get() * 2);
|
|
167
|
+
*
|
|
168
|
+
* createEffect(() => {
|
|
169
|
+
* console.log('double =', double.get());
|
|
170
|
+
* });
|
|
171
|
+
*
|
|
172
|
+
* count.set(2); // double 将变为 4 并触发订阅者
|
|
173
|
+
* ```
|
|
174
|
+
* 更多用法示例:
|
|
175
|
+
*
|
|
176
|
+
* 1) 对象参数(只读 getter):
|
|
177
|
+
* ```javascript
|
|
178
|
+
* const first = signal('John');
|
|
179
|
+
* const last = signal('Doe');
|
|
180
|
+
* const fullName = computed({
|
|
181
|
+
* get: () => first.get() + ' ' + last.get()
|
|
182
|
+
* });
|
|
183
|
+
* createEffect(() => {
|
|
184
|
+
* console.log('fullName =', fullName.get()); // John Doe
|
|
185
|
+
* });
|
|
186
|
+
* ```
|
|
187
|
+
*
|
|
188
|
+
* 2) 通过更新源信号实现“可写效果”(模拟 Vue3 的 setter 行为):
|
|
189
|
+
* ```javascript
|
|
190
|
+
* const setFullName = (nv) => {
|
|
191
|
+
* const [f, l] = nv.split(' ');
|
|
192
|
+
* first.set(f);
|
|
193
|
+
* last.set(l);
|
|
194
|
+
* };
|
|
195
|
+
* setFullName('David Smith'); // fullName 将变为 "David Smith"
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* 3) 直接使用 `{ get, set }` 创建“可写 computed”,支持 `.set(value)`:
|
|
199
|
+
* ```javascript
|
|
200
|
+
* const fullName = computed({
|
|
201
|
+
* get: () => first.get() + ' ' + last.get(),
|
|
202
|
+
* set: (nv) => {
|
|
203
|
+
* const [f, l] = nv.split(' ');
|
|
204
|
+
* first.set(f);
|
|
205
|
+
* last.set(l);
|
|
206
|
+
* }
|
|
207
|
+
* });
|
|
208
|
+
* fullName.set('David Smith'); // 将调用你的 set 更新源信号,并重算派生值
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export function computed<T>(get: ComputedGetter<T>, forceGlobal?: boolean): ReadonlySignal<T>
|
|
212
|
+
export function computed<T>(options: ComputedOptions<T>, forceGlobal?: boolean): WritableSignal<T>
|
|
213
|
+
export function computed<T>(
|
|
214
|
+
arg: any,
|
|
215
|
+
forceGlobal?: boolean,
|
|
216
|
+
): ReadonlySignal<T> | WritableSignal<T>;
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 创建 Reactive:返回一个对象/数组的响应式代理(深/浅、只读可选)
|
|
222
|
+
*
|
|
223
|
+
* 用法(JavaScript / TypeScript):
|
|
224
|
+
* ```ts
|
|
225
|
+
* // 基础对象:读取与写入都响应式
|
|
226
|
+
* const state = reactive({ user: { name: 'A' }, items: ['x'] })
|
|
227
|
+
* console.log(state.user.name) // 'A'
|
|
228
|
+
* state.user.name = 'B' // 写入嵌套字段,触发订阅者
|
|
229
|
+
* state.items.push('y') // 数组写入也可触发(通过路径写入实现不可变更新)
|
|
230
|
+
*
|
|
231
|
+
* // 在 Vapor JSX 中使用(自动 DOM 更新)
|
|
232
|
+
* // <span>{state.user.name}</span>
|
|
233
|
+
* // <input value={state.user.name} onInput={e => state.user.name = e.target.value} />
|
|
234
|
+
*
|
|
235
|
+
* // 只读代理:禁止写入
|
|
236
|
+
* const ro = reactive({ a: 1 }, { readonly: true })
|
|
237
|
+
* // ro.a = 2 // 将被忽略或导致失败(只读)
|
|
238
|
+
*
|
|
239
|
+
* // 浅代理:仅对顶层对象进行代理,子对象不递归代理
|
|
240
|
+
* const sh = reactive({ nested: { a: 1 } }, { shallow: true })
|
|
241
|
+
* // sh.nested 仍为普通对象(非代理)
|
|
242
|
+
*
|
|
243
|
+
* // 原始类型:普通值会自动包裹为 { value } 并返回其代理
|
|
244
|
+
* const num = reactive(0)
|
|
245
|
+
* console.log(num.value) // 0
|
|
246
|
+
* num.value = 1 // 写入 value 字段触发订阅者
|
|
247
|
+
* const str = reactive('A')
|
|
248
|
+
* str.value = 'B' // 原始类型统一通过 value 字段读写
|
|
249
|
+
*
|
|
250
|
+
* // 自定义等值比较:用于控制触发频率
|
|
251
|
+
* const eq = (prev: any, next: any) => _.isEqual(prev, next)
|
|
252
|
+
* const obj = reactive({ a: 1 }, { equals: eq })
|
|
253
|
+
* obj.a = 1 // 不触发(相等)
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
export function reactive<T extends Primitive>(
|
|
257
|
+
initial: T,
|
|
258
|
+
options?: ReactiveOptions<T>,
|
|
259
|
+
forceGlobal?: boolean,
|
|
260
|
+
): { value: Widen<T> }
|
|
261
|
+
export function reactive<T extends object | Function>(
|
|
262
|
+
initial: T,
|
|
263
|
+
options?: ReactiveOptions<T>,
|
|
264
|
+
forceGlobal?: boolean,
|
|
265
|
+
): T
|
|
266
|
+
export function reactive<T = any>(
|
|
267
|
+
initial: T,
|
|
268
|
+
options?: ReactiveOptions<T>,
|
|
269
|
+
forceGlobal?: boolean,
|
|
270
|
+
): any
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 创建 Reactive:返回一个对象/数组的响应式代理(浅)
|
|
274
|
+
*
|
|
275
|
+
* 用法(JavaScript / TypeScript):
|
|
276
|
+
* ```ts
|
|
277
|
+
* // 只读代理:禁止写入
|
|
278
|
+
* const ro = shallowReactive({ a: 1 })
|
|
279
|
+
* // ro.a = 2 // 将被忽略或导致失败(只读)
|
|
280
|
+
*
|
|
281
|
+
* // 浅代理:仅对顶层对象进行代理,子对象不递归代理
|
|
282
|
+
* const sh = shallowReactive({ nested: { a: 1 } })
|
|
283
|
+
* // sh.nested 仍为普通对象(非代理)
|
|
284
|
+
*
|
|
285
|
+
* // 自定义等值比较:用于控制触发频率
|
|
286
|
+
* const eq = (prev: any, next: any) => _.isEqual(prev, next)
|
|
287
|
+
* const obj = shallowReactive({ a: 1 }, { equals: eq })
|
|
288
|
+
* obj.a = 1 // 不触发(相等)
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
export function shallowReactive<T extends object>(
|
|
292
|
+
initial: T,
|
|
293
|
+
options?: { equals?: Equals<T> },
|
|
294
|
+
forceGlobal?: boolean,
|
|
295
|
+
): T
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 创建 Reactive:返回一个对象/数组的响应式代理(深度只读)
|
|
299
|
+
*
|
|
300
|
+
* 用法(JavaScript / TypeScript):
|
|
301
|
+
* ```ts
|
|
302
|
+
* // 只读代理:禁止写入
|
|
303
|
+
* const ro = readonly({ a: 1 })
|
|
304
|
+
* // ro.a = 2 // 将被忽略或导致失败(只读)
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
export function readonly<T extends object>(initial: T, forceGlobal?: boolean): T
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* 创建 Reactive:返回一个对象/数组的响应式代理(第一层只读)
|
|
311
|
+
*
|
|
312
|
+
* 用法(JavaScript / TypeScript):
|
|
313
|
+
* ```ts
|
|
314
|
+
* // 只读代理:禁止写入
|
|
315
|
+
* const ro = readonly({ a: 1, b: {hello: 'world'} })
|
|
316
|
+
* // ro.a = 2 // 将被忽略或导致失败(只读)
|
|
317
|
+
* // ro.hello = 'new world' // 成功
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
320
|
+
export function shallowReadonly<T extends object>(initial: T, forceGlobal?: boolean): T
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* useMemo:根据依赖数组缓存计算结果,等值依赖不重新计算
|
|
326
|
+
*/
|
|
327
|
+
export function useMemo<T>(factory: () => T, deps: any[]): T;
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* useSetup:仅在首次调用时计算一次并缓存
|
|
333
|
+
*/
|
|
334
|
+
export function useSetup<T>(factory: () => T): T;
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* useState 选项
|
|
340
|
+
*/
|
|
341
|
+
export interface UseStateOptions<T = any> {
|
|
342
|
+
equals?: (prev: T, next: T) => boolean
|
|
343
|
+
kind?: 'reactive' | 'ref' | 'signal'
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* useState 钩子:统一的轻量状态容器(支持 reactive/ref/signal 三种形态)
|
|
348
|
+
*
|
|
349
|
+
* 设计概览:
|
|
350
|
+
* - 默认形态为 `reactive`:当初始值为对象/数组时直接返回其响应式代理;当为原始类型时自动包裹为 `{ value }` 并返回其代理。
|
|
351
|
+
* - 可选 `kind`:
|
|
352
|
+
* - `'reactive'`:对象/数组的响应式代理;原始类型将自动包裹为 `{ value }`
|
|
353
|
+
* - `'ref'`:总是返回 `{ value }` 的响应式代理,便于统一读写
|
|
354
|
+
* - `'signal'`:返回底层 `SignalHandle`,适合需要精细控制 get/set/update/path 的场景
|
|
355
|
+
* - 等值比较:通过 `options.equals(prev, next)` 自定义比较逻辑,返回 `true` 表示值相等,不触发订阅者。
|
|
356
|
+
*
|
|
357
|
+
* 使用示例(JavaScript / TypeScript):
|
|
358
|
+
* // reactive(默认)
|
|
359
|
+
* const [state, setState] = useState({ user: { name: 'A' }, items: ['x'] })
|
|
360
|
+
* state.user.name = 'B' // 响应式写入
|
|
361
|
+
* setState({ user: { name: 'C' }, items: ['y'] }) // 整体替换
|
|
362
|
+
* setState(prev => ({ ...prev, user: { ...prev.user, name: 'D' } })) // 基于回调更新
|
|
363
|
+
*
|
|
364
|
+
* // ref(原始类型亦可统一为 { value })
|
|
365
|
+
* const [count, setCount] = useState(0, { kind: 'ref' })
|
|
366
|
+
* console.log(count.value) // 0
|
|
367
|
+
* setCount(1) // 触发订阅者
|
|
368
|
+
* setCount(ref => { ref.value += 1 }) // 2
|
|
369
|
+
*
|
|
370
|
+
* // signal(底层句柄)
|
|
371
|
+
* const [sig, setSig] = useState({ a: 1 }, { kind: 'signal' })
|
|
372
|
+
* console.log(sig.get()) // { a: 1 }
|
|
373
|
+
* sig.set({ a: 2 }) // 触发订阅者
|
|
374
|
+
* setSig(handle => ({ a: handle.peek().a + 1 })) // { a: 3 }
|
|
375
|
+
* sig.setPath('a', 4) // 路径写入
|
|
376
|
+
* console.log(sig.get()) // { a: 4 }
|
|
377
|
+
*
|
|
378
|
+
* // 自定义等值比较(默认使用 shallowEqual)
|
|
379
|
+
* const [state2, setState2] = useState({ a: 1, b: 2 }, { kind: 'reactive', equals: (prev, next) => prev.a === next.a })
|
|
380
|
+
* setState2({ a: 1, b: 3 }) // 不触发订阅者,因为 a 未改变
|
|
381
|
+
* setState2({ a: 2, b: 4 }) // 触发订阅者,因为 a 改变
|
|
382
|
+
*/
|
|
383
|
+
export function useState<T extends Primitive>(
|
|
384
|
+
initial: T | (() => T),
|
|
385
|
+
options?: UseStateOptions<T> & { kind?: 'reactive' | 'ref' },
|
|
386
|
+
): [{ value: Widen<T> }, (v: Widen<T> | ((ref: { value: Widen<T> }) => Widen<T> | void)) => void]
|
|
387
|
+
export function useState<T extends Primitive>(
|
|
388
|
+
initial: T | (() => T),
|
|
389
|
+
options: UseStateOptions<T> & { kind: 'signal' },
|
|
390
|
+
): [SignalHandle<Widen<T>>, (v: Widen<T> | ((sig: SignalHandle<Widen<T>>) => Widen<T> | void)) => void]
|
|
391
|
+
export function useState<T extends object | Function>(
|
|
392
|
+
initial: T | (() => T),
|
|
393
|
+
options: UseStateOptions<T> & { kind: 'reactive' },
|
|
394
|
+
): [T, (v: T | ((state: T) => T | void)) => void]
|
|
395
|
+
export function useState<T extends object | Function>(
|
|
396
|
+
initial: T | (() => T),
|
|
397
|
+
options?: UseStateOptions<T> & { kind?: 'reactive' },
|
|
398
|
+
): [T, (v: T | ((state: T) => T | void)) => void]
|
|
399
|
+
export function useState<T extends object | Function>(
|
|
400
|
+
initial: T | (() => T),
|
|
401
|
+
options: UseStateOptions<T> & { kind: 'ref' },
|
|
402
|
+
): [{ value: T }, (v: T | ((ref: { value: T }) => T | void)) => void]
|
|
403
|
+
export function useState<T extends object | Function>(
|
|
404
|
+
initial: T | (() => T),
|
|
405
|
+
options: UseStateOptions<T> & { kind: 'signal' },
|
|
406
|
+
): [SignalHandle<T>, (v: T | ((sig: SignalHandle<T>) => T | void)) => void]
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* 设置调度模式
|
|
412
|
+
* - mode="sync":信号变更时尽可能同步触发副作用
|
|
413
|
+
* - 其他值:采用默认微任务合并调度
|
|
414
|
+
* 示例(JavaScript):
|
|
415
|
+
* ```javascript
|
|
416
|
+
* const { setReactiveScheduling } = wasmModule;
|
|
417
|
+
* setReactiveScheduling('sync'); // 适合少量、快速的更新
|
|
418
|
+
* // setReactiveScheduling('microtask'); // 使用默认微任务合并(传非 "sync" 即可)
|
|
419
|
+
* ```
|
|
420
|
+
*/
|
|
421
|
+
export function setReactiveScheduling(mode: 'sync' | string): void;
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* 通用取值转换
|
|
425
|
+
* 支持以下输入:
|
|
426
|
+
* - 函数:调用并返回其结果
|
|
427
|
+
* - 对象:优先读取 `value` 字段,其次尝试调用 `get()` 方法
|
|
428
|
+
* - 其他:直接返回原值
|
|
429
|
+
* 示例(JavaScript):
|
|
430
|
+
* ```javascript
|
|
431
|
+
* const { toValue } = wasmModule;
|
|
432
|
+
* toValue(() => 1); // 1
|
|
433
|
+
* toValue({ value: 2 }); // 2
|
|
434
|
+
* toValue({ get() { return 3; } }); // 3
|
|
435
|
+
* toValue('hello'); // 'hello'
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
export function toValue<T>(x: T | (() => T) | { value?: T; get?: () => T }): T;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* 原始类型:普通值会自动包裹为 { value } 并返回其代理
|
|
442
|
+
*/
|
|
443
|
+
type Primitive = string | number | bigint | boolean | symbol | null | undefined
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* 扩展原始类型:将原始值包裹为 { value: T } 并返回其代理
|
|
447
|
+
*/
|
|
448
|
+
type Widen<T> = T extends string
|
|
449
|
+
? string
|
|
450
|
+
: T extends number
|
|
451
|
+
? number
|
|
452
|
+
: T extends boolean
|
|
453
|
+
? boolean
|
|
454
|
+
: T extends bigint
|
|
455
|
+
? bigint
|
|
456
|
+
: T extends symbol
|
|
457
|
+
? symbol
|
|
458
|
+
: T extends null | undefined
|
|
459
|
+
? T
|
|
460
|
+
: never
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* 侦听选项:{ immediate?: bool, scheduler?: Function, equals?: Function, debounce?: number }
|
|
466
|
+
*/
|
|
467
|
+
export interface WatchOptions<T = any> {
|
|
468
|
+
immediate?: boolean;
|
|
469
|
+
scheduler?: (run: () => void) => void;
|
|
470
|
+
equals?: (prev: T, next: T) => boolean;
|
|
471
|
+
debounce?: number;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* 侦听副作用:在副作用体内自行读取信号,依赖会自动收集
|
|
476
|
+
* 类似 Vue 的 `watchEffect`
|
|
477
|
+
* 示例(JavaScript):
|
|
478
|
+
* ```javascript
|
|
479
|
+
* const { createSignal, watchEffect } = wasmModule;
|
|
480
|
+
* const s = createSignal(0);
|
|
481
|
+
* watchEffect(() => {
|
|
482
|
+
* console.log('value:', s.get());
|
|
483
|
+
* }, {
|
|
484
|
+
* //可选 scheduler
|
|
485
|
+
* });
|
|
486
|
+
* s.set(1);
|
|
487
|
+
* ```
|
|
488
|
+
*/
|
|
489
|
+
export function watchEffect(
|
|
490
|
+
cb: () => void,
|
|
491
|
+
options?: { scheduler?: (run: () => void) => void } | null,
|
|
492
|
+
): EffectHandle;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* 侦听任意 getter 函数的结果变化
|
|
496
|
+
* options: { immediate?: bool, scheduler?: Function, equals?: Function }
|
|
497
|
+
* 示例(JavaScript):
|
|
498
|
+
* ```javascript
|
|
499
|
+
* const { watchFn } = wasmModule;
|
|
500
|
+
* const obj = { value: 0 };
|
|
501
|
+
* const stop = watchFn(
|
|
502
|
+
* () => obj.value,
|
|
503
|
+
* (newv, oldv) => console.log('changed:', oldv, '->', newv),
|
|
504
|
+
* { immediate: true }
|
|
505
|
+
* );
|
|
506
|
+
* obj.value = 1; // 当 getter 读取到新值时触发 handler
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
export function watchFn<T>(
|
|
510
|
+
getter: () => T,
|
|
511
|
+
handler: (newv: T, oldv: T) => void,
|
|
512
|
+
options?: WatchOptions<T> | null,
|
|
513
|
+
): EffectHandle;
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* 侦听信号值变化:`src` 为信号句柄,`handler` 为值变化时的回调函数
|
|
517
|
+
* options: { immediate?: bool, scheduler?: Function, equals?: Function }
|
|
518
|
+
* 示例(JavaScript):
|
|
519
|
+
* ```javascript
|
|
520
|
+
* const { createSignal, watchSignal } = wasmModule;
|
|
521
|
+
* const s = createSignal(0);
|
|
522
|
+
* watchSignal(s, (newv, oldv) => {
|
|
523
|
+
* console.log('value:', oldv, '->', newv);
|
|
524
|
+
* }, { immediate: true });
|
|
525
|
+
* s.set(1); // 当信号值变化时触发 handler
|
|
526
|
+
* ```
|
|
527
|
+
*/
|
|
528
|
+
export function watchSignal(
|
|
529
|
+
src: SignalHandle,
|
|
530
|
+
handler: (newv: any, oldv: any) => void,
|
|
531
|
+
options?: WatchOptions<any> | null,
|
|
532
|
+
): EffectHandle;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* 侦听信号值变化(深度):`src` 为信号句柄,`handler` 为值变化时的回调函数
|
|
536
|
+
* options: { immediate?: bool, scheduler?: Function, equals?: Function }
|
|
537
|
+
* 示例(JavaScript):
|
|
538
|
+
* ```javascript
|
|
539
|
+
* const { createSignal, watchDeepSignal } = wasmModule;
|
|
540
|
+
* const s = createSignal({ a: 0 });
|
|
541
|
+
* watchDeepSignal(s, (newv, oldv) => {
|
|
542
|
+
* console.log('value:', oldv, '->', newv);
|
|
543
|
+
* }, { immediate: true });
|
|
544
|
+
* s.set({ a: 1 }); // 当信号值变化时触发 handler
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
547
|
+
export function watchDeepSignal(
|
|
548
|
+
src: SignalHandle,
|
|
549
|
+
handler: (newv: any, oldv: any) => void,
|
|
550
|
+
options?: WatchOptions<any> | null,
|
|
551
|
+
): EffectHandle;
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* 侦听信号路径值变化:`src` 为信号句柄,`path` 为路径(数组或点分字符串),`handler` 为值变化时的回调函数
|
|
555
|
+
* options: { immediate?: bool, scheduler?: Function, equals?: Function }
|
|
556
|
+
* 示例(JavaScript):
|
|
557
|
+
* ```javascript
|
|
558
|
+
* const { createSignal, watchPath } = wasmModule;
|
|
559
|
+
* const s = createSignal({ a: { b: 0 } });
|
|
560
|
+
* watchPath(s, ['a','b'], (n, o) => {
|
|
561
|
+
* console.log('b changed:', o, '->', n)
|
|
562
|
+
* }, { immediate: true })
|
|
563
|
+
* s.setPath(['a','b'], 1) // 当路径值变化时触发 handler
|
|
564
|
+
* ```
|
|
565
|
+
*/
|
|
566
|
+
export function watchPath(
|
|
567
|
+
src: SignalHandle,
|
|
568
|
+
path: string | Array<string | number>,
|
|
569
|
+
handler: (newv: any, oldv: any) => void,
|
|
570
|
+
options?: WatchOptions<any> | null,
|
|
571
|
+
): EffectHandle;
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* 侦听来源:信号句柄、getter 函数或来源数组(信号句柄 / getter 函数)
|
|
575
|
+
*/
|
|
576
|
+
export type WatchSource<T = any> = SignalHandle | (() => T) | Array<SignalHandle | (() => any)>;
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* 侦听任意来源变化:`source` 为信号句柄、getter 函数或来源数组(信号句柄 / getter 函数 / 常量),`handler` 为变化时的回调函数
|
|
580
|
+
* options: { immediate?: bool, scheduler?: Function, equals?: Function }
|
|
581
|
+
* 示例(JavaScript):
|
|
582
|
+
* ```javascript
|
|
583
|
+
* const { createSignal, watch } = wasmModule;
|
|
584
|
+
* const s1 = createSignal(0);
|
|
585
|
+
* const s2 = createSignal(10);
|
|
586
|
+
* watch([s1, s2, 'x'], (n, o) => {
|
|
587
|
+
* console.log('values:', o, '->', n);
|
|
588
|
+
* }, { immediate: true });
|
|
589
|
+
* s1.set(1); s2.set(11); // 当来源数组中任意值变化时触发 handler
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
export function watch<T>(
|
|
593
|
+
source: WatchSource<T>,
|
|
594
|
+
handler: (newv: T | any[], oldv: T | any[]) => void,
|
|
595
|
+
options?: WatchOptions<T | any[]> | null,
|
|
596
|
+
): EffectHandle;
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* 副作用句柄:用于在 JS 侧停止副作用
|
|
602
|
+
*/
|
|
603
|
+
export interface EffectHandle {
|
|
604
|
+
/** 停止后续运行,并执行最后一次已注册的清理 */
|
|
605
|
+
dispose(): void;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* 创建副作用(立即运行一次),并返回句柄
|
|
610
|
+
* 示例(JavaScript):
|
|
611
|
+
* ```javascript
|
|
612
|
+
* const { createSignal, createEffect, onCleanup, untrack, batch } = wasmModule;
|
|
613
|
+
* const s = createSignal(0);
|
|
614
|
+
*
|
|
615
|
+
* // 基本副作用:读取信号并打印
|
|
616
|
+
* createEffect(() => {
|
|
617
|
+
* console.log('value:', s.get());
|
|
618
|
+
* });
|
|
619
|
+
* s.set(1);
|
|
620
|
+
*
|
|
621
|
+
* // 清理示例:注册定时器并在下一次运行前清理
|
|
622
|
+
* createEffect(() => {
|
|
623
|
+
* const id = setInterval(() => console.log('tick'), 1000);
|
|
624
|
+
* onCleanup(() => clearInterval(id));
|
|
625
|
+
* });
|
|
626
|
+
*
|
|
627
|
+
* // 断开依赖收集:不会订阅 other 信号
|
|
628
|
+
* createEffect(() => {
|
|
629
|
+
* const v = untrack(() => s.get());
|
|
630
|
+
* console.log('untracked:', v);
|
|
631
|
+
* });
|
|
632
|
+
*
|
|
633
|
+
* 创建副作用(支持自定义调度器与懒执行)
|
|
634
|
+
* options:
|
|
635
|
+
* - scheduler: Function( run: Function ),用于自定义何时调用 run
|
|
636
|
+
* - lazy: bool,true 时不在创建时立即运行
|
|
637
|
+
* 示例(JavaScript):
|
|
638
|
+
* ```javascript
|
|
639
|
+
* const { createEffect } = wasmModule;
|
|
640
|
+
* const scheduler = (run) => queueMicrotask(run); // 或者使用 requestAnimationFrame
|
|
641
|
+
* createEffect(() => {
|
|
642
|
+
* // ...effect body
|
|
643
|
+
* }, { scheduler, lazy: false });
|
|
644
|
+
* ```
|
|
645
|
+
*/
|
|
646
|
+
export function createEffect(
|
|
647
|
+
cb: () => void,
|
|
648
|
+
options?: { scheduler?: (run: () => void) => void; lazy?: boolean } | null,
|
|
649
|
+
): EffectHandle;
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* 在当前运行的副作用上注册清理函数
|
|
653
|
+
* 清理函数会在下一次该副作用执行之前被调用
|
|
654
|
+
* 用法与示例(JavaScript):
|
|
655
|
+
* ```js
|
|
656
|
+
* // 需求:每次依赖变化时重建一个定时器,并在下一次运行前清理上一次的定时器
|
|
657
|
+
* const s = createSignal(0)
|
|
658
|
+
* const eh = createEffect(() => {
|
|
659
|
+
* const v = s.get() // 建立订阅
|
|
660
|
+
* const id = setInterval(() => console.log('[onCleanup-demo] tick v=', v), 500)
|
|
661
|
+
* onCleanup(() => clearInterval(id)) // 下次运行前清理上一次的定时器
|
|
662
|
+
* })
|
|
663
|
+
* s.set(1) // 触发重跑:先清理旧定时器,再创建新定时器(只保留一个)
|
|
664
|
+
* s.set(2) // 同理
|
|
665
|
+
* eh.dispose() // 停止后续运行,并执行最后一次已注册的清理(定时器不再 tick)
|
|
666
|
+
* ```
|
|
667
|
+
*/
|
|
668
|
+
export function onCleanup(cb: () => void): void;
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* 断开依赖收集地执行回调(不记录对 Signal 的订阅)
|
|
672
|
+
*
|
|
673
|
+
* 用途与示例(JavaScript):
|
|
674
|
+
* - 在同一个副作用中,订阅主依赖 `a`,但临时读取 `b` 并且不希望 `b` 的变化触发该副作用。
|
|
675
|
+
* - `untrack(fn)` 会在执行 `fn` 时临时关闭依赖收集,`fn` 内部对信号的读取不会把当前副作用订阅到这些信号上。
|
|
676
|
+
*
|
|
677
|
+
* ```js
|
|
678
|
+
* const a = createSignal(0)
|
|
679
|
+
* const b = createSignal(0)
|
|
680
|
+
* createEffect(() => {
|
|
681
|
+
* const av = a.get() // 订阅 a:a 变化会使该副作用重跑
|
|
682
|
+
* const bv = untrack(() => b.get()) // 断开依赖读取 b:b 变化不会触发该副作用
|
|
683
|
+
* console.log('[untrack-demo] run av=', av, 'bv=', bv)
|
|
684
|
+
* })
|
|
685
|
+
* b.set(1) // 不触发副作用(因为对 b 的读取发生在 untrack 内)
|
|
686
|
+
* b.set(2) // 不触发副作用
|
|
687
|
+
* a.set(10) // 触发副作用(因为对 a 进行了正常订阅)
|
|
688
|
+
* ```
|
|
689
|
+
*
|
|
690
|
+
* 说明:若仅需“不订阅地读取”某个信号,也可使用 `peek()`(例如 `const v = sig.peek()`)。
|
|
691
|
+
*/
|
|
692
|
+
export function untrack<T>(cb: () => T): T;
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* 批量更新:在回调期间抑制副作用的即时触发,统一在结束后刷新
|
|
696
|
+
*
|
|
697
|
+
* 用法与示例(JavaScript):
|
|
698
|
+
* ```js
|
|
699
|
+
* const { createSignal, createEffect, batch } = wasmModule
|
|
700
|
+
*
|
|
701
|
+
* // 示例一:同一信号多次 set,仅在批量结束后重跑一次
|
|
702
|
+
* const count = createSignal(0)
|
|
703
|
+
* createEffect(() => {
|
|
704
|
+
* console.log('[batch-demo] count =', count.get())
|
|
705
|
+
* })
|
|
706
|
+
* batch(() => {
|
|
707
|
+
* count.set(1)
|
|
708
|
+
* count.set(2)
|
|
709
|
+
* count.set(3)
|
|
710
|
+
* // 在批量范围内不会立即触发副作用
|
|
711
|
+
* })
|
|
712
|
+
* // 批量结束后只触发一次副作用,打印最新值 3
|
|
713
|
+
*
|
|
714
|
+
* // 示例二:一次性更新多个信号,副作用只合并重跑一次
|
|
715
|
+
* const a = createSignal(0)
|
|
716
|
+
* const b = createSignal(0)
|
|
717
|
+
* createEffect(() => {
|
|
718
|
+
* console.log('[batch-demo] sum =', a.get() + b.get())
|
|
719
|
+
* })
|
|
720
|
+
* batch(() => {
|
|
721
|
+
* a.set(10)
|
|
722
|
+
* b.set(20)
|
|
723
|
+
* }) // 仅一次重跑,输出 sum = 30
|
|
724
|
+
* ```
|
|
725
|
+
*/
|
|
726
|
+
export function batch(cb: () => void): void;
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* 自定义等值比较函数类型:`(prev: T, next: T) => boolean`
|
|
732
|
+
*/
|
|
733
|
+
export type Equals<T> = (prev: T, next: T) => boolean;
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* 响应式选项:用于创建响应式对象/数组
|
|
737
|
+
*/
|
|
738
|
+
export interface ReactiveOptions<T> {
|
|
739
|
+
equals?: Equals<T>
|
|
740
|
+
readonly?: boolean
|
|
741
|
+
shallow?: boolean
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* 信号句柄:包含
|
|
746
|
+
* `{ get, peek, set, update, getPath, setPath, updatePath, peekPath, toJSON, valueOf, toString, value }` 方法/属性
|
|
747
|
+
*/
|
|
748
|
+
export interface SignalHandle<T> {
|
|
749
|
+
/**
|
|
750
|
+
* 读取当前值,并在有正在运行的副作用时将其订阅到该信号
|
|
751
|
+
*/
|
|
752
|
+
get(): T
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* 查看当前值:不进行依赖收集(不订阅当前副作用)
|
|
756
|
+
*/
|
|
757
|
+
peek(): T
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* 查看路径值:不进行依赖收集(不订阅当前副作用)
|
|
761
|
+
*
|
|
762
|
+
* 用法(JavaScript):
|
|
763
|
+
* ```js
|
|
764
|
+
* const s = signal({ user: { name: 'A' }, items: ['x'] })
|
|
765
|
+
* const n = s.peekPath('user.name') // 'A'(不订阅)
|
|
766
|
+
*
|
|
767
|
+
* // 边界:
|
|
768
|
+
* // - 空路径:返回根值(不订阅)
|
|
769
|
+
* console.log(s.peekPath([])) // { user: { name: 'A' }, items: ['x'] }
|
|
770
|
+
* console.log(s.peekPath('')) // { user: { name: 'A' }, items: ['x'] }
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
peekPath(path: string | Array<string | number>): any
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* 设置新值,按等值比较决定是否通知订阅者
|
|
777
|
+
*/
|
|
778
|
+
set(value: T): void
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* 根据回调对当前值进行计算并设置(相当于 set( updater(current) ))
|
|
782
|
+
*/
|
|
783
|
+
update(updater: (current: T) => T): void
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* JSON 序列化:`JSON.stringify(signal)` 时返回内部值
|
|
787
|
+
*/
|
|
788
|
+
toJSON(): T
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* 原始值表示:`signal.valueOf()` 返回内部值
|
|
792
|
+
*/
|
|
793
|
+
valueOf(): T
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* 字符串表示:`String(signal)` 返回 JSON 字符串或占位文本
|
|
797
|
+
*/
|
|
798
|
+
toString(): string
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* 只读/写属性:`signal.value` 直接读取内部值;写入等价于 `set`
|
|
802
|
+
*
|
|
803
|
+
* 用法(JavaScript):
|
|
804
|
+
* ```js
|
|
805
|
+
* const s = signal(0)
|
|
806
|
+
* console.log(s.value) // 0(不订阅)
|
|
807
|
+
* s.value = 1 // 等价于 s.set(1)
|
|
808
|
+
* ```
|
|
809
|
+
*/
|
|
810
|
+
value: T
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* 根据路径读取当前对象/数组的子项值(依赖收集)
|
|
814
|
+
*
|
|
815
|
+
* 用法(JavaScript):
|
|
816
|
+
* ```js
|
|
817
|
+
* const s = signal({ user: { profile: { name: 'A' } }, items: ['x'] })
|
|
818
|
+
* const name1 = s.getPath(['user','profile','name']) // 'A'
|
|
819
|
+
* const name2 = s.getPath('user.profile.name') // 'A'
|
|
820
|
+
* // 在 watch 中使用路径读取会自动依赖收集
|
|
821
|
+
* watchFn(() => s.getPath(['user','profile','name']), (n, o) => { })
|
|
822
|
+
* watchFn(() => s.getPath('user.profile.name'), (n, o) => { })
|
|
823
|
+
*
|
|
824
|
+
* // 边界:
|
|
825
|
+
* // - 空路径:返回根值并订阅
|
|
826
|
+
* const root = s.getPath([]) // 订阅根
|
|
827
|
+
* // - 数字字段解析:'items.0' 解析为 ['items', 0]
|
|
828
|
+
* const first = s.getPath('items.0') // 'x'
|
|
829
|
+
* ```
|
|
830
|
+
*/
|
|
831
|
+
getPath(path: string | Array<string | number>): any
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* 根据路径设置对象/数组的子项值:不可变更新,生成新的根对象并触发订阅者
|
|
835
|
+
*
|
|
836
|
+
* 用法(JavaScript):
|
|
837
|
+
* ```js
|
|
838
|
+
* const s = signal({ user: { profile: { name: 'A' }, age: 20 }, items: ['x'] })
|
|
839
|
+
* // 设置嵌套对象字段(数组路径)
|
|
840
|
+
* s.setPath(['user', 'profile', 'name'], 'B')
|
|
841
|
+
* // 设置数组元素(数组路径)
|
|
842
|
+
* s.setPath(['items', 0], 'y')
|
|
843
|
+
* // 字符串路径(以 . 分隔,数字段转为数组索引)
|
|
844
|
+
* s.setPath('user.profile.name', 'B')
|
|
845
|
+
* s.setPath('items.0', 'y')
|
|
846
|
+
* // 若根不是对象/数组,会以空对象作为根进行赋值
|
|
847
|
+
* s.setPath(['foo', 'bar'], 1)
|
|
848
|
+
* s.setPath('foo.bar', 1)
|
|
849
|
+
*
|
|
850
|
+
* // 边界:
|
|
851
|
+
* // - 空路径:替换整个根值并触发订阅者
|
|
852
|
+
* s.setPath([], { user: { profile: { name: 'C' } } })
|
|
853
|
+
* s.setPath('', { user: { profile: { name: 'D' } } })
|
|
854
|
+
* // - 缺失中间段:自动以空对象占位
|
|
855
|
+
* s.setPath(['config','theme','color'], 'red') // 若 'config' 不存在,将创建为 {}
|
|
856
|
+
* // - 数组越界写入:当目标段为现有数组时会扩展长度
|
|
857
|
+
* s.setPath(['items', 2], 'z') // items: ['x', undefined, 'z']
|
|
858
|
+
* // - 函数值不执行:作为普通值读写
|
|
859
|
+
* const fn = () => 42
|
|
860
|
+
* s.setPath('cb', fn)
|
|
861
|
+
* console.log(typeof s.getPath('cb')) // 'function'
|
|
862
|
+
* ```
|
|
863
|
+
*
|
|
864
|
+
* 参数:
|
|
865
|
+
* - `path`: JS 数组或以 `.` 分隔的字符串(如 `['user','profile','name']`、`['items', 0]` 或 `'user.profile.name'`、`'items.0'`)
|
|
866
|
+
* - `value`: 目标路径的新值
|
|
867
|
+
* 行为与边界:
|
|
868
|
+
* - 路径缺失会自动以空对象占位,数组越界写入会扩展长度
|
|
869
|
+
* - 根为非对象/数组时以空对象为根进行赋值
|
|
870
|
+
* - 函数值作为普通值读写,不会被执行
|
|
871
|
+
*/
|
|
872
|
+
setPath(path: string | Array<string | number>, value: any): void
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* 根据路径函数式更新子项:`updater(currentAtPath) -> nextAtPath`
|
|
876
|
+
*
|
|
877
|
+
* 用法(JavaScript):
|
|
878
|
+
* ```js
|
|
879
|
+
* const s = signal({ user: { age: 20 }, items: ['x'] })
|
|
880
|
+
* s.updatePath(['user','age'], prev => prev + 1) // age: 21
|
|
881
|
+
* s.updatePath(['user','name'], prev => (prev || '').toUpperCase())
|
|
882
|
+
* // 使用字符串路径
|
|
883
|
+
* s.updatePath('user.age', prev => prev + 1) // age: 21
|
|
884
|
+
* s.updatePath('items.0', prev => (prev || '').toUpperCase())
|
|
885
|
+
*
|
|
886
|
+
* // 边界:
|
|
887
|
+
* // - 空路径:以根值为输入进行整体更新
|
|
888
|
+
* s.updatePath([], root => ({ ...root, flag: true }))
|
|
889
|
+
* s.updatePath('', root => ({ ...root, mark: 1 }))
|
|
890
|
+
* // - 缺失段:以空对象占位后再写入
|
|
891
|
+
* s.updatePath(['config','theme'], prev => ({ ...(prev || {}), mode: 'dark' }))
|
|
892
|
+
* ```
|
|
893
|
+
*/
|
|
894
|
+
updatePath(
|
|
895
|
+
path: string | Array<string | number>,
|
|
896
|
+
updater: (currentAtPath: any) => any,
|
|
897
|
+
): void
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* 创建信号:`createSignal(initialValue, { equals?: Equals<T> })`
|
|
902
|
+
*
|
|
903
|
+
* 参数:
|
|
904
|
+
* - `initialValue`: 初始值
|
|
905
|
+
* - `options.equals`: 自定义等值比较函数,默认使用 `===`
|
|
906
|
+
*
|
|
907
|
+
* 示例(JavaScript):
|
|
908
|
+
* ```javascript
|
|
909
|
+
* const s = createSignal(0)
|
|
910
|
+
* s.set(1) // 通知订阅者
|
|
911
|
+
* s.set(1) // 不通知订阅者(因为 === 相等)
|
|
912
|
+
*
|
|
913
|
+
* const s2 = createSignal({ a: 0 }, { equals: (prev, next) => JSON.stringify(prev) === JSON.stringify(next) })
|
|
914
|
+
* s2.set({ a: 0 }) // 通知订阅者
|
|
915
|
+
* s2.set({ a: 0 }) // 不通知订阅者(因为 JSON.stringify 相等)
|
|
916
|
+
* ```
|
|
917
|
+
*/
|
|
918
|
+
export function createSignal<T = any>(
|
|
919
|
+
initial: T,
|
|
920
|
+
options?: { equals?: Equals<T> },
|
|
921
|
+
): SignalHandle<T>;
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* 创建引用信号:`createRef(initialValue)`
|
|
925
|
+
*
|
|
926
|
+
* 参数:
|
|
927
|
+
* - `initialValue`: 初始值
|
|
928
|
+
*
|
|
929
|
+
* 示例(JavaScript):
|
|
930
|
+
* ```javascript
|
|
931
|
+
* const ref = createRef(0)
|
|
932
|
+
* ref.set(1) // 通知订阅者
|
|
933
|
+
* ref.set(1) // 不通知订阅者(因为 === 相等)
|
|
934
|
+
*
|
|
935
|
+
* // 属性与调试方法
|
|
936
|
+
* console.log(ref.value) // 0(不订阅)
|
|
937
|
+
* ref.value = 2 // 等价于 ref.set(2)
|
|
938
|
+
* console.log(String(ref)) // 调用 toString()
|
|
939
|
+
* console.log(ref.valueOf()) // 调用 valueOf()
|
|
940
|
+
* console.log(JSON.stringify(ref)) // 调用 toJSON()
|
|
941
|
+
* ```
|
|
942
|
+
*/
|
|
943
|
+
export function createRef<T = any>(initial: T): SignalHandle<T>;
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* 创建 Reactive:返回一个对象/数组的响应式代理(深/浅、只读可选)
|
|
947
|
+
*
|
|
948
|
+
* 用法(JavaScript / TypeScript):
|
|
949
|
+
* ```ts
|
|
950
|
+
* // 基础对象:读取与写入都响应式
|
|
951
|
+
* const state = createReactive({ user: { name: 'A' }, items: ['x'] })
|
|
952
|
+
* console.log(state.user.name) // 'A'
|
|
953
|
+
* state.user.name = 'B' // 写入嵌套字段,触发订阅者
|
|
954
|
+
* state.items.push('y') // 数组写入也可触发(通过路径写入实现不可变更新)
|
|
955
|
+
*
|
|
956
|
+
* // 在 Vapor JSX 中使用(自动 DOM 更新)
|
|
957
|
+
* // <span>{state.user.name}</span>
|
|
958
|
+
* // <input value={state.user.name} onInput={e => state.user.name = e.target.value} />
|
|
959
|
+
*
|
|
960
|
+
* // 只读代理:禁止写入
|
|
961
|
+
* const ro = createReactive({ a: 1 }, { readonly: true })
|
|
962
|
+
* // ro.a = 2 // 将被忽略或导致失败(只读)
|
|
963
|
+
*
|
|
964
|
+
* // 浅代理:仅对顶层对象进行代理,子对象不递归代理
|
|
965
|
+
* const sh = createReactive({ nested: { a: 1 } }, { shallow: true })
|
|
966
|
+
* // sh.nested 仍为普通对象(非代理)
|
|
967
|
+
*
|
|
968
|
+
* // 原始类型:普通值会自动包裹为 { value } 并返回其代理
|
|
969
|
+
* const num = createReactive(0)
|
|
970
|
+
* console.log(num.value) // 0
|
|
971
|
+
* num.value = 1 // 写入 value 字段触发订阅者
|
|
972
|
+
* const str = createReactive('A')
|
|
973
|
+
* str.value = 'B' // 原始类型统一通过 value 字段读写
|
|
974
|
+
*
|
|
975
|
+
* // 自定义等值比较:用于控制触发频率
|
|
976
|
+
* const eq = (prev: any, next: any) => _.isEqual(prev, next)
|
|
977
|
+
* const obj = createReactive({ a: 1 }, { equals: eq })
|
|
978
|
+
* obj.a = 1 // 不触发(相等)
|
|
979
|
+
* ```
|
|
980
|
+
*/
|
|
981
|
+
export function createReactive<T = any>(
|
|
982
|
+
initial: T,
|
|
983
|
+
options?: ReactiveOptions<T>,
|
|
984
|
+
): never;
|
|
985
|
+
export function createReactive<T extends Primitive>(
|
|
986
|
+
initial: T,
|
|
987
|
+
options?: ReactiveOptions<T>,
|
|
988
|
+
): { value: Widen<T> };
|
|
989
|
+
export function createReactive<T extends object | Function>(
|
|
990
|
+
initial: T,
|
|
991
|
+
options?: ReactiveOptions<T>,
|
|
992
|
+
): T;
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* 组件实例上下文,用于响应式访问 Hook 插槽
|
|
998
|
+
*/
|
|
999
|
+
export interface HookContainer {
|
|
1000
|
+
states: any[];
|
|
1001
|
+
index: number;
|
|
1002
|
+
__forcedIndex?: number;
|
|
1003
|
+
[key: string]: any;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* 组件实例上下文类型,可用于类型注解
|
|
1008
|
+
*/
|
|
1009
|
+
export type HookHost =
|
|
1010
|
+
| (Record<string, any> & { __hooks?: HookContainer })
|
|
1011
|
+
| null
|
|
1012
|
+
| undefined;
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* 设置当前实例
|
|
1016
|
+
* 传入 `null/undefined` 表示清空;否则记录为 Some(instance)
|
|
1017
|
+
* 示例(JavaScript):
|
|
1018
|
+
* ```javascript
|
|
1019
|
+
* const { setCurrentInstance, getCurrentInstance, withHookSlot, createSignal } = wasmModule;
|
|
1020
|
+
* const inst = { name: 'MyComponent' };
|
|
1021
|
+
* setCurrentInstance(inst);
|
|
1022
|
+
*
|
|
1023
|
+
* // 在实例上分配一个 Hook 插槽,用于存储状态对象
|
|
1024
|
+
* const state = withHookSlot(() => ({ count: createSignal(0) }));
|
|
1025
|
+
* console.log(getCurrentInstance()); // 当前实例或根实例
|
|
1026
|
+
* ```
|
|
1027
|
+
*/
|
|
1028
|
+
export function setCurrentInstance(instance: HookHost): void;
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* 获取当前实例;若未设置则返回空值(null/undefined),不再构造默认根实例
|
|
1032
|
+
* 用法(JavaScript):
|
|
1033
|
+
* ```javascript
|
|
1034
|
+
* const { setCurrentInstance, getCurrentInstance, withHookSlot } = wasmModule;
|
|
1035
|
+
*
|
|
1036
|
+
* // 设置并获取当前实例
|
|
1037
|
+
* setCurrentInstance({ name: 'A' });
|
|
1038
|
+
* const inst = getCurrentInstance(); // { name: 'A', ... }
|
|
1039
|
+
*
|
|
1040
|
+
* // 清空当前实例后,获取为空(undefined 或 null)
|
|
1041
|
+
* setCurrentInstance(undefined);
|
|
1042
|
+
* const none = getCurrentInstance(); // undefined(或 null)
|
|
1043
|
+
*
|
|
1044
|
+
* // 在有当前实例时,withHookSlot 为该实例分配/复用一个 Hook 插槽
|
|
1045
|
+
* setCurrentInstance({});
|
|
1046
|
+
* const state = withHookSlot(() => ({ count: 0 })); // 首次创建并缓存到 __hooks.states
|
|
1047
|
+
* ```
|
|
1048
|
+
*/
|
|
1049
|
+
export function getCurrentInstance(): HookHost;
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* 在当前实例上为 Hook 分配/复用一个插槽
|
|
1053
|
+
* - 若无当前实例,则直接执行 factory 返回对象
|
|
1054
|
+
* - 有实例时,依据 `__hooks.index` 或 `__forcedIndex` 计算插槽序号
|
|
1055
|
+
* 示例(JavaScript):
|
|
1056
|
+
* ```javascript
|
|
1057
|
+
* const { setCurrentInstance, withHookSlot } = wasmModule;
|
|
1058
|
+
* setCurrentInstance({});
|
|
1059
|
+
* const a = withHookSlot(() => ({ id: 1 }));
|
|
1060
|
+
* const b = withHookSlot(() => ({ id: 2 }));
|
|
1061
|
+
* // 若再次调用 withHookSlot(且未强制索引),将分配下一个插槽
|
|
1062
|
+
* ```
|
|
1063
|
+
*/
|
|
1064
|
+
export function withHookSlot<T = any>(factory: () => T): T;
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* 通过唯一 id 为 Hook 分配稳定插槽索引并运行回调
|
|
1068
|
+
* - 若该 id 尚未存在,则以当前 `states.length` 作为新索引
|
|
1069
|
+
* - 运行期间通过设置 `__forcedIndex` 确保 `withHookSlot` 使用该索引
|
|
1070
|
+
* - 运行结束后自动清理 `__forcedIndex`
|
|
1071
|
+
*/
|
|
1072
|
+
export function vaporWithHookId<T = any>(id: string, runner: () => T): T;
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* 只读信号句柄:仅提供 get/peek 方法,不支持 set/update
|
|
1078
|
+
*/
|
|
1079
|
+
export interface ReadonlySignal<T> { get(): T; peek(): T }
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* 可写信号句柄:提供 get/peek/set/update 方法
|
|
1083
|
+
*/
|
|
1084
|
+
export interface WritableSignal<T> extends ReadonlySignal<T> {
|
|
1085
|
+
set(value: T): void;
|
|
1086
|
+
update?(updater: (current: T) => T): void;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* 计算属性 getter 函数类型:`() => any`
|
|
1091
|
+
*/
|
|
1092
|
+
export type ComputedGetter<T> = () => T;
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* 计算属性 setter 函数类型:`(value: T) => void`
|
|
1096
|
+
*/
|
|
1097
|
+
export type ComputedSetter<T> = (value: T) => void;
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* 计算属性选项:{ get: ComputedGetter<T>, set?: ComputedSetter<T> }
|
|
1101
|
+
*/
|
|
1102
|
+
export interface ComputedOptions<T> { get: ComputedGetter<T>; set?: ComputedSetter<T> }
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* 创建计算属性
|
|
1106
|
+
* - 参数:可为函数 `() => any`,或对象 `{ get: () => any }`
|
|
1107
|
+
* 返回:一个只读信号句柄(通过 get/peek 读取,内部通过 effect 驱动更新)
|
|
1108
|
+
* 示例(JavaScript):
|
|
1109
|
+
* ```javascript
|
|
1110
|
+
* const { createSignal, createComputed, createEffect } = wasmModule;
|
|
1111
|
+
* const count = createSignal(1);
|
|
1112
|
+
* const double = createComputed(() => count.get() * 2);
|
|
1113
|
+
*
|
|
1114
|
+
* createEffect(() => {
|
|
1115
|
+
* console.log('double =', double.get());
|
|
1116
|
+
* });
|
|
1117
|
+
*
|
|
1118
|
+
* count.set(2); // double 将变为 4 并触发订阅者
|
|
1119
|
+
* ```
|
|
1120
|
+
* 更多用法示例:
|
|
1121
|
+
*
|
|
1122
|
+
* 1) 对象参数(只读 getter):
|
|
1123
|
+
* ```javascript
|
|
1124
|
+
* const { createSignal, createComputed, createEffect } = wasmModule;
|
|
1125
|
+
* const first = createSignal('John');
|
|
1126
|
+
* const last = createSignal('Doe');
|
|
1127
|
+
* const fullName = createComputed({
|
|
1128
|
+
* get: () => first.get() + ' ' + last.get()
|
|
1129
|
+
* });
|
|
1130
|
+
* createEffect(() => {
|
|
1131
|
+
* console.log('fullName =', fullName.get()); // John Doe
|
|
1132
|
+
* });
|
|
1133
|
+
* ```
|
|
1134
|
+
*
|
|
1135
|
+
* 2) 通过更新源信号实现“可写效果”(模拟 Vue3 的 setter 行为):
|
|
1136
|
+
* ```javascript
|
|
1137
|
+
* const setFullName = (nv) => {
|
|
1138
|
+
* const [f, l] = nv.split(' ');
|
|
1139
|
+
* first.set(f);
|
|
1140
|
+
* last.set(l);
|
|
1141
|
+
* };
|
|
1142
|
+
* setFullName('David Smith'); // fullName 将变为 "David Smith"
|
|
1143
|
+
* ```
|
|
1144
|
+
*
|
|
1145
|
+
* 3) 直接使用 `{ get, set }` 创建“可写 computed”,支持 `.set(value)`:
|
|
1146
|
+
* ```javascript
|
|
1147
|
+
* const fullName = createComputed({
|
|
1148
|
+
* get: () => first.get() + ' ' + last.get(),
|
|
1149
|
+
* set: (nv) => {
|
|
1150
|
+
* const [f, l] = nv.split(' ');
|
|
1151
|
+
* first.set(f);
|
|
1152
|
+
* last.set(l);
|
|
1153
|
+
* }
|
|
1154
|
+
* });
|
|
1155
|
+
* fullName.set('David Smith'); // 将调用你的 set 更新源信号,并重算派生值
|
|
1156
|
+
* ```
|
|
1157
|
+
*/
|
|
1158
|
+
export function createComputed<T>(get: ComputedGetter<T>): ReadonlySignal<T>;
|
|
1159
|
+
export function createComputed<T>(options: ComputedOptions<T>): WritableSignal<T>;
|
|
1160
|
+
export function createComputed<T>(arg: ComputedGetter<T> | ComputedOptions<T>): ReadonlySignal<T> | WritableSignal<T>;
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* 资源句柄:包含 `{ data, error, loading }` 三个信号
|
|
1166
|
+
*/
|
|
1167
|
+
export interface Resource<TData = any> {
|
|
1168
|
+
data: SignalHandle;
|
|
1169
|
+
error: SignalHandle;
|
|
1170
|
+
loading: SignalHandle;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* 创建资源:根据 `src` 信号的值调用 `fetcher`(返回 Promise),
|
|
1175
|
+
* 并提供 `{ data, error, loading }` 三个信号以反映异步状态。
|
|
1176
|
+
* 示例(JavaScript):
|
|
1177
|
+
* ```javascript
|
|
1178
|
+
* const { createSignal, createResource, createEffect } = wasmModule;
|
|
1179
|
+
* const key = createSignal('https://api.example.com/data');
|
|
1180
|
+
* const res = createResource(key, async (k) => {
|
|
1181
|
+
* const r = await fetch(k);
|
|
1182
|
+
* if (!r.ok) throw new Error('network error');
|
|
1183
|
+
* return r.json();
|
|
1184
|
+
* });
|
|
1185
|
+
*
|
|
1186
|
+
* createEffect(() => {
|
|
1187
|
+
* console.log('loading:', res.loading.get());
|
|
1188
|
+
* console.log('data:', res.data.get());
|
|
1189
|
+
* console.log('error:', res.error.get());
|
|
1190
|
+
* });
|
|
1191
|
+
*
|
|
1192
|
+
* key.set('https://api.example.com/data2');
|
|
1193
|
+
* ```
|
|
1194
|
+
*/
|
|
1195
|
+
export function createResource<TSrc, TData>(
|
|
1196
|
+
src: SignalHandle,
|
|
1197
|
+
fetcher: (src: TSrc) => Promise<TData>,
|
|
1198
|
+
): Resource<TData>;
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
export class EffectHandle {
|
|
1203
|
+
private constructor();
|
|
1204
|
+
free(): void;
|
|
1205
|
+
[Symbol.dispose](): void;
|
|
1206
|
+
/**
|
|
1207
|
+
* 销毁副作用:标记为已处置,并清空其清理回调
|
|
1208
|
+
*
|
|
1209
|
+
* 用法(JavaScript):
|
|
1210
|
+
* ```js
|
|
1211
|
+
* const eh = createEffect(() => {
|
|
1212
|
+
* // ...effect body
|
|
1213
|
+
* onCleanup(() => {
|
|
1214
|
+
* // 这里注册的清理会在下一次执行前或 dispose() 时运行
|
|
1215
|
+
* })
|
|
1216
|
+
* })
|
|
1217
|
+
* // 需要停止该副作用时:
|
|
1218
|
+
* eh.dispose() // 标记为已处置,并执行最后一次已注册的清理
|
|
1219
|
+
* ```
|
|
1220
|
+
*/
|
|
1221
|
+
dispose(): void;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
export class SignalHandle {
|
|
1225
|
+
private constructor();
|
|
1226
|
+
free(): void;
|
|
1227
|
+
[Symbol.dispose](): void;
|
|
1228
|
+
/**
|
|
1229
|
+
* 根据路径读取当前对象/数组的子项值(依赖收集)
|
|
1230
|
+
*
|
|
1231
|
+
* 用法(JavaScript):
|
|
1232
|
+
* ```js
|
|
1233
|
+
* const s = createSignal({ user: { profile: { name: 'A' } } })
|
|
1234
|
+
* const name1 = s.getPath(['user','profile','name']) // 'A'
|
|
1235
|
+
* const name2 = s.getPath('user.profile.name') // 'A'
|
|
1236
|
+
* // 在 watch 中使用路径读取会自动依赖收集
|
|
1237
|
+
* watchFn(() => s.getPath(['user','profile','name']), (n, o) => { })
|
|
1238
|
+
* watchFn(() => s.getPath('user.profile.name'), (n, o) => { })
|
|
1239
|
+
* ```
|
|
1240
|
+
*/
|
|
1241
|
+
getPath(path: any): any;
|
|
1242
|
+
/**
|
|
1243
|
+
* 根据路径设置对象/数组的子项值:不可变更新,生成新的根对象并触发订阅者
|
|
1244
|
+
*
|
|
1245
|
+
* 用法(JavaScript):
|
|
1246
|
+
* ```js
|
|
1247
|
+
* const s = createSignal({ user: { profile: { name: 'A' }, age: 20 }, items: ['x'] })
|
|
1248
|
+
* // 设置嵌套对象字段(数组路径)
|
|
1249
|
+
* s.setPath(['user', 'profile', 'name'], 'B')
|
|
1250
|
+
* // 设置数组元素(数组路径)
|
|
1251
|
+
* s.setPath(['items', 0], 'y')
|
|
1252
|
+
* // 字符串路径(以 . 分隔,数字段转为数组索引)
|
|
1253
|
+
* s.setPath('user.profile.name', 'B')
|
|
1254
|
+
* s.setPath('items.0', 'y')
|
|
1255
|
+
* // 若根不是对象/数组,会以空对象作为根进行赋值
|
|
1256
|
+
* s.setPath(['foo', 'bar'], 1)
|
|
1257
|
+
* s.setPath('foo.bar', 1)
|
|
1258
|
+
* ```
|
|
1259
|
+
*
|
|
1260
|
+
* 参数:
|
|
1261
|
+
* - `path`: JS 数组或以 `.` 分隔的字符串(如 `['user','profile','name']`、`['items', 0]` 或 `'user.profile.name'`、`'items.0'`)
|
|
1262
|
+
* - `value`: 目标路径的新值
|
|
1263
|
+
* 行为与边界:
|
|
1264
|
+
* - 路径缺失会自动以空对象占位,数组越界写入会扩展长度
|
|
1265
|
+
* - 根为非对象/数组时以空对象为根进行赋值
|
|
1266
|
+
* - 函数值作为普通值读写,不会被执行
|
|
1267
|
+
*/
|
|
1268
|
+
setPath(path: any, value: any): void;
|
|
1269
|
+
/**
|
|
1270
|
+
* 便于调试:`signal.valueOf()` 返回内部值
|
|
1271
|
+
*/
|
|
1272
|
+
valueOf(): any;
|
|
1273
|
+
/**
|
|
1274
|
+
* 路径偷看:不进行依赖收集(不订阅当前副作用),仅返回当前路径的值
|
|
1275
|
+
* - 支持以 `.` 分隔的字符串路径(如 `user.profile.name`)或数组路径(如 `['user','profile','name']`、`['items', 0]`)
|
|
1276
|
+
* - 行为与 `getPath` 相同,但不会触发依赖收集,适合用于调试/序列化
|
|
1277
|
+
*/
|
|
1278
|
+
peekPath(path: any): any;
|
|
1279
|
+
/**
|
|
1280
|
+
* 便于调试:`String(signal)` 返回值的 JSON 字符串或占位文本
|
|
1281
|
+
*/
|
|
1282
|
+
toString(): string;
|
|
1283
|
+
/**
|
|
1284
|
+
* 根据路径函数式更新子项:`updater(currentAtPath) -> nextAtPath`
|
|
1285
|
+
*
|
|
1286
|
+
* 用法(JavaScript):
|
|
1287
|
+
* ```js
|
|
1288
|
+
* const s = createSignal({ user: { age: 20 }, items: ['x'] })
|
|
1289
|
+
* s.updatePath(['user','age'], prev => prev + 1) // age: 21
|
|
1290
|
+
* s.updatePath(['user','name'], prev => (prev || '').toUpperCase())
|
|
1291
|
+
* // 使用字符串路径
|
|
1292
|
+
* s.updatePath('user.age', prev => prev + 1) // age: 21
|
|
1293
|
+
* s.updatePath('items.0', prev => (prev || '').toUpperCase())
|
|
1294
|
+
* ```
|
|
1295
|
+
*/
|
|
1296
|
+
updatePath(path: any, updater: Function): void;
|
|
1297
|
+
/**
|
|
1298
|
+
* 读取当前值,并在有正在运行的副作用时将其订阅到该信号
|
|
1299
|
+
*/
|
|
1300
|
+
get(): any;
|
|
1301
|
+
/**
|
|
1302
|
+
* 设置新值,按等值比较决定是否通知订阅者
|
|
1303
|
+
*/
|
|
1304
|
+
set(v: any): void;
|
|
1305
|
+
/**
|
|
1306
|
+
* 偷看当前值:不进行依赖收集(不订阅当前副作用)
|
|
1307
|
+
*/
|
|
1308
|
+
peek(): any;
|
|
1309
|
+
/**
|
|
1310
|
+
* 便于调试:`JSON.stringify(signal)` 时返回内部值
|
|
1311
|
+
*/
|
|
1312
|
+
toJSON(): any;
|
|
1313
|
+
/**
|
|
1314
|
+
* 根据回调对当前值进行计算并设置(相当于 set( updater(current) ))
|
|
1315
|
+
*/
|
|
1316
|
+
update(updater: Function): void;
|
|
1317
|
+
/**
|
|
1318
|
+
* 只读调试:`signal.value` 直接返回内部值(不进行依赖收集)
|
|
1319
|
+
*/
|
|
1320
|
+
value: any;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
export class WasmRue {
|
|
1324
|
+
private constructor();
|
|
1325
|
+
free(): void;
|
|
1326
|
+
[Symbol.dispose](): void;
|
|
1327
|
+
onCreated(f: any): void;
|
|
1328
|
+
onMounted(f: any): void;
|
|
1329
|
+
onUpdated(f: any): void;
|
|
1330
|
+
/**
|
|
1331
|
+
* 使用插件:将 options 归一化为 Vec<JsValue> 并调用内部 use_plugin
|
|
1332
|
+
*/
|
|
1333
|
+
use(plugin: any, options: any): void;
|
|
1334
|
+
onUnmounted(f: any): void;
|
|
1335
|
+
/**
|
|
1336
|
+
* 创建元素/组件的 VNode(开发态对象或注册表引用)
|
|
1337
|
+
*
|
|
1338
|
+
* - 组件函数:构建 Component 类型并输出注册表 ID
|
|
1339
|
+
* - 普通标签:构建 VNodeType,归一化 children,入注册表或内联对象
|
|
1340
|
+
*/
|
|
1341
|
+
createElement(type_tag: any, props: any, children: any): any;
|
|
1342
|
+
/**
|
|
1343
|
+
* 区间渲染入口:解析 id/函数组件/对象为 VNode,并提交区间信息
|
|
1344
|
+
*/
|
|
1345
|
+
renderBetween(vnode_id: any, parent: any, start: any, end: any): void;
|
|
1346
|
+
/**
|
|
1347
|
+
* 读取当前 DOM 适配器(从全局 __rue_dom)
|
|
1348
|
+
*/
|
|
1349
|
+
getDOMAdapter(): any;
|
|
1350
|
+
onBeforeMount(f: any): void;
|
|
1351
|
+
/**
|
|
1352
|
+
* 设置 DOM 适配器(JsDomAdapter),用于后续渲染与元素操作
|
|
1353
|
+
*/
|
|
1354
|
+
setDOMAdapter(adapter: any): void;
|
|
1355
|
+
onBeforeCreate(f: any): void;
|
|
1356
|
+
onBeforeUpdate(f: any): void;
|
|
1357
|
+
onBeforeUnmount(f: any): void;
|
|
1358
|
+
/**
|
|
1359
|
+
* 获取 DOM 适配器的“可变”接口(同只读返回,兼容 JS 侧命名)
|
|
1360
|
+
*/
|
|
1361
|
+
getDOMAdapterMut(): any;
|
|
1362
|
+
/**
|
|
1363
|
+
* JSX/h 入口:与 createElement 行为一致,聚合到同一实现
|
|
1364
|
+
*/
|
|
1365
|
+
h(type_tag: any, props: any, children: any): any;
|
|
1366
|
+
/**
|
|
1367
|
+
* 获取当前容器:优先返回最近一次渲染或挂载设置的容器
|
|
1368
|
+
*/
|
|
1369
|
+
getCurrentContainer(): any;
|
|
1370
|
+
/**
|
|
1371
|
+
* 挂载入口:预刷新延迟任务 → 调用 app → 回退到空片段
|
|
1372
|
+
*/
|
|
1373
|
+
mount(app: any, container: any): void;
|
|
1374
|
+
/**
|
|
1375
|
+
* 返回 Vapor/VaporWithSetup 的注册表对象
|
|
1376
|
+
*
|
|
1377
|
+
* - 若传入 setup 函数:构建 VaporWithSetup,并尝试立即调用以填充 el
|
|
1378
|
+
* - 若 el 为片段:收集其子节点并写入 __fragNodes
|
|
1379
|
+
* - 否则构建普通 Vapor VNode
|
|
1380
|
+
*/
|
|
1381
|
+
vapor(setup: any): any;
|
|
1382
|
+
/**
|
|
1383
|
+
* 渲染入口:接受 id/对象/vaporElement,解析为 VNode 并异步提交
|
|
1384
|
+
*/
|
|
1385
|
+
render(vnode_id: any, container: any): void;
|
|
1386
|
+
/**
|
|
1387
|
+
* 返回一个 JS 回调函数,用于在组件内部发射事件
|
|
1388
|
+
*
|
|
1389
|
+
* - 根据 props 构建事件发射器 emitter(name, args)
|
|
1390
|
+
* - 回调参数 args 若为数组,将其拆解为 Vec<JsValue> 传入
|
|
1391
|
+
*/
|
|
1392
|
+
emitted(props: any): any;
|
|
1393
|
+
/**
|
|
1394
|
+
* 卸载入口:清空容器 innerHTML,并解除 container_map 绑定
|
|
1395
|
+
*/
|
|
1396
|
+
unmount(container: any): void;
|
|
1397
|
+
onError(f: any): void;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* 批量更新:在回调期间抑制副作用的即时触发,统一在结束后刷新
|
|
1402
|
+
*
|
|
1403
|
+
* 用法与示例(JavaScript):
|
|
1404
|
+
* ```js
|
|
1405
|
+
* const { createSignal, createEffect, batch } = wasmModule
|
|
1406
|
+
*
|
|
1407
|
+
* // 示例一:同一信号多次 set,仅在批量结束后重跑一次
|
|
1408
|
+
* const count = createSignal(0)
|
|
1409
|
+
* createEffect(() => {
|
|
1410
|
+
* console.log('[batch-demo] count =', count.get())
|
|
1411
|
+
* })
|
|
1412
|
+
* batch(() => {
|
|
1413
|
+
* count.set(1)
|
|
1414
|
+
* count.set(2)
|
|
1415
|
+
* count.set(3)
|
|
1416
|
+
* // 在批量范围内不会立即触发副作用
|
|
1417
|
+
* })
|
|
1418
|
+
* // 批量结束后只触发一次副作用,打印最新值 3
|
|
1419
|
+
*
|
|
1420
|
+
* // 示例二:一次性更新多个信号,副作用只合并重跑一次
|
|
1421
|
+
* const a = createSignal(0)
|
|
1422
|
+
* const b = createSignal(0)
|
|
1423
|
+
* createEffect(() => {
|
|
1424
|
+
* console.log('[batch-demo] sum =', a.get() + b.get())
|
|
1425
|
+
* })
|
|
1426
|
+
* batch(() => {
|
|
1427
|
+
* a.set(10)
|
|
1428
|
+
* b.set(20)
|
|
1429
|
+
* }) // 仅一次重跑,输出 sum = 30
|
|
1430
|
+
* ```
|
|
1431
|
+
*/
|
|
1432
|
+
export function batch(cb: Function): void;
|
|
1433
|
+
|
|
1434
|
+
export function computed(arg: any, force_global?: boolean | null): SignalHandle;
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
* 创建计算属性
|
|
1438
|
+
* - 参数:可为函数 `() => any`,或对象 `{ get: () => any }`
|
|
1439
|
+
* 返回:一个只读信号句柄(通过 get/peek 读取,内部通过 effect 驱动更新)
|
|
1440
|
+
* 示例(JavaScript):
|
|
1441
|
+
* ```javascript
|
|
1442
|
+
* const { createSignal, createComputed, createEffect } = wasmModule;
|
|
1443
|
+
* const count = createSignal(1);
|
|
1444
|
+
* const double = createComputed(() => count.get() * 2);
|
|
1445
|
+
*
|
|
1446
|
+
* createEffect(() => {
|
|
1447
|
+
* console.log('double =', double.get());
|
|
1448
|
+
* });
|
|
1449
|
+
*
|
|
1450
|
+
* count.set(2); // double 将变为 4 并触发订阅者
|
|
1451
|
+
* ```
|
|
1452
|
+
* 更多用法示例:
|
|
1453
|
+
*
|
|
1454
|
+
* 1) 对象参数(只读 getter):
|
|
1455
|
+
* ```javascript
|
|
1456
|
+
* const { createSignal, createComputed, createEffect } = wasmModule;
|
|
1457
|
+
* const first = createSignal('John');
|
|
1458
|
+
* const last = createSignal('Doe');
|
|
1459
|
+
* const fullName = createComputed({
|
|
1460
|
+
* get: () => first.get() + ' ' + last.get()
|
|
1461
|
+
* });
|
|
1462
|
+
* createEffect(() => {
|
|
1463
|
+
* console.log('fullName =', fullName.get()); // John Doe
|
|
1464
|
+
* });
|
|
1465
|
+
* ```
|
|
1466
|
+
*
|
|
1467
|
+
* 2) 通过更新源信号实现“可写效果”(模拟 Vue3 的 setter 行为):
|
|
1468
|
+
* ```javascript
|
|
1469
|
+
* const setFullName = (nv) => {
|
|
1470
|
+
* const [f, l] = nv.split(' ');
|
|
1471
|
+
* first.set(f);
|
|
1472
|
+
* last.set(l);
|
|
1473
|
+
* };
|
|
1474
|
+
* setFullName('David Smith'); // fullName 将变为 "David Smith"
|
|
1475
|
+
* ```
|
|
1476
|
+
*
|
|
1477
|
+
* 3) 直接使用 `{ get, set }` 创建“可写 computed”,支持 `.set(value)`:
|
|
1478
|
+
* ```javascript
|
|
1479
|
+
* const fullName = createComputed({
|
|
1480
|
+
* get: () => first.get() + ' ' + last.get(),
|
|
1481
|
+
* set: (nv) => {
|
|
1482
|
+
* const [f, l] = nv.split(' ');
|
|
1483
|
+
* first.set(f);
|
|
1484
|
+
* last.set(l);
|
|
1485
|
+
* }
|
|
1486
|
+
* });
|
|
1487
|
+
* fullName.set('David Smith'); // 将调用你的 set 更新源信号,并重算派生值
|
|
1488
|
+
* ```
|
|
1489
|
+
*/
|
|
1490
|
+
export function createComputed(arg: any): SignalHandle;
|
|
1491
|
+
|
|
1492
|
+
/**
|
|
1493
|
+
* 创建副作用(立即运行一次),并返回句柄
|
|
1494
|
+
* 示例(JavaScript):
|
|
1495
|
+
* ```javascript
|
|
1496
|
+
* const { createSignal, createEffect, onCleanup, untrack, batch } = wasmModule;
|
|
1497
|
+
* const s = createSignal(0);
|
|
1498
|
+
*
|
|
1499
|
+
* // 基本副作用:读取信号并打印
|
|
1500
|
+
* createEffect(() => {
|
|
1501
|
+
* console.log('value:', s.get());
|
|
1502
|
+
* });
|
|
1503
|
+
* s.set(1);
|
|
1504
|
+
*
|
|
1505
|
+
* // 清理示例:注册定时器并在下一次运行前清理
|
|
1506
|
+
* createEffect(() => {
|
|
1507
|
+
* const id = setInterval(() => console.log('tick'), 1000);
|
|
1508
|
+
* onCleanup(() => clearInterval(id));
|
|
1509
|
+
* });
|
|
1510
|
+
*
|
|
1511
|
+
* // 断开依赖收集:不会订阅 other 信号
|
|
1512
|
+
* createEffect(() => {
|
|
1513
|
+
* const v = untrack(() => s.get());
|
|
1514
|
+
* console.log('untracked:', v);
|
|
1515
|
+
* });
|
|
1516
|
+
*
|
|
1517
|
+
* 创建副作用(支持自定义调度器与懒执行)
|
|
1518
|
+
* options:
|
|
1519
|
+
* - scheduler: Function( run: Function ),用于自定义何时调用 run
|
|
1520
|
+
* - lazy: bool,true 时不在创建时立即运行
|
|
1521
|
+
* 示例(JavaScript):
|
|
1522
|
+
* ```javascript
|
|
1523
|
+
* const { createEffect } = wasmModule;
|
|
1524
|
+
* const scheduler = (run) => queueMicrotask(run); // 或者使用 requestAnimationFrame
|
|
1525
|
+
* createEffect(() => {
|
|
1526
|
+
* // ...effect body
|
|
1527
|
+
* }, { scheduler, lazy: false });
|
|
1528
|
+
* ```
|
|
1529
|
+
*/
|
|
1530
|
+
export function createEffect(cb: Function, options?: any | null): EffectHandle;
|
|
1531
|
+
|
|
1532
|
+
/**
|
|
1533
|
+
* 创建 Reactive:返回一个对象/数组的响应式代理(深/浅、只读可选)
|
|
1534
|
+
*
|
|
1535
|
+
* 用法(JavaScript / TypeScript):
|
|
1536
|
+
* ```ts
|
|
1537
|
+
* // 基础对象:读取与写入都响应式
|
|
1538
|
+
* const state = createReactive({ user: { name: 'A' }, items: ['x'] })
|
|
1539
|
+
* console.log(state.user.name) // 'A'
|
|
1540
|
+
* state.user.name = 'B' // 写入嵌套字段,触发订阅者
|
|
1541
|
+
* state.items.push('y') // 数组写入也可触发(通过路径写入实现不可变更新)
|
|
1542
|
+
*
|
|
1543
|
+
* // 在 Vapor JSX 中使用(自动 DOM 更新)
|
|
1544
|
+
* // <span>{state.user.name}</span>
|
|
1545
|
+
* // <input value={state.user.name} onInput={e => state.user.name = e.target.value} />
|
|
1546
|
+
*
|
|
1547
|
+
* // 只读代理:禁止写入
|
|
1548
|
+
* const ro = createReactive({ a: 1 }, { readonly: true })
|
|
1549
|
+
* // ro.a = 2 // 将被忽略或导致失败(只读)
|
|
1550
|
+
*
|
|
1551
|
+
* // 浅代理:仅对顶层对象进行代理,子对象不递归代理
|
|
1552
|
+
* const sh = createReactive({ nested: { a: 1 } }, { shallow: true })
|
|
1553
|
+
* // sh.nested 仍为普通对象(非代理)
|
|
1554
|
+
*
|
|
1555
|
+
* // 原始类型:普通值会自动包裹为 { value } 并返回其代理
|
|
1556
|
+
* const num = createReactive(0)
|
|
1557
|
+
* console.log(num.value) // 0
|
|
1558
|
+
* num.value = 1 // 写入 value 字段触发订阅者
|
|
1559
|
+
* const str = createReactive('A')
|
|
1560
|
+
* str.value = 'B' // 原始类型统一通过 value 字段读写
|
|
1561
|
+
*
|
|
1562
|
+
* // 自定义等值比较:用于控制触发频率
|
|
1563
|
+
* const eq = (prev: any, next: any) => _.isEqual(prev, next)
|
|
1564
|
+
* const obj = createReactive({ a: 1 }, { equals: eq })
|
|
1565
|
+
* obj.a = 1 // 不触发(相等)
|
|
1566
|
+
* ```
|
|
1567
|
+
*/
|
|
1568
|
+
export function createReactive(initial: any, options?: any | null): any;
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
* 创建 Ref:返回一个带有 `value` 字段的响应式代理对象
|
|
1572
|
+
*
|
|
1573
|
+
* 用法(JavaScript / TypeScript):
|
|
1574
|
+
* ```ts
|
|
1575
|
+
* // 基本使用:读写 value,自动依赖收集
|
|
1576
|
+
* const r = createRef(0)
|
|
1577
|
+
* console.log(r.value) // 0
|
|
1578
|
+
* r.value = 1 // 触发订阅者
|
|
1579
|
+
*
|
|
1580
|
+
* // 与 watchEffect 配合(依赖自动收集)
|
|
1581
|
+
* const stop = watchEffect(() => {
|
|
1582
|
+
* console.log('ref value =', r.value)
|
|
1583
|
+
* })
|
|
1584
|
+
* r.value = 2 // 触发前面的 watchEffect
|
|
1585
|
+
* stop() // 停止响应
|
|
1586
|
+
*
|
|
1587
|
+
* // peek:查看当前值,不收集依赖(不会订阅当前副作用)
|
|
1588
|
+
* const cur = r.peek() // 仅返回值,不产生订阅
|
|
1589
|
+
*
|
|
1590
|
+
* // update:基于当前值计算并写回
|
|
1591
|
+
* r.update(prev => prev + 1) // 等价于 r.value = (prev + 1)
|
|
1592
|
+
*
|
|
1593
|
+
* // 自定义等值比较:避免无意义的触发
|
|
1594
|
+
* const r2 = createRef({ a: 1 }, { equals: (p, n) => _.isEqual(p?.value, n?.value) })
|
|
1595
|
+
* r2.value = { a: 1 } // 不触发(相等)
|
|
1596
|
+
*
|
|
1597
|
+
* // 与组件/DOM 结合(Vapor 模式下自动更新)
|
|
1598
|
+
* // <span>{r.value}</span> 会被编译为原生 DOM + 响应式更新
|
|
1599
|
+
* ```
|
|
1600
|
+
*/
|
|
1601
|
+
export function createRef(initial: any, options?: any | null): any;
|
|
1602
|
+
|
|
1603
|
+
/**
|
|
1604
|
+
* 创建资源:根据 `src` 信号的值调用 `fetcher`(返回 Promise),
|
|
1605
|
+
* 并提供 `{ data, error, loading }` 三个信号以反映异步状态。
|
|
1606
|
+
* 示例(JavaScript):
|
|
1607
|
+
* ```javascript
|
|
1608
|
+
* const { createSignal, createResource, createEffect } = wasmModule;
|
|
1609
|
+
* const key = createSignal('https://api.example.com/data');
|
|
1610
|
+
* const res = createResource(key, async (k) => {
|
|
1611
|
+
* const r = await fetch(k);
|
|
1612
|
+
* if (!r.ok) throw new Error('network error');
|
|
1613
|
+
* return r.json();
|
|
1614
|
+
* });
|
|
1615
|
+
*
|
|
1616
|
+
* createEffect(() => {
|
|
1617
|
+
* console.log('loading:', res.loading.get());
|
|
1618
|
+
* console.log('data:', res.data.get());
|
|
1619
|
+
* console.log('error:', res.error.get());
|
|
1620
|
+
* });
|
|
1621
|
+
*
|
|
1622
|
+
* key.set('https://api.example.com/data2');
|
|
1623
|
+
* ```
|
|
1624
|
+
*/
|
|
1625
|
+
export function createResource(src: SignalHandle, fetcher: Function): any;
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* 创建 WasmRue 实例(可选设置 DOM 适配器)
|
|
1629
|
+
*
|
|
1630
|
+
* - 若传入 adapter(JS 对象),则设置为 DomAdapter,并在全局挂载 __rue_dom
|
|
1631
|
+
* - 初始化内部 Rue,构造渲染队列与上次容器记录
|
|
1632
|
+
*/
|
|
1633
|
+
export function createRue(adapter: any): WasmRue;
|
|
1634
|
+
|
|
1635
|
+
/**
|
|
1636
|
+
* 创建带选项的信号
|
|
1637
|
+
* options.equals: Function(prev, next) -> bool,返回 true 表示值相等(不触发)
|
|
1638
|
+
* 示例(JavaScript):
|
|
1639
|
+
* ```javascript
|
|
1640
|
+
* const { createSignal, createEffect } = wasmModule;
|
|
1641
|
+
* const count = createSignal(0);
|
|
1642
|
+
* createEffect(() => {
|
|
1643
|
+
* console.log('count =', count.get());
|
|
1644
|
+
* });
|
|
1645
|
+
* count.set(1); // 触发 effect
|
|
1646
|
+
*
|
|
1647
|
+
* const eq = (prev, next) => prev === next;
|
|
1648
|
+
* const s = createSignal(0, { equals: eq });
|
|
1649
|
+
* s.set(0); // 不触发,因为 equals 返回 true(相等)
|
|
1650
|
+
* s.set(2); // 触发订阅者
|
|
1651
|
+
* ```
|
|
1652
|
+
*/
|
|
1653
|
+
export function createSignal(initial: any, options?: any | null): SignalHandle;
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* 获取当前实例;若未设置则返回空值(null/undefined),不再构造默认根实例
|
|
1657
|
+
* 用法(JavaScript):
|
|
1658
|
+
* ```javascript
|
|
1659
|
+
* const { setCurrentInstance, getCurrentInstance, withHookSlot } = wasmModule;
|
|
1660
|
+
*
|
|
1661
|
+
* // 设置并获取当前实例
|
|
1662
|
+
* setCurrentInstance({ name: 'A' });
|
|
1663
|
+
* const inst = getCurrentInstance(); // { name: 'A', ... }
|
|
1664
|
+
*
|
|
1665
|
+
* // 清空当前实例后,获取为空(undefined 或 null)
|
|
1666
|
+
* setCurrentInstance(undefined);
|
|
1667
|
+
* const none = getCurrentInstance(); // undefined(或 null)
|
|
1668
|
+
*
|
|
1669
|
+
* // 在有当前实例时,withHookSlot 为该实例分配/复用一个 Hook 插槽
|
|
1670
|
+
* setCurrentInstance({});
|
|
1671
|
+
* const state = withHookSlot(() => ({ count: 0 })); // 首次创建并缓存到 __hooks.states
|
|
1672
|
+
* ```
|
|
1673
|
+
*/
|
|
1674
|
+
export function getCurrentInstance(): any;
|
|
1675
|
+
|
|
1676
|
+
export function isReactive(obj: any): boolean;
|
|
1677
|
+
|
|
1678
|
+
/**
|
|
1679
|
+
* 在当前运行的副作用上注册清理函数
|
|
1680
|
+
* 清理函数会在下一次该副作用执行之前被调用
|
|
1681
|
+
* 用法与示例(JavaScript):
|
|
1682
|
+
* ```js
|
|
1683
|
+
* // 需求:每次依赖变化时重建一个定时器,并在下一次运行前清理上一次的定时器
|
|
1684
|
+
* const s = createSignal(0)
|
|
1685
|
+
* const eh = createEffect(() => {
|
|
1686
|
+
* const v = s.get() // 建立订阅
|
|
1687
|
+
* const id = setInterval(() => console.log('[onCleanup-demo] tick v=', v), 500)
|
|
1688
|
+
* onCleanup(() => clearInterval(id)) // 下次运行前清理上一次的定时器
|
|
1689
|
+
* })
|
|
1690
|
+
* s.set(1) // 触发重跑:先清理旧定时器,再创建新定时器(只保留一个)
|
|
1691
|
+
* s.set(2) // 同理
|
|
1692
|
+
* eh.dispose() // 停止后续运行,并执行最后一次已注册的清理(定时器不再 tick)
|
|
1693
|
+
* ```
|
|
1694
|
+
*/
|
|
1695
|
+
export function onCleanup(cb: Function): void;
|
|
1696
|
+
|
|
1697
|
+
export function propsReactive(initial: any, force_global?: boolean | null): any;
|
|
1698
|
+
|
|
1699
|
+
export function reactive(initial: any, options?: any | null, force_global?: boolean | null): any;
|
|
1700
|
+
|
|
1701
|
+
export function readonly(initial: any, force_global?: boolean | null): any;
|
|
1702
|
+
|
|
1703
|
+
export function ref(initial: any, options?: any | null, force_global?: boolean | null): any;
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* 设置当前实例
|
|
1707
|
+
* 传入 `null/undefined` 表示清空;否则记录为 Some(instance)
|
|
1708
|
+
* 示例(JavaScript):
|
|
1709
|
+
* ```javascript
|
|
1710
|
+
* const { setCurrentInstance, getCurrentInstance, withHookSlot, createSignal } = wasmModule;
|
|
1711
|
+
* const inst = { name: 'MyComponent' };
|
|
1712
|
+
* setCurrentInstance(inst);
|
|
1713
|
+
*
|
|
1714
|
+
* // 在实例上分配一个 Hook 插槽,用于存储状态对象
|
|
1715
|
+
* const state = withHookSlot(() => ({ count: createSignal(0) }));
|
|
1716
|
+
* console.log(getCurrentInstance()); // 当前实例或根实例
|
|
1717
|
+
* ```
|
|
1718
|
+
*/
|
|
1719
|
+
export function setCurrentInstance(i: any): void;
|
|
1720
|
+
|
|
1721
|
+
/**
|
|
1722
|
+
* 设置调度模式
|
|
1723
|
+
* - mode="sync":信号变更时尽可能同步触发副作用
|
|
1724
|
+
* - 其他值:采用默认微任务合并调度
|
|
1725
|
+
* 示例(JavaScript):
|
|
1726
|
+
* ```javascript
|
|
1727
|
+
* const { setReactiveScheduling } = wasmModule;
|
|
1728
|
+
* setReactiveScheduling('sync'); // 适合少量、快速的更新
|
|
1729
|
+
* // setReactiveScheduling('microtask'); // 使用默认微任务合并(传非 "sync" 即可)
|
|
1730
|
+
* ```
|
|
1731
|
+
*/
|
|
1732
|
+
export function setReactiveScheduling(mode: string): void;
|
|
1733
|
+
|
|
1734
|
+
export function shallowReactive(initial: any, options?: any | null, force_global?: boolean | null): any;
|
|
1735
|
+
|
|
1736
|
+
export function shallowReadonly(initial: any, force_global?: boolean | null): any;
|
|
1737
|
+
|
|
1738
|
+
export function signal(initial: any, options?: any | null, force_global?: boolean | null): SignalHandle;
|
|
1739
|
+
|
|
1740
|
+
export function toRaw(obj: any): any;
|
|
1741
|
+
|
|
1742
|
+
/**
|
|
1743
|
+
* 通用取值转换
|
|
1744
|
+
* 支持以下输入:
|
|
1745
|
+
* - 函数:调用并返回其结果
|
|
1746
|
+
* - 对象:优先读取 `value` 字段,其次尝试调用 `get()` 方法
|
|
1747
|
+
* - 其他:直接返回原值
|
|
1748
|
+
* 示例(JavaScript):
|
|
1749
|
+
* ```javascript
|
|
1750
|
+
* const { toValue } = wasmModule;
|
|
1751
|
+
* toValue(() => 1); // 1
|
|
1752
|
+
* toValue({ value: 2 }); // 2
|
|
1753
|
+
* toValue({ get() { return 3; } }); // 3
|
|
1754
|
+
* toValue('hello'); // 'hello'
|
|
1755
|
+
* ```
|
|
1756
|
+
*/
|
|
1757
|
+
export function toValue(x: any): any;
|
|
1758
|
+
|
|
1759
|
+
export function unref(obj: any): any;
|
|
1760
|
+
|
|
1761
|
+
/**
|
|
1762
|
+
* 断开依赖收集地执行回调(不记录对 Signal 的订阅)
|
|
1763
|
+
*
|
|
1764
|
+
* 用途与示例(JavaScript):
|
|
1765
|
+
* - 在同一个副作用中,订阅主依赖 `a`,但临时读取 `b` 并且不希望 `b` 的变化触发该副作用。
|
|
1766
|
+
* - `untrack(fn)` 会在执行 `fn` 时临时关闭依赖收集,`fn` 内部对信号的读取不会把当前副作用订阅到这些信号上。
|
|
1767
|
+
*
|
|
1768
|
+
* ```js
|
|
1769
|
+
* const a = createSignal(0)
|
|
1770
|
+
* const b = createSignal(0)
|
|
1771
|
+
* createEffect(() => {
|
|
1772
|
+
* const av = a.get() // 订阅 a:a 变化会使该副作用重跑
|
|
1773
|
+
* const bv = untrack(() => b.get()) // 断开依赖读取 b:b 变化不会触发该副作用
|
|
1774
|
+
* console.log('[untrack-demo] run av=', av, 'bv=', bv)
|
|
1775
|
+
* })
|
|
1776
|
+
* b.set(1) // 不触发副作用(因为对 b 的读取发生在 untrack 内)
|
|
1777
|
+
* b.set(2) // 不触发副作用
|
|
1778
|
+
* a.set(10) // 触发副作用(因为对 a 进行了正常订阅)
|
|
1779
|
+
* ```
|
|
1780
|
+
*
|
|
1781
|
+
* 说明:若仅需“不订阅地读取”某个信号,也可使用 `peek()`(例如 `const v = sig.peek()`)。
|
|
1782
|
+
*/
|
|
1783
|
+
export function untrack(cb: Function): any;
|
|
1784
|
+
|
|
1785
|
+
export function useCallback(func: Function, deps: any): Function;
|
|
1786
|
+
|
|
1787
|
+
export function useEffect(effect: Function, deps?: any | null, options?: any | null): void;
|
|
1788
|
+
|
|
1789
|
+
export function useMemo(factory: Function, deps: any): any;
|
|
1790
|
+
|
|
1791
|
+
export function useRef(initial: any): any;
|
|
1792
|
+
|
|
1793
|
+
export function useSetup(factory: Function): any;
|
|
1794
|
+
|
|
1795
|
+
/**
|
|
1796
|
+
* useSignal:等价于 `useState(initial, { kind: 'signal', ...options })`
|
|
1797
|
+
*
|
|
1798
|
+
* 用途:
|
|
1799
|
+
* - 以“底层信号句柄”的形式管理状态,适合需要精细控制 `get/set/update/setPath/peek` 的场景
|
|
1800
|
+
* - 支持 `equals(prev, next)` 自定义等值比较,返回 `true` 表示不触发订阅者
|
|
1801
|
+
*
|
|
1802
|
+
* 示例:
|
|
1803
|
+
* const [sig, setSig] = useSignal({ a: 1 })
|
|
1804
|
+
* console.log(sig.get()) // { a: 1 }
|
|
1805
|
+
* setSig({ a: 2 }) // 触发订阅者
|
|
1806
|
+
* setSig(h => ({ a: h.peek().a + 1 })) // { a: 3 }
|
|
1807
|
+
* sig.setPath('a', 4)
|
|
1808
|
+
* console.log(sig.get()) // { a: 4 }
|
|
1809
|
+
*/
|
|
1810
|
+
export function useSignal(initial: any, options?: any | null): any;
|
|
1811
|
+
|
|
1812
|
+
/**
|
|
1813
|
+
* useState 钩子:统一的轻量状态容器(支持 reactive/ref/signal 三种形态)
|
|
1814
|
+
*
|
|
1815
|
+
* 设计概览:
|
|
1816
|
+
* - 默认形态为 `reactive`:当初始值为对象/数组时直接返回其响应式代理;当为原始类型时自动包裹为 `{ value }` 并返回其代理。
|
|
1817
|
+
* - 可选 `kind`:
|
|
1818
|
+
* - `'reactive'`:对象/数组的响应式代理;原始类型将自动包裹为 `{ value }`
|
|
1819
|
+
* - `'ref'`:总是返回 `{ value }` 的响应式代理,便于统一读写
|
|
1820
|
+
* - `'signal'`:返回底层 `SignalHandle`,适合需要精细控制 get/set/update/path 的场景
|
|
1821
|
+
* - 等值比较:通过 `options.equals(prev, next)` 自定义比较逻辑,返回 `true` 表示值相等,不触发订阅者。
|
|
1822
|
+
*
|
|
1823
|
+
* 使用示例(JavaScript / TypeScript):
|
|
1824
|
+
* // reactive(默认)
|
|
1825
|
+
* const [state, setState] = useState({ user: { name: 'A' }, items: ['x'] })
|
|
1826
|
+
* state.user.name = 'B' // 响应式写入
|
|
1827
|
+
* setState({ user: { name: 'C' }, items: ['y'] }) // 整体替换
|
|
1828
|
+
* setState(prev => ({ ...prev, user: { ...prev.user, name: 'D' } })) // 基于回调更新
|
|
1829
|
+
*
|
|
1830
|
+
* // ref(原始类型亦可统一为 { value })
|
|
1831
|
+
* const [count, setCount] = useState(0, { kind: 'ref' })
|
|
1832
|
+
* console.log(count.value) // 0
|
|
1833
|
+
* setCount(1) // 触发订阅者
|
|
1834
|
+
* setCount(ref => { ref.value += 1 }) // 2
|
|
1835
|
+
*
|
|
1836
|
+
* // signal(底层句柄)
|
|
1837
|
+
* const [sig, setSig] = useState({ a: 1 }, { kind: 'signal' })
|
|
1838
|
+
* console.log(sig.get()) // { a: 1 }
|
|
1839
|
+
* sig.set({ a: 2 }) // 触发订阅者
|
|
1840
|
+
* setSig(handle => ({ a: handle.peek().a + 1 })) // { a: 3 }
|
|
1841
|
+
* sig.setPath('a', 4) // 路径写入
|
|
1842
|
+
* console.log(sig.get()) // { a: 4 }
|
|
1843
|
+
*
|
|
1844
|
+
* // 自定义等值比较(默认使用 shallowEqual)
|
|
1845
|
+
* const [state2, setState2] = useState({ a: 1, b: 2 }, { kind: 'reactive', equals: (prev, next) => prev.a === next.a })
|
|
1846
|
+
* setState2({ a: 1, b: 3 }) // 不触发订阅者,因为 a 未改变
|
|
1847
|
+
* setState2({ a: 2, b: 4 }) // 触发订阅者,因为 a 改变
|
|
1848
|
+
*/
|
|
1849
|
+
export function useState(initial: any, options?: any | null): any;
|
|
1850
|
+
|
|
1851
|
+
/**
|
|
1852
|
+
* 按 Hook 唯一 id 绑定插槽索引并运行回调
|
|
1853
|
+
* - 行为:在当前实例的 `__hooks.__idMap` 中为 `id` 分配稳定索引,并通过设置 `__forcedIndex` 强制 `withHookSlot` 使用该索引
|
|
1854
|
+
* - 若此前不存在该 id,则使用 `states.length` 作为新索引
|
|
1855
|
+
* - 运行 `runner()`,并在结束后清除 `__forcedIndex`
|
|
1856
|
+
*/
|
|
1857
|
+
export function vaporWithHookId(id: any, runner: Function): any;
|
|
1858
|
+
|
|
1859
|
+
/**
|
|
1860
|
+
* 统一侦听入口:接受函数、信号句柄对象(含 `get` 方法)或来源数组
|
|
1861
|
+
*
|
|
1862
|
+
* 用法(JavaScript):
|
|
1863
|
+
* ```js
|
|
1864
|
+
* // 1) 侦听函数源(等价 watchFn)
|
|
1865
|
+
* const stop1 = watch(() => state.count, (n, o) => { console.log(n, o) }, { immediate: true })
|
|
1866
|
+
*
|
|
1867
|
+
* // 2) 侦听信号句柄对象(含 get 方法)
|
|
1868
|
+
* const s = createSignal(0)
|
|
1869
|
+
* const stop2 = watch(s, (n, o) => { console.log(n, o) })
|
|
1870
|
+
* s.set(1)
|
|
1871
|
+
*
|
|
1872
|
+
* // 3) 侦听来源数组:函数 / 信号句柄 / 常量混合
|
|
1873
|
+
* const s1 = createSignal(0)
|
|
1874
|
+
* const s2 = createSignal(10)
|
|
1875
|
+
* const stop3 = watch([() => s1.get(), s2, 'x'], ([n1, n2, c], [o1, o2, oc]) => { console.log(n1, n2, c, o1, o2, oc) })
|
|
1876
|
+
* s1.set(1); s2.set(11)
|
|
1877
|
+
*
|
|
1878
|
+
* // 自定义比较:覆盖默认逐项浅比较
|
|
1879
|
+
* watch([s1, s2], (n, o) => {}, { equals: (prev, next) => _.isEqual(prev, next) })
|
|
1880
|
+
* ```
|
|
1881
|
+
*/
|
|
1882
|
+
export function watch(source: any, handler: Function, options_raw?: any | null): EffectHandle;
|
|
1883
|
+
|
|
1884
|
+
/**
|
|
1885
|
+
* 深度侦听:当对象/数组任意子项变化时触发
|
|
1886
|
+
*
|
|
1887
|
+
* 用法(JavaScript):
|
|
1888
|
+
* ```js
|
|
1889
|
+
* const s = createSignal({ user: { name: 'A' }, items: ['x'] })
|
|
1890
|
+
* const stop = watchDeepSignal(s, (newv, oldv) => {
|
|
1891
|
+
* console.log('deep changed', oldv, '->', newv)
|
|
1892
|
+
* }, { immediate: true })
|
|
1893
|
+
* s.setPath(['user','name'], 'B')
|
|
1894
|
+
* s.setPath(['items', 0], 'y')
|
|
1895
|
+
* stop.dispose() // 取消侦听
|
|
1896
|
+
* ```
|
|
1897
|
+
*/
|
|
1898
|
+
export function watchDeepSignal(src: SignalHandle, handler: Function, options?: any | null): EffectHandle;
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* 侦听副作用:在副作用体内自行读取信号,依赖会自动收集
|
|
1902
|
+
* 类似 Vue 的 `watchEffect`
|
|
1903
|
+
* 示例(JavaScript):
|
|
1904
|
+
* ```javascript
|
|
1905
|
+
* const { createSignal, watchEffect } = wasmModule;
|
|
1906
|
+
* const s = createSignal(0);
|
|
1907
|
+
* watchEffect(() => {
|
|
1908
|
+
* console.log('value:', s.get());
|
|
1909
|
+
* }, {
|
|
1910
|
+
* //可选 scheduler
|
|
1911
|
+
* });
|
|
1912
|
+
* s.set(1);
|
|
1913
|
+
* ```
|
|
1914
|
+
*/
|
|
1915
|
+
export function watchEffect(cb: Function, options?: any | null): EffectHandle;
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* 侦听任意 getter 函数的结果变化
|
|
1919
|
+
* options: { immediate?: bool, scheduler?: Function, equals?: Function }
|
|
1920
|
+
* 示例(JavaScript):
|
|
1921
|
+
* ```javascript
|
|
1922
|
+
* const { watchFn } = wasmModule;
|
|
1923
|
+
* const obj = { value: 0 };
|
|
1924
|
+
* const stop = watchFn(
|
|
1925
|
+
* () => obj.value,
|
|
1926
|
+
* (newv, oldv) => console.log('changed:', oldv, '->', newv),
|
|
1927
|
+
* { immediate: true }
|
|
1928
|
+
* );
|
|
1929
|
+
* obj.value = 1; // 当 getter 读取到新值时触发 handler
|
|
1930
|
+
* ```
|
|
1931
|
+
*/
|
|
1932
|
+
export function watchFn(getter: Function, handler: Function, options?: any | null): EffectHandle;
|
|
1933
|
+
|
|
1934
|
+
/**
|
|
1935
|
+
* 侦听指定路径的值变化:`path` 支持 JS 数组或以 `.` 分隔的字符串
|
|
1936
|
+
*
|
|
1937
|
+
* 用法(JavaScript):
|
|
1938
|
+
* ```js
|
|
1939
|
+
* const s = createSignal({ user: { profile: { name: 'A' } } })
|
|
1940
|
+
* // 数组路径
|
|
1941
|
+
* watchPath(s, ['user','profile','name'], (n, o) => {
|
|
1942
|
+
* console.log('name changed:', o, '->', n)
|
|
1943
|
+
* }, { immediate: true })
|
|
1944
|
+
* // 字符串路径(数字段会自动转为数组索引)
|
|
1945
|
+
* watchPath(s, 'user.profile.name', (n, o) => {
|
|
1946
|
+
* console.log('name changed:', o, '->', n)
|
|
1947
|
+
* }, { immediate: true })
|
|
1948
|
+
* s.setPath(['user','profile','name'], 'B')
|
|
1949
|
+
* s.setPath('user.profile.name', 'B')
|
|
1950
|
+
* ```
|
|
1951
|
+
*/
|
|
1952
|
+
export function watchPath(src: SignalHandle, path: any, handler: Function, options?: any | null): EffectHandle;
|
|
1953
|
+
|
|
1954
|
+
/**
|
|
1955
|
+
* 侦听单个信号的变化并在变化时触发处理器
|
|
1956
|
+
* options: { immediate?: bool, scheduler?: Function, equals?: Function }
|
|
1957
|
+
* 示例(JavaScript):
|
|
1958
|
+
* ```javascript
|
|
1959
|
+
* const { createSignal, watchSignal } = wasmModule;
|
|
1960
|
+
* const s = createSignal(0);
|
|
1961
|
+
* watchSignal(s, (newv, oldv) => {
|
|
1962
|
+
* console.log('signal changed:', oldv, '->', newv);
|
|
1963
|
+
* }, { immediate: true });
|
|
1964
|
+
* s.set(1);
|
|
1965
|
+
* ```
|
|
1966
|
+
*/
|
|
1967
|
+
export function watchSignal(src: SignalHandle, handler: Function, options?: any | null): EffectHandle;
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* 在当前实例上为 Hook 分配/复用一个插槽
|
|
1971
|
+
* - 若无当前实例,则直接执行 factory 返回对象
|
|
1972
|
+
* - 有实例时,依据 `__hooks.index` 或 `__forcedIndex` 计算插槽序号
|
|
1973
|
+
* 示例(JavaScript):
|
|
1974
|
+
* ```javascript
|
|
1975
|
+
* const { setCurrentInstance, withHookSlot } = wasmModule;
|
|
1976
|
+
* setCurrentInstance({});
|
|
1977
|
+
* const a = withHookSlot(() => ({ id: 1 }));
|
|
1978
|
+
* const b = withHookSlot(() => ({ id: 2 }));
|
|
1979
|
+
* // 若再次调用 withHookSlot(且未强制索引),将分配下一个插槽
|
|
1980
|
+
* ```
|
|
1981
|
+
*/
|
|
1982
|
+
export function withHookSlot(factory: Function): any;
|