@whitesev/utils 2.10.0 → 2.11.1

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;
@@ -1592,8 +1617,9 @@ declare class Utils {
1592
1617
  }) => any;
1593
1618
  /**
1594
1619
  * 判断页面中是否存在`worker-src`的CSP规则
1620
+ * @param timeout 超时时间,默认为`1500ms`
1595
1621
  */
1596
- hasWorkerCSP(): Promise<boolean>;
1622
+ hasWorkerCSP(timeout?: number): Promise<boolean>;
1597
1623
  }
1598
1624
  declare const utils: Utils;
1599
1625
  export { utils as Utils };
@@ -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.1",
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;
@@ -3843,10 +3868,13 @@ class Utils {
3843
3868
  }
3844
3869
  /**
3845
3870
  * 判断页面中是否存在`worker-src`的CSP规则
3871
+ * @param timeout 超时时间,默认为`1500ms`
3846
3872
  */
3847
- hasWorkerCSP() {
3873
+ hasWorkerCSP(timeout: number = 1500) {
3848
3874
  return new Promise<boolean>((resolve) => {
3849
3875
  let flag = true;
3876
+ let timeId: number | undefined = void 0;
3877
+ let worker: Worker | undefined = void 0;
3850
3878
  let workerBlobUrl: string | undefined = void 0;
3851
3879
 
3852
3880
  const workerJs = /*js*/ `
@@ -3863,11 +3891,26 @@ class Utils {
3863
3891
  }
3864
3892
  );
3865
3893
  })();`;
3894
+ /**
3895
+ * 返回结果
3896
+ */
3897
+ const finishCallBack = () => {
3898
+ clearTimeout(timeId);
3899
+ if (worker != null) {
3900
+ worker.terminate();
3901
+ }
3902
+ // 释放
3903
+ if (typeof workerBlobUrl === "string") {
3904
+ globalThis.URL.revokeObjectURL(workerBlobUrl);
3905
+ workerBlobUrl = void 0;
3906
+ }
3907
+ resolve(flag);
3908
+ };
3866
3909
  try {
3867
3910
  const workerScript = new Blob([workerJs], {
3868
3911
  type: "application/javascript",
3869
3912
  });
3870
- workerBlobUrl = window.URL.createObjectURL(workerScript);
3913
+ workerBlobUrl = globalThis.URL.createObjectURL(workerScript);
3871
3914
  // @ts-expect-error
3872
3915
  if (globalThis.trustedTypes && typeof globalThis.trustedTypes.createPolicy === "function") {
3873
3916
  // 使用这个后虽然不报错,但是仍会有blob错误
@@ -3879,23 +3922,25 @@ class Utils {
3879
3922
  });
3880
3923
  workerBlobUrl = workerPolicy.createScriptURL(workerBlobUrl);
3881
3924
  }
3882
- const worker = new Worker(workerBlobUrl!);
3925
+ worker = new Worker(workerBlobUrl!);
3883
3926
  worker.onmessage = (data) => {
3884
3927
  if (data.data.success) {
3885
3928
  flag = false;
3929
+ finishCallBack();
3886
3930
  }
3887
3931
  };
3888
- setTimeout(() => {
3889
- worker.terminate();
3890
- resolve(flag);
3891
- }, 500);
3932
+ timeId = setTimeout(() => {
3933
+ finishCallBack();
3934
+ }, timeout);
3892
3935
  worker.postMessage("test");
3893
3936
  } catch {
3894
3937
  flag = true;
3938
+ finishCallBack();
3895
3939
  } finally {
3896
3940
  // 释放
3897
3941
  if (typeof workerBlobUrl === "string") {
3898
3942
  globalThis.URL.revokeObjectURL(workerBlobUrl);
3943
+ workerBlobUrl = void 0;
3899
3944
  }
3900
3945
  }
3901
3946
  });
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) {