@whitesev/utils 2.9.13 → 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;
@@ -1590,6 +1615,10 @@ declare class Utils {
1590
1615
  }) => Promise<any> : (...args: {
1591
1616
  [K in keyof P]: any;
1592
1617
  }) => any;
1618
+ /**
1619
+ * 判断页面中是否存在`worker-src`的CSP规则
1620
+ */
1621
+ hasWorkerCSP(): Promise<boolean>;
1593
1622
  }
1594
1623
  declare const utils: Utils;
1595
1624
  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.9.13",
4
+ "version": "2.11.0",
5
5
  "description": "一个常用的工具库",
6
6
  "keywords": [
7
7
  "ScriptCat",
package/src/Utils.ts CHANGED
@@ -234,8 +234,8 @@ class Utils {
234
234
  let timer: any = null as any;
235
235
  const that = this;
236
236
  return function (...args: A) {
237
- that.workerClearTimeout(timer);
238
- timer = that.workerSetTimeout(function () {
237
+ clearTimeout(timer);
238
+ timer = setTimeout(function () {
239
239
  fn.apply(that, args);
240
240
  }, delay);
241
241
  };
@@ -263,7 +263,6 @@ class Utils {
263
263
  **/
264
264
  downloadBase64(base64Data: string, fileName: string, isIFrame?: boolean): void;
265
265
  downloadBase64(base64Data: string, fileName: string, isIFrame = false) {
266
- const that = this;
267
266
  if (typeof base64Data !== "string") {
268
267
  throw new Error("Utils.downloadBase64 参数 base64Data 必须为 string 类型");
269
268
  }
@@ -276,7 +275,7 @@ class Utils {
276
275
  $iframe.style.display = "none";
277
276
  $iframe.src = base64Data;
278
277
  (this.windowApi.document.body || this.windowApi.document.documentElement).appendChild($iframe);
279
- that.workerSetTimeout(() => {
278
+ setTimeout(() => {
280
279
  $iframe!.contentWindow!.document.execCommand("SaveAs", true, fileName);
281
280
  (this.windowApi.document.body || this.windowApi.document.documentElement).removeChild($iframe);
282
281
  }, 100);
@@ -2841,7 +2840,6 @@ class Utils {
2841
2840
  **/
2842
2841
  setTimeout(callback: (() => void) | string, delayTime?: number): Promise<any>;
2843
2842
  setTimeout(callback: (() => void) | string, delayTime: number = 0): Promise<any> {
2844
- const that = this;
2845
2843
  if (typeof callback !== "function" && typeof callback !== "string") {
2846
2844
  throw new TypeError("Utils.setTimeout 参数 callback 必须为 function|string 类型");
2847
2845
  }
@@ -2849,8 +2847,8 @@ class Utils {
2849
2847
  throw new TypeError("Utils.setTimeout 参数 delayTime 必须为 number 类型");
2850
2848
  }
2851
2849
  return new Promise((resolve) => {
2852
- that.workerSetTimeout(() => {
2853
- resolve(that.tryCatch().run(callback));
2850
+ setTimeout(() => {
2851
+ resolve(this.tryCatch().run(callback));
2854
2852
  }, delayTime);
2855
2853
  });
2856
2854
  }
@@ -2862,12 +2860,11 @@ class Utils {
2862
2860
  **/
2863
2861
  sleep(delayTime?: number): Promise<void>;
2864
2862
  sleep(delayTime: number = 0): Promise<void> {
2865
- const that = this;
2866
2863
  if (typeof delayTime !== "number") {
2867
2864
  throw new Error("Utils.sleep 参数 delayTime 必须为 number 类型");
2868
2865
  }
2869
2866
  return new Promise((resolve) => {
2870
- that.workerSetTimeout(() => {
2867
+ setTimeout(() => {
2871
2868
  resolve(void 0);
2872
2869
  }, delayTime);
2873
2870
  });
@@ -3397,13 +3394,12 @@ class Utils {
3397
3394
  intervalTimer: number = 250,
3398
3395
  maxTime: number = -1
3399
3396
  ): Promise<T> {
3400
- const that = this;
3401
3397
  if (checkFn == null) {
3402
3398
  throw new TypeError("checkObj 不能为空对象 ");
3403
3399
  }
3404
3400
  let isResolve = false;
3405
3401
  return new Promise((resolve, reject) => {
3406
- const interval = that.workerSetInterval(() => {
3402
+ const interval = setInterval(() => {
3407
3403
  let inst = checkFn;
3408
3404
  if (typeof checkFn === "function") {
3409
3405
  inst = checkFn();
@@ -3416,14 +3412,14 @@ class Utils {
3416
3412
  }
3417
3413
  if ((typeof propertyName === "function" && propertyName(inst)) || Reflect.has(inst, propertyName as string)) {
3418
3414
  isResolve = true;
3419
- that.workerClearInterval(interval);
3415
+ clearInterval(interval);
3420
3416
  resolve((inst as any)[propertyName as string]);
3421
3417
  }
3422
3418
  }, intervalTimer);
3423
3419
  if (maxTime !== -1) {
3424
- that.workerSetTimeout(() => {
3420
+ setTimeout(() => {
3425
3421
  if (!isResolve) {
3426
- that.workerClearInterval(interval);
3422
+ clearInterval(interval);
3427
3423
  reject();
3428
3424
  }
3429
3425
  }, maxTime);
@@ -3733,14 +3729,39 @@ class Utils {
3733
3729
  /**
3734
3730
  * 自定义的动态响应对象
3735
3731
  * @example
3736
- * let vue = new Utils.Vue();
3737
- * let reactive = new vue.reactive({});
3738
- * 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)=>{
3739
3753
  * console.log("newValue ==> " + newValue);
3740
3754
  * console.log("oldValue ==> " + oldValue);
3755
+ * },{
3756
+ * triggerMethod: "set",
3741
3757
  * })
3742
- * vue["name"] = "测试";
3743
- * > "测试"
3758
+ * reactive.name = "测试";
3759
+ * > newValue ==> 测试
3760
+ * > oldValue ==>
3761
+ * reactive.name = "测试";
3762
+ * > newValue ==> 测试
3763
+ * > oldValue ==> 测试
3764
+ *
3744
3765
  */
3745
3766
  Vue = Vue;
3746
3767
  ModuleRaid = ModuleRaid;
@@ -3752,8 +3773,7 @@ class Utils {
3752
3773
  workerSetTimeout(callback: (...args: any[]) => any, timeout: number = 0) {
3753
3774
  try {
3754
3775
  return WorkerSetTimeout(callback, timeout);
3755
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3756
- } catch (error) {
3776
+ } catch {
3757
3777
  return this.windowApi.setTimeout(callback, timeout);
3758
3778
  }
3759
3779
  }
@@ -3766,10 +3786,7 @@ class Utils {
3766
3786
  if (timeId != null) {
3767
3787
  WorkerClearTimeout(timeId);
3768
3788
  }
3769
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3770
- } catch (error) {
3771
- // console.log(error);
3772
- } finally {
3789
+ } catch {
3773
3790
  this.windowApi.clearTimeout(timeId);
3774
3791
  }
3775
3792
  }
@@ -3781,8 +3798,7 @@ class Utils {
3781
3798
  workerSetInterval(callback: (...args: any[]) => any, timeout: number = 0) {
3782
3799
  try {
3783
3800
  return WorkerSetInterval(callback, timeout);
3784
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3785
- } catch (error) {
3801
+ } catch {
3786
3802
  return this.windowApi.setInterval(callback, timeout);
3787
3803
  }
3788
3804
  }
@@ -3795,10 +3811,7 @@ class Utils {
3795
3811
  if (timeId != null) {
3796
3812
  WorkerClearInterval(timeId);
3797
3813
  }
3798
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
3799
- } catch (error) {
3800
- // console.log(error);
3801
- } finally {
3814
+ } catch {
3802
3815
  this.windowApi.clearInterval(timeId);
3803
3816
  }
3804
3817
  }
@@ -3853,6 +3866,65 @@ class Utils {
3853
3866
  return new FunctionConstructor(...args);
3854
3867
  }
3855
3868
  }
3869
+ /**
3870
+ * 判断页面中是否存在`worker-src`的CSP规则
3871
+ */
3872
+ hasWorkerCSP() {
3873
+ return new Promise<boolean>((resolve) => {
3874
+ let flag = true;
3875
+ let workerBlobUrl: string | undefined = void 0;
3876
+
3877
+ const workerJs = /*js*/ `
3878
+ (() => {
3879
+ this.addEventListener(
3880
+ "message",
3881
+ function () {
3882
+ this.postMessage({
3883
+ success: true,
3884
+ });
3885
+ },
3886
+ {
3887
+ capture: true,
3888
+ }
3889
+ );
3890
+ })();`;
3891
+ try {
3892
+ const workerScript = new Blob([workerJs], {
3893
+ type: "application/javascript",
3894
+ });
3895
+ workerBlobUrl = window.URL.createObjectURL(workerScript);
3896
+ // @ts-expect-error
3897
+ if (globalThis.trustedTypes && typeof globalThis.trustedTypes.createPolicy === "function") {
3898
+ // 使用这个后虽然不报错,但是仍会有blob错误
3899
+ // violates the following Content Security Policy directive: "worker-src 'self'". The action has been blocked.
3900
+ // 且这个错误无法使用try/catch捕捉,导致本该提醒使用手动匹配的结果并无提醒弹窗
3901
+ // @ts-expect-error
3902
+ const workerPolicy = globalThis.trustedTypes.createPolicy("workerPolicy", {
3903
+ createScriptURL: (url: string) => url,
3904
+ });
3905
+ workerBlobUrl = workerPolicy.createScriptURL(workerBlobUrl);
3906
+ }
3907
+ const worker = new Worker(workerBlobUrl!);
3908
+ worker.onmessage = (data) => {
3909
+ if (data.data.success) {
3910
+ flag = false;
3911
+ }
3912
+ };
3913
+ setTimeout(() => {
3914
+ worker.terminate();
3915
+ resolve(flag);
3916
+ }, 500);
3917
+ worker.postMessage("test");
3918
+ } catch {
3919
+ flag = true;
3920
+ } finally {
3921
+ // 释放
3922
+ if (typeof workerBlobUrl === "string") {
3923
+ globalThis.URL.revokeObjectURL(workerBlobUrl);
3924
+ }
3925
+ }
3926
+ });
3927
+ }
3856
3928
  }
3857
3929
 
3858
3930
  const utils = new Utils();
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) {