@whitesev/utils 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1519,14 +1519,39 @@ declare class Utils {
1519
1519
  /**
1520
1520
  * 自定义的动态响应对象
1521
1521
  * @example
1522
- * let vue = new Utils.Vue();
1523
- * let reactive = new vue.reactive({});
1524
- * vue.watch(()=>reactive["name"], (newValue, oldValue)=>{
1522
+ * const vue = new Utils.Vue();
1523
+ * const reactive = vue.reactive({
1524
+ * name: "",
1525
+ * });
1526
+ * vue.watch(()=>reactive.name, (newValue, oldValue)=>{
1527
+ * console.log("newValue ==> " + newValue);
1528
+ * console.log("oldValue ==> " + oldValue);
1529
+ * })
1530
+ * reactive.name = "测试";
1531
+ * > newValue ==> 测试
1532
+ * > oldValue ==>
1533
+ * reactive.name = "null";
1534
+ * > newValue ==> null
1535
+ * > oldValue ==> 测试
1536
+ * reactive.name = "null";
1537
+ * @example
1538
+ * const vue = new Utils.Vue();
1539
+ * const reactive = vue.reactive({
1540
+ * name: "",
1541
+ * });
1542
+ * vue.watch(()=>reactive.name, (newValue, oldValue)=>{
1525
1543
  * console.log("newValue ==> " + newValue);
1526
1544
  * console.log("oldValue ==> " + oldValue);
1545
+ * },{
1546
+ * triggerMethod: "set",
1527
1547
  * })
1528
- * vue["name"] = "测试";
1529
- * > "测试"
1548
+ * reactive.name = "测试";
1549
+ * > newValue ==> 测试
1550
+ * > oldValue ==>
1551
+ * reactive.name = "测试";
1552
+ * > newValue ==> 测试
1553
+ * > oldValue ==> 测试
1554
+ *
1530
1555
  */
1531
1556
  Vue: typeof Vue;
1532
1557
  ModuleRaid: typeof ModuleRaid;
@@ -1,3 +1,23 @@
1
+ type ObjectWatchOptionItem = {
2
+ /**
3
+ * 是否立即执行
4
+ * @default false
5
+ */
6
+ immediate?: boolean;
7
+ /**
8
+ * 是否仅触发一次
9
+ * @default false
10
+ */
11
+ once?: boolean;
12
+ /**
13
+ * 值改变触发监听器的条件
14
+ * @default "not-same"
15
+ * @desc
16
+ * + `not-same`: 值改变时触发
17
+ * + `set`: 值被设置时触发
18
+ */
19
+ triggerMethod: "not-same" | "set";
20
+ };
1
21
  declare class RefImpl {
2
22
  _value: any;
3
23
  _isRef: boolean;
@@ -28,8 +48,11 @@ export declare class Vue {
28
48
  * 观察被reactive的对象值改变
29
49
  * @param source 被观察的对象,这里采用函数返回对象
30
50
  * @param changeCallBack 值改变的回调
51
+ * @param options 配置项
31
52
  */
32
- watch<T>(source: () => T, changeCallBack: (newValue: T | undefined, oldValue: T | undefined) => void): void;
53
+ watch<T>(source: () => T, changeCallBack: (newValue: T | undefined, oldValue: T | undefined) => void, options?: ObjectWatchOptionItem): {
54
+ unwatch: () => void;
55
+ } | undefined;
33
56
  toReactive(value: any): any;
34
57
  ref(value: any): RefImpl;
35
58
  toRef(object: any, key: any): ObjectRefImpl;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@whitesev/utils",
4
- "version": "2.10.0",
4
+ "version": "2.11.0",
5
5
  "description": "一个常用的工具库",
6
6
  "keywords": [
7
7
  "ScriptCat",
package/src/Utils.ts CHANGED
@@ -3729,14 +3729,39 @@ class Utils {
3729
3729
  /**
3730
3730
  * 自定义的动态响应对象
3731
3731
  * @example
3732
- * let vue = new Utils.Vue();
3733
- * let reactive = new vue.reactive({});
3734
- * vue.watch(()=>reactive["name"], (newValue, oldValue)=>{
3732
+ * const vue = new Utils.Vue();
3733
+ * const reactive = vue.reactive({
3734
+ * name: "",
3735
+ * });
3736
+ * vue.watch(()=>reactive.name, (newValue, oldValue)=>{
3737
+ * console.log("newValue ==> " + newValue);
3738
+ * console.log("oldValue ==> " + oldValue);
3739
+ * })
3740
+ * reactive.name = "测试";
3741
+ * > newValue ==> 测试
3742
+ * > oldValue ==>
3743
+ * reactive.name = "null";
3744
+ * > newValue ==> null
3745
+ * > oldValue ==> 测试
3746
+ * reactive.name = "null";
3747
+ * @example
3748
+ * const vue = new Utils.Vue();
3749
+ * const reactive = vue.reactive({
3750
+ * name: "",
3751
+ * });
3752
+ * vue.watch(()=>reactive.name, (newValue, oldValue)=>{
3735
3753
  * console.log("newValue ==> " + newValue);
3736
3754
  * console.log("oldValue ==> " + oldValue);
3755
+ * },{
3756
+ * triggerMethod: "set",
3737
3757
  * })
3738
- * vue["name"] = "测试";
3739
- * > "测试"
3758
+ * reactive.name = "测试";
3759
+ * > newValue ==> 测试
3760
+ * > oldValue ==>
3761
+ * reactive.name = "测试";
3762
+ * > newValue ==> 测试
3763
+ * > oldValue ==> 测试
3764
+ *
3740
3765
  */
3741
3766
  Vue = Vue;
3742
3767
  ModuleRaid = ModuleRaid;
package/src/Vue.ts CHANGED
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  const VueUtils = {
3
2
  /** 标签 */
4
3
  ReactiveFlags: {
@@ -34,16 +33,39 @@ const VueUtils = {
34
33
  },
35
34
  };
36
35
 
36
+ type ObjectWatchOptionItem = {
37
+ /**
38
+ * 是否立即执行
39
+ * @default false
40
+ */
41
+ immediate?: boolean;
42
+ /**
43
+ * 是否仅触发一次
44
+ * @default false
45
+ */
46
+ once?: boolean;
47
+ /**
48
+ * 值改变触发监听器的条件
49
+ * @default "not-same"
50
+ * @desc
51
+ * + `not-same`: 值改变时触发
52
+ * + `set`: 值被设置时触发
53
+ */
54
+ triggerMethod: "not-same" | "set";
55
+ };
56
+
37
57
  class ReactiveEffect {
38
58
  deps: any[] = [];
39
- private active = true;
40
- private fn;
41
- private scheduler;
42
- constructor(fn: (...args: any[]) => any, scheduler: any) {
59
+ active = true;
60
+ fn;
61
+ scheduler;
62
+ options;
63
+ constructor(fn: (...args: any[]) => any, scheduler: any, options: ObjectWatchOptionItem) {
43
64
  this.fn = fn;
44
65
  this.scheduler = scheduler;
66
+ this.options = options; // 默认值为'same'
45
67
  }
46
- run(cb: (activeEffect: any) => void) {
68
+ run(cb?: (activeEffect: any) => void) {
47
69
  if (!this.active) {
48
70
  this.fn();
49
71
  }
@@ -58,6 +80,18 @@ class ReactiveEffect {
58
80
  }
59
81
  }
60
82
  }
83
+ stop() {
84
+ if (this.active) {
85
+ // 清除依赖关系
86
+ if (this.deps && this.deps.length) {
87
+ this.deps.forEach((dep: any) => {
88
+ dep.delete(this);
89
+ });
90
+ this.deps.length = 0;
91
+ }
92
+ this.active = false;
93
+ }
94
+ }
61
95
  }
62
96
  class RefImpl {
63
97
  _value;
@@ -127,9 +161,7 @@ export class Vue {
127
161
  set(target, key, value, receiver) {
128
162
  const oldValue = target[key as keyof T];
129
163
  const result = Reflect.set(target, key, value, receiver);
130
- if (oldValue !== value) {
131
- that.trigger(target, "set", key, oldValue, value);
132
- }
164
+ that.trigger(target, "set", key, oldValue, value);
133
165
  return result;
134
166
  },
135
167
  });
@@ -140,8 +172,13 @@ export class Vue {
140
172
  * 观察被reactive的对象值改变
141
173
  * @param source 被观察的对象,这里采用函数返回对象
142
174
  * @param changeCallBack 值改变的回调
175
+ * @param options 配置项
143
176
  */
144
- watch<T>(source: () => T, changeCallBack: (newValue: T | undefined, oldValue: T | undefined) => void) {
177
+ watch<T>(
178
+ source: () => T,
179
+ changeCallBack: (newValue: T | undefined, oldValue: T | undefined) => void,
180
+ options?: ObjectWatchOptionItem
181
+ ) {
145
182
  let getter;
146
183
  if (VueUtils.isReactive(source)) {
147
184
  getter = () => this.traversal(source);
@@ -151,17 +188,35 @@ export class Vue {
151
188
  return;
152
189
  }
153
190
  let oldValue: any;
191
+ const unwatch = () => {
192
+ effect.stop();
193
+ };
154
194
  const job = () => {
155
195
  const newValue = effect.run((activeEffect) => {
156
196
  this.activeEffect = activeEffect;
157
197
  });
158
198
  changeCallBack(newValue, oldValue);
199
+ if (options?.once) {
200
+ // 仅触发一次
201
+ unwatch();
202
+ }
159
203
  oldValue = newValue;
160
204
  };
161
- const effect = new ReactiveEffect(getter, job);
205
+ const effect = new ReactiveEffect(getter, job, {
206
+ triggerMethod: "not-same",
207
+ ...(options ?? {}),
208
+ });
162
209
  oldValue = effect.run((activeEffect) => {
163
210
  this.activeEffect = activeEffect;
164
211
  });
212
+ if (options) {
213
+ if (options.immediate) {
214
+ job();
215
+ }
216
+ }
217
+ return {
218
+ unwatch,
219
+ };
165
220
  }
166
221
  toReactive(value: any) {
167
222
  return VueUtils.isObject(value) ? this.reactive(value) : value;
@@ -179,26 +234,42 @@ export class Vue {
179
234
  }
180
235
  return result;
181
236
  }
182
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
183
- private trigger(target: any, type: string, key: string | symbol, oldValue: any, value: any) {
237
+ private trigger(target: any, type: "set", key: string | symbol, oldValue: any, value: any) {
184
238
  const depsMap = this.targetMap.get(target);
185
239
  if (!depsMap) return;
186
240
  const effects = depsMap.get(key);
187
- this.triggerEffect(effects, "effects");
241
+ this.triggerEffect(effects, type, "effects", oldValue, value);
188
242
  }
189
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
190
- private triggerEffect(effects: any[], name: string) {
243
+ private triggerEffect(
244
+ effects: (typeof ReactiveEffect)["prototype"][],
245
+ _type: "set",
246
+ _name: string,
247
+ oldValue: any,
248
+ value: any
249
+ ) {
191
250
  if (effects) {
251
+ const isSame = oldValue === value;
192
252
  effects.forEach((effect) => {
193
- if (effect.scheduler) {
194
- effect.scheduler();
195
- } else {
196
- effect.run();
253
+ if (effect.options.triggerMethod === "not-same") {
254
+ if (isSame) {
255
+ return;
256
+ }
257
+ if (effect.scheduler) {
258
+ effect.scheduler();
259
+ } else {
260
+ effect.run();
261
+ }
262
+ } else if (effect.options.triggerMethod === "set") {
263
+ if (effect.scheduler) {
264
+ effect.scheduler();
265
+ } else {
266
+ effect.run();
267
+ }
197
268
  }
198
269
  });
199
270
  }
200
271
  }
201
- private track(target: WeakKey, type: string, key: string | symbol) {
272
+ private track(target: WeakKey, _type: "get", key: string | symbol) {
202
273
  if (!this.activeEffect) return;
203
274
  let depsMap = this.targetMap.get(target);
204
275
  if (!depsMap) {