@feng3d/reactivity 0.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +124 -26
  2. package/dist/index.js +1018 -58
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.umd.cjs +1017 -57
  5. package/dist/index.umd.cjs.map +1 -1
  6. package/lib/Reactivity.d.ts +69 -0
  7. package/lib/Reactivity.d.ts.map +1 -0
  8. package/lib/arrayInstrumentations.d.ts +2 -0
  9. package/lib/arrayInstrumentations.d.ts.map +1 -0
  10. package/lib/baseHandlers.d.ts +5 -0
  11. package/lib/baseHandlers.d.ts.map +1 -0
  12. package/lib/batch.d.ts +23 -0
  13. package/lib/batch.d.ts.map +1 -0
  14. package/lib/collectionHandlers.d.ts +7 -0
  15. package/lib/collectionHandlers.d.ts.map +1 -0
  16. package/lib/computed.d.ts +94 -0
  17. package/lib/computed.d.ts.map +1 -0
  18. package/lib/effect.d.ts +52 -0
  19. package/lib/effect.d.ts.map +1 -0
  20. package/lib/index.d.ts +7 -18
  21. package/lib/index.d.ts.map +1 -1
  22. package/lib/property.d.ts +44 -0
  23. package/lib/property.d.ts.map +1 -0
  24. package/lib/reactive.d.ts +66 -0
  25. package/lib/reactive.d.ts.map +1 -0
  26. package/lib/ref.d.ts +41 -0
  27. package/lib/ref.d.ts.map +1 -0
  28. package/lib/shared/constants.d.ts +34 -0
  29. package/lib/shared/constants.d.ts.map +1 -0
  30. package/lib/shared/general.d.ts +36 -0
  31. package/lib/shared/general.d.ts.map +1 -0
  32. package/package.json +3 -2
  33. package/src/Reactivity.ts +126 -0
  34. package/src/arrayInstrumentations.ts +448 -0
  35. package/src/baseHandlers.ts +220 -0
  36. package/src/batch.ts +91 -0
  37. package/src/collectionHandlers.ts +298 -0
  38. package/src/computed.ts +201 -0
  39. package/src/effect.ts +108 -0
  40. package/src/index.ts +7 -200
  41. package/src/property.ts +223 -0
  42. package/src/reactive.ts +130 -0
  43. package/src/ref.ts +88 -0
  44. package/src/shared/constants.ts +41 -0
  45. package/src/shared/general.ts +109 -0
package/src/batch.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { ComputedReactivity } from './computed';
2
+ import { Reactivity } from './Reactivity';
3
+
4
+ /**
5
+ * 合批处理。
6
+ *
7
+ * @param dep 要处理的依赖。
8
+ * @param isRunning 添加时是否是正在运行。
9
+ */
10
+ export function batch(dep: ComputedReactivity, isRunning: boolean): void
11
+ {
12
+ if (isRunning)
13
+ {
14
+ _isRunedDeps.push(dep);
15
+ }
16
+ else
17
+ {
18
+ _needEffectDeps.push(dep);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * 批次执行多次修改反应式对象,可以减少不必要的反应式触发。
24
+ *
25
+ * ```ts
26
+ * batchRun(() => {
27
+ * // 修改反应式对象
28
+ * reactiveObj.a = 1;
29
+ * reactiveObj.b = 2;
30
+ * })
31
+ * ```
32
+ *
33
+ * @param fn 要执行的函数,在此函数中多次修改反应式对象。
34
+ */
35
+ export function batchRun<T>(fn: () => T): T
36
+ {
37
+ _batchDepth++;
38
+
39
+ const result = fn();
40
+
41
+ if (--_batchDepth > 0)
42
+ {
43
+ return;
44
+ }
45
+
46
+ // 处理已经运行过的依赖,
47
+ if (_isRunedDeps.length > 0)
48
+ {
49
+ _isRunedDeps.forEach((dep) =>
50
+ {
51
+ // 此时依赖以及子依赖都已经运行过了,只需修复与子节点关系。
52
+ __DEV__ && console.assert(dep._isDirty === false, 'dep.dirty === false');
53
+
54
+ // 修复与子节点关系
55
+ dep._children.forEach((version, node) =>
56
+ {
57
+ node._parents.set(dep, dep._version);
58
+ });
59
+ dep._children.clear();
60
+ });
61
+ _isRunedDeps.length = 0;
62
+ }
63
+
64
+ // 批次处理
65
+ if (_needEffectDeps.length > 0)
66
+ {
67
+ _needEffectDeps.forEach((dep) =>
68
+ {
69
+ // 独立执行回调
70
+ const pre = Reactivity.activeReactivity;
71
+ Reactivity.activeReactivity = null;
72
+
73
+ dep.runIfDirty();
74
+
75
+ Reactivity.activeReactivity = pre;
76
+ });
77
+ _needEffectDeps.length = 0;
78
+ }
79
+
80
+ return result;
81
+ }
82
+
83
+ let _batchDepth = 0;
84
+ /**
85
+ * 正在运行的依赖。
86
+ */
87
+ const _needEffectDeps: ComputedReactivity[] = [];
88
+ /**
89
+ * 已经运行过的依赖,只需要修复与子节点关系。
90
+ */
91
+ const _isRunedDeps: ComputedReactivity[] = [];
@@ -0,0 +1,298 @@
1
+ import { toReactive } from './reactive';
2
+ import { ITERATE_KEY, MAP_KEY_ITERATE_KEY, ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './shared/constants';
3
+ import { hasChanged, hasOwn, isMap, Target, toRaw, toRawType, warn } from './shared/general';
4
+ import { PropertyReactivity } from './property';
5
+
6
+ export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
7
+ get: createInstrumentationGetter(),
8
+ };
9
+
10
+ function createInstrumentationGetter()
11
+ {
12
+ const instrumentations = createInstrumentations();
13
+
14
+ return (
15
+ target: CollectionTypes,
16
+ key: string | symbol,
17
+ receiver: CollectionTypes,
18
+ ) =>
19
+ {
20
+ if (key === ReactiveFlags.IS_REACTIVE)
21
+ {
22
+ return true;
23
+ }
24
+ else if (key === ReactiveFlags.RAW)
25
+ {
26
+ return target;
27
+ }
28
+
29
+ return Reflect.get(
30
+ hasOwn(instrumentations, key) && key in target
31
+ ? instrumentations
32
+ : target,
33
+ key,
34
+ receiver,
35
+ );
36
+ };
37
+ }
38
+
39
+ type Instrumentations = Record<string | symbol, Function | number>;
40
+
41
+ function createInstrumentations(): Instrumentations
42
+ {
43
+ const instrumentations: Instrumentations = {
44
+ get(this: MapTypes, key: unknown)
45
+ {
46
+ // #1772: readonly(reactive(Map)) should return readonly + reactive version
47
+ // of the value
48
+ const target = this[ReactiveFlags.RAW];
49
+ const rawTarget = toRaw(target);
50
+ const rawKey = toRaw(key);
51
+
52
+ if (hasChanged(key, rawKey))
53
+ {
54
+ PropertyReactivity.track(rawTarget, TrackOpTypes.GET, key);
55
+ }
56
+ PropertyReactivity.track(rawTarget, TrackOpTypes.GET, rawKey);
57
+
58
+ const { has } = getProto(rawTarget);
59
+ const wrap = toReactive;
60
+ if (has.call(rawTarget, key))
61
+ {
62
+ return wrap(target.get(key));
63
+ }
64
+ else if (has.call(rawTarget, rawKey))
65
+ {
66
+ return wrap(target.get(rawKey));
67
+ }
68
+ else if (target !== rawTarget)
69
+ {
70
+ // #3602 readonly(reactive(Map))
71
+ // ensure that the nested reactive `Map` can do tracking for itself
72
+ target.get(key);
73
+ }
74
+ },
75
+ get size()
76
+ {
77
+ const target = (this as unknown as IterableCollections)[ReactiveFlags.RAW];
78
+ PropertyReactivity.track(toRaw(target), TrackOpTypes.ITERATE, ITERATE_KEY);
79
+
80
+ return Reflect.get(target, 'size', target);
81
+ },
82
+ has(this: CollectionTypes, key: unknown): boolean
83
+ {
84
+ const target = this[ReactiveFlags.RAW];
85
+ const rawTarget = toRaw(target);
86
+ const rawKey = toRaw(key);
87
+
88
+ if (hasChanged(key, rawKey))
89
+ {
90
+ PropertyReactivity.track(rawTarget, TrackOpTypes.HAS, key);
91
+ }
92
+ PropertyReactivity.track(rawTarget, TrackOpTypes.HAS, rawKey);
93
+
94
+ return key === rawKey
95
+ ? target.has(key)
96
+ : target.has(key) || target.has(rawKey);
97
+ },
98
+ forEach(this: IterableCollections, callback: Function, thisArg?: unknown)
99
+ {
100
+ const observed = this;
101
+ const target = observed[ReactiveFlags.RAW];
102
+ const rawTarget = toRaw(target);
103
+ const wrap = toReactive;
104
+ PropertyReactivity.track(rawTarget, TrackOpTypes.ITERATE, ITERATE_KEY);
105
+
106
+ return target.forEach((value: unknown, key: unknown) =>
107
+
108
+ // important: make sure the callback is
109
+ // 1. invoked with the reactive map as `this` and 3rd arg
110
+ // 2. the value received should be a corresponding reactive/readonly.
111
+ callback.call(thisArg, wrap(value), wrap(key), observed)
112
+ );
113
+ },
114
+
115
+ add(this: SetTypes, value: unknown)
116
+ {
117
+ value = toRaw(value);
118
+ const target = toRaw(this);
119
+ const proto = getProto(target);
120
+ const hadKey = proto.has.call(target, value);
121
+ if (!hadKey)
122
+ {
123
+ target.add(value);
124
+ PropertyReactivity.trigger(target, TriggerOpTypes.ADD, value, value);
125
+ }
126
+
127
+ return this;
128
+ },
129
+ set(this: MapTypes, key: unknown, value: unknown)
130
+ {
131
+ value = toRaw(value);
132
+ const target = toRaw(this);
133
+ const { has, get } = getProto(target);
134
+
135
+ let hadKey = has.call(target, key);
136
+ if (!hadKey)
137
+ {
138
+ key = toRaw(key);
139
+ hadKey = has.call(target, key);
140
+ }
141
+ else if (__DEV__)
142
+ {
143
+ checkIdentityKeys(target, has, key);
144
+ }
145
+
146
+ const oldValue = get.call(target, key);
147
+ target.set(key, value);
148
+ if (!hadKey)
149
+ {
150
+ PropertyReactivity.trigger(target, TriggerOpTypes.ADD, key, value);
151
+ }
152
+ else if (hasChanged(value, oldValue))
153
+ {
154
+ PropertyReactivity.trigger(target, TriggerOpTypes.SET, key, value, oldValue);
155
+ }
156
+
157
+ return this;
158
+ },
159
+ delete(this: CollectionTypes, key: unknown)
160
+ {
161
+ const target = toRaw(this);
162
+ const { has, get } = getProto(target);
163
+ let hadKey = has.call(target, key);
164
+ if (!hadKey)
165
+ {
166
+ key = toRaw(key);
167
+ hadKey = has.call(target, key);
168
+ }
169
+ else if (__DEV__)
170
+ {
171
+ checkIdentityKeys(target, has, key);
172
+ }
173
+
174
+ const oldValue = get ? get.call(target, key) : undefined;
175
+ // forward the operation before queueing reactions
176
+ const result = target.delete(key);
177
+ if (hadKey)
178
+ {
179
+ PropertyReactivity.trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue);
180
+ }
181
+
182
+ return result;
183
+ },
184
+ clear(this: IterableCollections)
185
+ {
186
+ const target = toRaw(this);
187
+ const hadItems = target.size !== 0;
188
+ const oldTarget = __DEV__
189
+ ? isMap(target)
190
+ ? new Map(target)
191
+ : new Set(target)
192
+ : undefined;
193
+ // forward the operation before queueing reactions
194
+ const result = target.clear();
195
+ if (hadItems)
196
+ {
197
+ PropertyReactivity.trigger(
198
+ target,
199
+ TriggerOpTypes.CLEAR,
200
+ undefined,
201
+ undefined,
202
+ oldTarget,
203
+ );
204
+ }
205
+
206
+ return result;
207
+ },
208
+ };
209
+
210
+ const iteratorMethods = [
211
+ 'keys',
212
+ 'values',
213
+ 'entries',
214
+ Symbol.iterator,
215
+ ] as const;
216
+
217
+ iteratorMethods.forEach((method) =>
218
+ {
219
+ instrumentations[method] = createIterableMethod(method);
220
+ });
221
+
222
+ return instrumentations;
223
+ }
224
+
225
+ function createIterableMethod(method: string | symbol)
226
+ {
227
+ return function (
228
+ this: IterableCollections,
229
+ ...args: unknown[]
230
+ ): Iterable<unknown> & Iterator<unknown>
231
+ {
232
+ const target = this[ReactiveFlags.RAW];
233
+ const rawTarget = toRaw(target);
234
+ const targetIsMap = isMap(rawTarget);
235
+ const isPair
236
+ = method === 'entries' || (method === Symbol.iterator && targetIsMap);
237
+ const isKeyOnly = method === 'keys' && targetIsMap;
238
+ const innerIterator = target[method](...args);
239
+ const wrap = toReactive;
240
+
241
+ PropertyReactivity.track(
242
+ rawTarget,
243
+ TrackOpTypes.ITERATE,
244
+ isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY,
245
+ );
246
+
247
+ // return a wrapped iterator which returns observed versions of the
248
+ // values emitted from the real iterator
249
+ return {
250
+ // iterator protocol
251
+ next()
252
+ {
253
+ const { value, done } = innerIterator.next();
254
+
255
+ return done
256
+ ? { value, done }
257
+ : {
258
+ value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),
259
+ done,
260
+ };
261
+ },
262
+ // iterable protocol
263
+ [Symbol.iterator]()
264
+ {
265
+ return this;
266
+ },
267
+ };
268
+ };
269
+ }
270
+
271
+ function checkIdentityKeys(
272
+ target: CollectionTypes,
273
+ has: (key: unknown) => boolean,
274
+ key: unknown,
275
+ )
276
+ {
277
+ const rawKey = toRaw(key);
278
+ if (rawKey !== key && has.call(target, rawKey))
279
+ {
280
+ const type = toRawType(target);
281
+ warn(
282
+ `Reactive ${type} contains both the raw and reactive `
283
+ + `versions of the same object${type === `Map` ? ` as keys` : ``}, `
284
+ + `which can lead to inconsistencies. `
285
+ + `Avoid differentiating between the raw and reactive versions `
286
+ + `of an object and only use the reactive version if possible.`,
287
+ );
288
+ }
289
+ }
290
+
291
+ const getProto = <T extends CollectionTypes>(v: T): any => Reflect.getPrototypeOf(v);
292
+
293
+ type CollectionTypes = IterableCollections | WeakCollections;
294
+
295
+ type IterableCollections = (Map<any, any> | Set<any>) & Target;
296
+ type WeakCollections = (WeakMap<any, any> | WeakSet<any>) & Target;
297
+ type MapTypes = (Map<any, any> | WeakMap<any, any>) & Target;
298
+ type SetTypes = (Set<any> | WeakSet<any>) & Target;
@@ -0,0 +1,201 @@
1
+ import { batch } from './batch';
2
+ import { Reactivity, ReactivityLink, forceTrack } from './Reactivity';
3
+ import { hasChanged } from './shared/general';
4
+
5
+ /**
6
+ * 创建计算反应式对象。
7
+ *
8
+ * 首次获取值将会执行函数,后续获取值且在依赖发生变化的情况下将会重新计算。
9
+ *
10
+ * @param func 检测的可能包含响应式的函数。
11
+ * @returns 包含 value 属性的对象,用于获取计算结果。
12
+ */
13
+ export function computed<T>(func: (oldValue?: T) => T): Computed<T>
14
+ {
15
+ return new ComputedReactivity(func) as any;
16
+ }
17
+
18
+ /**
19
+ * 计算反应式对象。
20
+ */
21
+ export interface Computed<T = any>
22
+ {
23
+ readonly value: T
24
+ [ComputedSymbol]: true
25
+ }
26
+ declare const ComputedSymbol: unique symbol;
27
+
28
+ export interface ComputedReactivity<T = any> extends Computed<T> { }
29
+
30
+ /**
31
+ * 计算反应式节点。
32
+ *
33
+ * 当使用 computed 函数时,会创建一个 ComputedDep 对象。
34
+ *
35
+ * 首次获取值将会执行函数,后续获取值且在依赖发生变化的情况下将会重新计算。
36
+ */
37
+ export class ComputedReactivity<T = any> extends Reactivity<T>
38
+ {
39
+ /**
40
+ * @internal
41
+ */
42
+ readonly __v_isRef = true;
43
+
44
+ /**
45
+ * 监听的函数。
46
+ */
47
+ protected _func: (oldValue?: T) => T;
48
+
49
+ /**
50
+ * 失效子节点。
51
+ *
52
+ * @private
53
+ */
54
+ _children = new Map<Reactivity, any>();
55
+
56
+ /**
57
+ * 是否脏,是否需要重新计算。
58
+ *
59
+ * 用于在没有值发生变化时,避免重复计算。
60
+ *
61
+ * @private
62
+ */
63
+ _isDirty = true;
64
+
65
+ /**
66
+ * 版本号。
67
+ *
68
+ * 重新计算后自动递增。用于判断子节点中的父节点引用是否过期。
69
+ *
70
+ * @private
71
+ */
72
+ _version = -1;
73
+
74
+ /**
75
+ * 创建计算依赖。
76
+ * @param func 检测的可能包含响应式的函数。
77
+ */
78
+ constructor(func: (oldValue?: T) => T)
79
+ {
80
+ super();
81
+ this._func = func;
82
+ }
83
+
84
+ /**
85
+ * 捕捉。
86
+ *
87
+ * 建立与父节点的依赖关系。
88
+ */
89
+ track()
90
+ {
91
+ this.runIfDirty();
92
+
93
+ super.track();
94
+ }
95
+
96
+ /**
97
+ * 触发。
98
+ *
99
+ * 冒泡到所有父节点,设置失效子节点字典。
100
+ *
101
+ * 把触发节点添加到失效子节点字典队列中。
102
+ */
103
+ trigger(): void
104
+ {
105
+ // 正在运行时被触发,需要在运行结束后修复父子节点关系。
106
+ if (Reactivity.activeReactivity === this)
107
+ {
108
+ batch(this, Reactivity.activeReactivity === this);
109
+ }
110
+
111
+ super.trigger();
112
+ }
113
+
114
+ /**
115
+ * 执行当前节点。
116
+ */
117
+ run()
118
+ {
119
+ // 不受嵌套的 effect 影响。
120
+ forceTrack(() =>
121
+ {
122
+ // 保存当前节点作为父节点。
123
+ const parentReactiveNode = Reactivity.activeReactivity;
124
+ // 设置当前节点为活跃节点。
125
+ Reactivity.activeReactivity = this as any;
126
+
127
+ this._version++;
128
+ this._value = this._func(this._value);
129
+
130
+ // 执行完毕后恢复父节点。
131
+ Reactivity.activeReactivity = parentReactiveNode;
132
+ });
133
+ }
134
+
135
+ /**
136
+ * 检查当前节点是否脏。
137
+ *
138
+ * 如果脏,则执行计算。
139
+ */
140
+ runIfDirty()
141
+ {
142
+ // 检查是否存在失效子节点字典。
143
+ this._isDirty = this._isDirty || this.isChildrenChanged();
144
+
145
+ // 标记为脏的情况下,执行计算。
146
+ if (this._isDirty)
147
+ {
148
+ // 立即去除脏标记,避免循环多重计算。
149
+ this._isDirty = false;
150
+
151
+ //
152
+ this.run();
153
+ }
154
+ }
155
+
156
+ /**
157
+ * 判断子节点是否发生变化。
158
+ */
159
+ protected isChildrenChanged()
160
+ {
161
+ if (this._children.size === 0) return false;
162
+
163
+ // 检查是否存在子节点发生变化。
164
+ let isChanged = false;
165
+
166
+ // 避免在检查过程建立依赖关系。
167
+ const preReactiveNode = Reactivity.activeReactivity;
168
+ Reactivity.activeReactivity = null;
169
+
170
+ // 检查子节点是否发生变化。
171
+ this._children.forEach((value, node) =>
172
+ {
173
+ if (isChanged) return;
174
+ if (node.value !== value)
175
+ {
176
+ // 子节点变化,需要重新计算。
177
+ isChanged = true;
178
+
179
+ return;
180
+ }
181
+ });
182
+
183
+ // 恢复父节点。
184
+ Reactivity.activeReactivity = preReactiveNode;
185
+
186
+ if (!isChanged)
187
+ {
188
+ // 修复与子节点关系
189
+ this._children.forEach((version, node) =>
190
+ {
191
+ node._parents.set(this, this._version);
192
+ });
193
+ }
194
+
195
+ // 清空子节点。
196
+ this._children.clear();
197
+
198
+ return isChanged;
199
+ }
200
+ }
201
+
package/src/effect.ts ADDED
@@ -0,0 +1,108 @@
1
+ import { batch, batchRun } from './batch';
2
+ import { ComputedReactivity } from './computed';
3
+ import { Reactivity } from './Reactivity';
4
+
5
+ /**
6
+ * 创建效果反应式节点。
7
+ *
8
+ * 将会维持反应式效果,当被作用的函数所引用的响应式对象发生变化时,会立即执行 fn 函数。
9
+ *
10
+ * @param fn 要执行的函数
11
+ * @returns 暂停和恢复副作用的函数
12
+ *
13
+ * 注:
14
+ * 1. 与 `@vue/reactivity` 中的 effect 不同,此函数返回的是一个 Effect 对象,而不是一个函数。
15
+ * 2. 不希望用户直接执行,而是通过反应式自动触发。
16
+ * 3. 真有需求,可以使用 effect(func).run(true) 来代替 @vue/reactivity 中的 effect(func)() 。
17
+ *
18
+ */
19
+ export function effect<T = any>(fn: () => T): Effect
20
+ {
21
+ return new EffectReactivity(fn);
22
+ }
23
+
24
+ /**
25
+ * 效果反应式节点。
26
+ */
27
+ export class EffectReactivity<T = any> extends ComputedReactivity<T> implements Effect
28
+ {
29
+ /**
30
+ * 是否为启用, 默认为 true。
31
+ *
32
+ * 启用时,会立即执行函数。
33
+ */
34
+ private _isEnable = true;
35
+
36
+ constructor(func: (oldValue?: T) => T)
37
+ {
38
+ super(func);
39
+ this.runIfDirty();
40
+ }
41
+
42
+ pause()
43
+ {
44
+ this._isEnable = false;
45
+ }
46
+
47
+ resume()
48
+ {
49
+ if (this._isEnable) return;
50
+ this._isEnable = true;
51
+ if (EffectReactivity.pausedQueueEffects.has(this))
52
+ {
53
+ EffectReactivity.pausedQueueEffects.delete(this);
54
+ this.trigger();
55
+ }
56
+ }
57
+
58
+ trigger()
59
+ {
60
+ batchRun(() =>
61
+ {
62
+ super.trigger();
63
+
64
+ if (this._isEnable)
65
+ {
66
+ // 合批时需要判断是否已经运行的依赖。
67
+ batch(this, Reactivity.activeReactivity === this);
68
+ }
69
+ else
70
+ {
71
+ EffectReactivity.pausedQueueEffects.add(this);
72
+ }
73
+ });
74
+ }
75
+ private static pausedQueueEffects = new WeakSet<EffectReactivity>();
76
+
77
+ /**
78
+ * 执行当前节点。
79
+ *
80
+ * 当暂停时将会直接执行被包装的函数。
81
+ */
82
+ run(): void
83
+ {
84
+ if (this._isEnable)
85
+ {
86
+ super.run();
87
+ }
88
+ else
89
+ {
90
+ this._func(this._value);
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * 维持反应式效果。
97
+ */
98
+ export interface Effect
99
+ {
100
+ /**
101
+ * 暂停。
102
+ */
103
+ pause: () => void;
104
+ /**
105
+ * 恢复。
106
+ */
107
+ resume: () => void;
108
+ }