ivue 2.3.0 → 2.4.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.
- package/dist/Reactive.d.ts +8 -4
- package/dist/index.cjs +1 -1
- package/dist/index.es.js +56 -54
- package/lib/Reactive.ts +32 -22
- package/lib/__tests__/Reactive.vitest.spec.ts +58 -0
- package/lib/ivue.ts +19 -0
- package/package.json +2 -1
- package/skills/ivue/SKILL.md +25 -3
package/dist/Reactive.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export declare type VuePropsWithDefaults<T extends VuePropsObject> = {
|
|
|
32
32
|
* @param val Any value
|
|
33
33
|
* @returns boolean If it's a JavaScript Class returns true
|
|
34
34
|
*/
|
|
35
|
-
export declare
|
|
35
|
+
export declare function isClass(val: any): boolean;
|
|
36
36
|
/**
|
|
37
37
|
* Creates props with defaults in defineComponent() style.
|
|
38
38
|
*
|
|
@@ -64,7 +64,7 @@ export declare const isClass: (val: any) => boolean;
|
|
|
64
64
|
* @param customCloner Optional cloner used for object/array defaults (defaults to structuredClone)
|
|
65
65
|
* @returns Props declared in defineComponent() style with all properties having default property declared.
|
|
66
66
|
*/
|
|
67
|
-
export declare
|
|
67
|
+
export declare function propsWithDefaults<T extends VuePropsObject>(defaults: Record<string, any>, typedProps: T, customCloner?: (val: any) => any): VuePropsWithDefaults<T>;
|
|
68
68
|
/**
|
|
69
69
|
* Type Utilities
|
|
70
70
|
*/
|
|
@@ -84,8 +84,12 @@ export declare type ReactiveInstance<T> = T & WritableGetters<T> & {
|
|
|
84
84
|
$watch: typeof watch;
|
|
85
85
|
/** Register a watchEffect in the instance's lazy effect scope (same signature as Vue `watchEffect`). */
|
|
86
86
|
$watchEffect: typeof watchEffect;
|
|
87
|
-
/** Stop the instance's effect scope and drop cached cells
|
|
88
|
-
|
|
87
|
+
/** Stop the instance's effect scope and drop cached cells (the next
|
|
88
|
+
* touch re-materializes). `{ reset: false }` stops watchers only —
|
|
89
|
+
* every cached cell survives with its current value. */
|
|
90
|
+
$stopEffects: (options?: {
|
|
91
|
+
reset?: boolean;
|
|
92
|
+
}) => void;
|
|
89
93
|
};
|
|
90
94
|
export declare type ReactiveClass<C extends new (...args: any) => any> = {
|
|
91
95
|
[Key in keyof C]: C[Key];
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("vue"),g=Object.hasOwn,y=Object.getPrototypeOf,w=Object.getOwnPropertyDescriptor,m=Object.getOwnPropertyNames,a=Object.defineProperty,d=Object.prototype,b=Symbol.for("ivue.raw"),p=Symbol.for("ivue.scope"),h=Symbol.for("ivue.processed");function
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("vue"),g=Object.hasOwn,y=Object.getPrototypeOf,w=Object.getOwnPropertyDescriptor,m=Object.getOwnPropertyNames,a=Object.defineProperty,d=Object.prototype,b=Symbol.for("ivue.raw"),p=Symbol.for("ivue.scope"),h=Symbol.for("ivue.processed");function i(e){const c=l.toRaw(e);if(c!==e)return c[b]??(c[b]=c);const r=e[b];return r?r===e?r:l.toRaw(r):e[b]=e}function O(e,c,r,o){a(e,c,{configurable:!0,enumerable:!1,get(){const t=i(this);return t[r]??(t[r]=o.bind(t))},set(t){i(this)[r]=t}})}function S(e,c,r,o,t){const n=c[0]==="$";a(e,c,{configurable:!0,enumerable:!1,get:function(){const u=i(this);if(r in u)return u[r];const f=o.call(u);return n?(u[r]=f,f):(l.isRef(f)?u[r]=f:a(e,c,{configurable:!0,enumerable:!1,get(){return o.call(i(this))},set:t?function(s){return t.call(i(this),s)}:void 0}),f)},set:t?function(u){return t.call(i(this),u)}:void 0})}function v(e){var c;return typeof e=="function"&&!!e.prototype&&!((c=w(e,"prototype"))!=null&&c.writable)}exports.Reactive=function(e){const c=[];let r=e.prototype;for(;r&&r!==d;)c.push(r),r=y(r);c.reverse();for(const o of c){if(g(o,h))continue;const t=m(o),n=[];for(const u of t){if(u==="constructor")continue;const f=w(o,u);if(typeof f.value=="function"){const s=Symbol(u);n.push(s),O(o,u,s,f.value)}else if(f.get){const s=Symbol(u);n.push(s),S(o,u,s,f.get,f.set)}}a(o,h,{value:n})}return g(e.prototype,"$stopEffects")||(a(e.prototype,"$watch",{enumerable:!1,configurable:!0,writable:!0,value:function(...o){const t=i(this);return(t[p]??(t[p]=l.effectScope(!0))).run(()=>l.watch(...o))}}),a(e.prototype,"$watchEffect",{enumerable:!1,configurable:!0,writable:!0,value:function(...o){const t=i(this);return(t[p]??(t[p]=l.effectScope(!0))).run(()=>l.watchEffect(...o))}}),a(e.prototype,"$stopEffects",{enumerable:!1,configurable:!0,writable:!0,value:function(o){const t=i(this);try{const n=t[p];n&&n.stop()}finally{if(delete t[p],(o==null?void 0:o.reset)!==!1){let n=y(t);for(;n&&n!==d;){const u=n[h];if(u)for(const f of u)delete t[f];n=y(n)}}}}})),e},exports.isClass=v,exports.propsWithDefaults=function(e,c,r){const o={};for(const t in c){const n=e==null?void 0:e[t],u=c[t];o[t]={...u},u.required||n===void 0||(typeof n=="object"&&n!==null?o[t].default=()=>r?r(n):structuredClone(n):v(n)?o[t].default=()=>n:o[t].default=n)}return o};
|
package/dist/index.es.js
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
import { effectScope as h, watch as w, watchEffect as O, toRaw as g, isRef as j } from "vue";
|
|
2
2
|
const m = Object.hasOwn, b = Object.getPrototypeOf, d = Object.getOwnPropertyDescriptor, S = Object.getOwnPropertyNames, a = Object.defineProperty, v = Object.prototype, p = Symbol.for("ivue.raw"), l = Symbol.for("ivue.scope"), y = Symbol.for("ivue.processed");
|
|
3
3
|
function f(e) {
|
|
4
|
-
const
|
|
5
|
-
if (
|
|
6
|
-
return
|
|
7
|
-
const
|
|
8
|
-
return
|
|
4
|
+
const c = g(e);
|
|
5
|
+
if (c !== e)
|
|
6
|
+
return c[p] ?? (c[p] = c);
|
|
7
|
+
const r = e[p];
|
|
8
|
+
return r ? r === e ? r : g(r) : e[p] = e;
|
|
9
9
|
}
|
|
10
|
-
function $(e,
|
|
11
|
-
a(e,
|
|
10
|
+
function $(e, c, r, o) {
|
|
11
|
+
a(e, c, { configurable: !0, enumerable: !1, get() {
|
|
12
12
|
const t = f(this);
|
|
13
|
-
return t[
|
|
13
|
+
return t[r] ?? (t[r] = o.bind(t));
|
|
14
14
|
}, set(t) {
|
|
15
|
-
f(this)[
|
|
15
|
+
f(this)[r] = t;
|
|
16
16
|
} });
|
|
17
17
|
}
|
|
18
|
-
function E(e,
|
|
19
|
-
const
|
|
20
|
-
a(e,
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
return
|
|
24
|
-
const u = o.call(
|
|
25
|
-
return
|
|
18
|
+
function E(e, c, r, o, t) {
|
|
19
|
+
const n = c[0] === "$";
|
|
20
|
+
a(e, c, { configurable: !0, enumerable: !1, get: function() {
|
|
21
|
+
const s = f(this);
|
|
22
|
+
if (r in s)
|
|
23
|
+
return s[r];
|
|
24
|
+
const u = o.call(s);
|
|
25
|
+
return n ? (s[r] = u, u) : (j(u) ? s[r] = u : a(e, c, { configurable: !0, enumerable: !1, get() {
|
|
26
26
|
return o.call(f(this));
|
|
27
27
|
}, set: t ? function(i) {
|
|
28
28
|
return t.call(f(this), i);
|
|
29
29
|
} : void 0 }), u);
|
|
30
|
-
}, set: t ? function(
|
|
31
|
-
return t.call(f(this),
|
|
30
|
+
}, set: t ? function(s) {
|
|
31
|
+
return t.call(f(this), s);
|
|
32
32
|
} : void 0 });
|
|
33
33
|
}
|
|
34
34
|
function C(e) {
|
|
35
|
-
const
|
|
36
|
-
let
|
|
37
|
-
for (;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
for (const o of
|
|
35
|
+
const c = [];
|
|
36
|
+
let r = e.prototype;
|
|
37
|
+
for (; r && r !== v; )
|
|
38
|
+
c.push(r), r = b(r);
|
|
39
|
+
c.reverse();
|
|
40
|
+
for (const o of c) {
|
|
41
41
|
if (m(o, y))
|
|
42
42
|
continue;
|
|
43
|
-
const t = S(o),
|
|
44
|
-
for (const
|
|
45
|
-
if (
|
|
43
|
+
const t = S(o), n = [];
|
|
44
|
+
for (const s of t) {
|
|
45
|
+
if (s === "constructor")
|
|
46
46
|
continue;
|
|
47
|
-
const u = d(o,
|
|
47
|
+
const u = d(o, s);
|
|
48
48
|
if (typeof u.value == "function") {
|
|
49
|
-
const i = Symbol(
|
|
50
|
-
|
|
49
|
+
const i = Symbol(s);
|
|
50
|
+
n.push(i), $(o, s, i, u.value);
|
|
51
51
|
} else if (u.get) {
|
|
52
|
-
const i = Symbol(
|
|
53
|
-
|
|
52
|
+
const i = Symbol(s);
|
|
53
|
+
n.push(i), E(o, s, i, u.get, u.set);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
a(o, y, { value:
|
|
56
|
+
a(o, y, { value: n });
|
|
57
57
|
}
|
|
58
58
|
return m(e.prototype, "$stopEffects") || (a(e.prototype, "$watch", { enumerable: !1, configurable: !0, writable: !0, value: function(...o) {
|
|
59
59
|
const t = f(this);
|
|
@@ -61,35 +61,37 @@ function C(e) {
|
|
|
61
61
|
} }), a(e.prototype, "$watchEffect", { enumerable: !1, configurable: !0, writable: !0, value: function(...o) {
|
|
62
62
|
const t = f(this);
|
|
63
63
|
return (t[l] ?? (t[l] = h(!0))).run(() => O(...o));
|
|
64
|
-
} }), a(e.prototype, "$stopEffects", { enumerable: !1, configurable: !0, writable: !0, value: function() {
|
|
65
|
-
const
|
|
64
|
+
} }), a(e.prototype, "$stopEffects", { enumerable: !1, configurable: !0, writable: !0, value: function(o) {
|
|
65
|
+
const t = f(this);
|
|
66
66
|
try {
|
|
67
|
-
const
|
|
68
|
-
|
|
67
|
+
const n = t[l];
|
|
68
|
+
n && n.stop();
|
|
69
69
|
} finally {
|
|
70
|
-
delete
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
if (delete t[l], (o == null ? void 0 : o.reset) !== !1) {
|
|
71
|
+
let n = b(t);
|
|
72
|
+
for (; n && n !== v; ) {
|
|
73
|
+
const s = n[y];
|
|
74
|
+
if (s)
|
|
75
|
+
for (const u of s)
|
|
76
|
+
delete t[u];
|
|
77
|
+
n = b(n);
|
|
78
|
+
}
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
} })), e;
|
|
81
82
|
}
|
|
82
|
-
|
|
83
|
-
var
|
|
84
|
-
return typeof e == "function" && !!e.prototype && !((
|
|
85
|
-
}
|
|
83
|
+
function P(e) {
|
|
84
|
+
var c;
|
|
85
|
+
return typeof e == "function" && !!e.prototype && !((c = d(e, "prototype")) != null && c.writable);
|
|
86
|
+
}
|
|
87
|
+
function D(e, c, r) {
|
|
86
88
|
const o = {};
|
|
87
|
-
for (const t in
|
|
88
|
-
const
|
|
89
|
-
o[t] = { ...
|
|
89
|
+
for (const t in c) {
|
|
90
|
+
const n = e == null ? void 0 : e[t], s = c[t];
|
|
91
|
+
o[t] = { ...s }, s.required || n === void 0 || (typeof n == "object" && n !== null ? o[t].default = () => r ? r(n) : structuredClone(n) : P(n) ? o[t].default = () => n : o[t].default = n);
|
|
90
92
|
}
|
|
91
93
|
return o;
|
|
92
|
-
}
|
|
94
|
+
}
|
|
93
95
|
export {
|
|
94
96
|
C as Reactive,
|
|
95
97
|
P as isClass,
|
package/lib/Reactive.ts
CHANGED
|
@@ -257,15 +257,19 @@ export function Reactive<C extends new (...args: any) => any>(
|
|
|
257
257
|
|
|
258
258
|
/**
|
|
259
259
|
* Tear down the instance: stop its effect scope (any watchers created via
|
|
260
|
-
* $watch) and drop all cached cells so refs/computeds become collectable
|
|
261
|
-
*
|
|
262
|
-
*
|
|
260
|
+
* $watch) and drop all cached cells so refs/computeds become collectable —
|
|
261
|
+
* the next touch re-materializes fresh cells (disposal is a reset).
|
|
262
|
+
* Pass { reset: false } to stop the watchers ONLY: every cached cell
|
|
263
|
+
* survives with its current value, and the instance can $watch again in a
|
|
264
|
+
* fresh scope. No hooks — ivue never calls user code; compose richer
|
|
265
|
+
* cleanup as an ordinary method that does its own work and then calls
|
|
266
|
+
* $stopEffects().
|
|
263
267
|
*/
|
|
264
268
|
defineProperty(targetClass.prototype, '$stopEffects', {
|
|
265
269
|
enumerable: false,
|
|
266
270
|
configurable: true,
|
|
267
271
|
writable: true,
|
|
268
|
-
value: function (this: any) {
|
|
272
|
+
value: function (this: any, options?: { reset?: boolean }) {
|
|
269
273
|
const raw = resolveRaw(this);
|
|
270
274
|
try {
|
|
271
275
|
const scope = raw[SCOPE];
|
|
@@ -274,18 +278,22 @@ export function Reactive<C extends new (...args: any) => any>(
|
|
|
274
278
|
// SCOPE is ivue-owned but is not a method/getter cache key.
|
|
275
279
|
delete raw[SCOPE];
|
|
276
280
|
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
281
|
+
// { reset: false } = stop watchers only; cells keep their
|
|
282
|
+
// values (a `return` here would swallow a throwing stop()).
|
|
283
|
+
if (options?.reset !== false) {
|
|
284
|
+
// Each processed prototype's PROCESSED marker carries the
|
|
285
|
+
// symbols it may cache on an instance. Walk Child -> Base and
|
|
286
|
+
// remove only those known keys.
|
|
287
|
+
let prototype = getPrototypeOf(raw);
|
|
288
|
+
while (prototype && prototype !== objectPrototype) {
|
|
289
|
+
const cacheKeys = prototype[PROCESSED] as
|
|
290
|
+
| readonly symbol[]
|
|
291
|
+
| undefined;
|
|
292
|
+
if (cacheKeys) {
|
|
293
|
+
for (const cacheKey of cacheKeys) delete raw[cacheKey];
|
|
294
|
+
}
|
|
295
|
+
prototype = getPrototypeOf(prototype);
|
|
287
296
|
}
|
|
288
|
-
prototype = getPrototypeOf(prototype);
|
|
289
297
|
}
|
|
290
298
|
}
|
|
291
299
|
},
|
|
@@ -321,7 +329,7 @@ export type VuePropsWithDefaults<T extends VuePropsObject> = {
|
|
|
321
329
|
* @param val Any value
|
|
322
330
|
* @returns boolean If it's a JavaScript Class returns true
|
|
323
331
|
*/
|
|
324
|
-
export
|
|
332
|
+
export function isClass(val: any): boolean {
|
|
325
333
|
if (typeof val !== 'function') return false; // Not a function, so not a class function either
|
|
326
334
|
|
|
327
335
|
if (!val.prototype) return false; // Arrow function, so not a class
|
|
@@ -333,7 +341,7 @@ export const isClass = (val: any): boolean => {
|
|
|
333
341
|
} else {
|
|
334
342
|
return true; // Class -> Not a function
|
|
335
343
|
}
|
|
336
|
-
}
|
|
344
|
+
}
|
|
337
345
|
/**
|
|
338
346
|
* Creates props with defaults in defineComponent() style.
|
|
339
347
|
*
|
|
@@ -365,12 +373,12 @@ export const isClass = (val: any): boolean => {
|
|
|
365
373
|
* @param customCloner Optional cloner used for object/array defaults (defaults to structuredClone)
|
|
366
374
|
* @returns Props declared in defineComponent() style with all properties having default property declared.
|
|
367
375
|
*/
|
|
368
|
-
export
|
|
376
|
+
export function propsWithDefaults<T extends VuePropsObject>(
|
|
369
377
|
defaults: Record<string, any>,
|
|
370
378
|
typedProps: T,
|
|
371
379
|
// Optional: Allows user to pass a custom cloner if structuredClone isn't enough
|
|
372
380
|
customCloner?: (val: any) => any,
|
|
373
|
-
): VuePropsWithDefaults<T>
|
|
381
|
+
): VuePropsWithDefaults<T> {
|
|
374
382
|
// NON-MUTATING: descriptor objects are routinely SHARED between props
|
|
375
383
|
// maps (`{ ...baseParamsTypes, extra }` — the spread copies the outer
|
|
376
384
|
// object but every inner `{ type }` descriptor stays the same reference).
|
|
@@ -396,7 +404,7 @@ export const propsWithDefaults = <T extends VuePropsObject>(
|
|
|
396
404
|
}
|
|
397
405
|
}
|
|
398
406
|
return result as VuePropsWithDefaults<T>;
|
|
399
|
-
}
|
|
407
|
+
}
|
|
400
408
|
|
|
401
409
|
/**
|
|
402
410
|
* Type Utilities
|
|
@@ -430,8 +438,10 @@ export type ReactiveInstance<T> = T &
|
|
|
430
438
|
$watch: typeof watch;
|
|
431
439
|
/** Register a watchEffect in the instance's lazy effect scope (same signature as Vue `watchEffect`). */
|
|
432
440
|
$watchEffect: typeof watchEffect;
|
|
433
|
-
/** Stop the instance's effect scope and drop cached cells
|
|
434
|
-
|
|
441
|
+
/** Stop the instance's effect scope and drop cached cells (the next
|
|
442
|
+
* touch re-materializes). `{ reset: false }` stops watchers only —
|
|
443
|
+
* every cached cell survives with its current value. */
|
|
444
|
+
$stopEffects: (options?: { reset?: boolean }) => void;
|
|
435
445
|
};
|
|
436
446
|
|
|
437
447
|
export type ReactiveClass<C extends new (...args: any) => any> = {
|
|
@@ -735,6 +735,64 @@ describe('Reactive()', () => {
|
|
|
735
735
|
expect(() => instance.$stopEffects()).not.toThrow();
|
|
736
736
|
});
|
|
737
737
|
|
|
738
|
+
it('{ reset: false } stops watchers but every cached cell survives', async () => {
|
|
739
|
+
class Session {
|
|
740
|
+
get counter() {
|
|
741
|
+
return ref(0);
|
|
742
|
+
}
|
|
743
|
+
get doubled() {
|
|
744
|
+
return computed(() => (this as any).counter.value * 2);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
const instance: any = new (Reactive(Session))();
|
|
748
|
+
let observed = 0;
|
|
749
|
+
instance.$watchEffect(() => {
|
|
750
|
+
observed = instance.doubled.value; // first-touches the computed IN the scope
|
|
751
|
+
});
|
|
752
|
+
const counterCell = instance.counter;
|
|
753
|
+
const doubledCell = instance.doubled;
|
|
754
|
+
instance.counter.value = 21;
|
|
755
|
+
await nextTick();
|
|
756
|
+
expect(observed).toBe(42);
|
|
757
|
+
|
|
758
|
+
instance.$stopEffects({ reset: false });
|
|
759
|
+
|
|
760
|
+
// watchers are dead…
|
|
761
|
+
instance.counter.value = 100;
|
|
762
|
+
await nextTick();
|
|
763
|
+
expect(observed).toBe(42);
|
|
764
|
+
// …but the cells survive with their CURRENT values (no re-init)
|
|
765
|
+
expect(instance.counter).toBe(counterCell);
|
|
766
|
+
expect(instance.doubled).toBe(doubledCell);
|
|
767
|
+
expect(instance.counter.value).toBe(100);
|
|
768
|
+
// the surviving computed still evaluates (pull-based past scope death)
|
|
769
|
+
expect(instance.doubled.value).toBe(200);
|
|
770
|
+
|
|
771
|
+
// and a SECOND life works: a fresh scope tracks the old cells
|
|
772
|
+
let observedAgain = 0;
|
|
773
|
+
instance.$watch(
|
|
774
|
+
() => instance.doubled.value,
|
|
775
|
+
(doubledValue: number) => {
|
|
776
|
+
observedAgain = doubledValue;
|
|
777
|
+
},
|
|
778
|
+
);
|
|
779
|
+
instance.counter.value = 7;
|
|
780
|
+
await nextTick();
|
|
781
|
+
expect(observedAgain).toBe(14);
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
it('default call still resets — cells re-initialize after teardown', () => {
|
|
785
|
+
class Session {
|
|
786
|
+
get counter() {
|
|
787
|
+
return ref(0);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
const instance: any = new (Reactive(Session))();
|
|
791
|
+
instance.counter.value = 41;
|
|
792
|
+
instance.$stopEffects();
|
|
793
|
+
expect(instance.counter.value).toBe(0);
|
|
794
|
+
});
|
|
795
|
+
|
|
738
796
|
it('$stopEffects is injected only once (idempotent re-Reactive)', () => {
|
|
739
797
|
class Store {
|
|
740
798
|
m() {
|
package/lib/ivue.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* DEPRECATED — ivue v1 (the reactive-proxy engine)
|
|
4
|
+
* ============================================================
|
|
5
|
+
*
|
|
6
|
+
* This file is the ORIGINAL v1 implementation: `ivue(Class, ...args)`
|
|
7
|
+
* wraps instances in `reactive()` and converts accessors to computeds.
|
|
8
|
+
* It is kept in-tree for reference and migration only.
|
|
9
|
+
*
|
|
10
|
+
* v2 is `Reactive()` in ./Reactive.ts (the package's main entry) — a
|
|
11
|
+
* one-time prototype transform: instances stay plain objects, plain
|
|
12
|
+
* getters cost zero bytes, creation measures 55-253x faster. New code
|
|
13
|
+
* must never import from this file.
|
|
14
|
+
*
|
|
15
|
+
* Migration guide: https://ivue.dev/guide/standard
|
|
16
|
+
*/
|
|
17
|
+
|
|
1
18
|
import type { ComputedRef, ExtractPropTypes, Ref, ToRef } from 'vue';
|
|
2
19
|
import { computed, markRaw, reactive, ref, shallowRef, toRef } from 'vue';
|
|
3
20
|
import type { Ref as DemiRef } from 'vue-demi';
|
|
@@ -399,6 +416,8 @@ const getClassPropertiesAccessorsMap = (obj: object): PropsMapValue => {
|
|
|
399
416
|
* get getter () { return 'hello world'; }
|
|
400
417
|
* }
|
|
401
418
|
*
|
|
419
|
+
* @deprecated v1 engine — use `Reactive()` from the main `ivue` entry
|
|
420
|
+
* instead; see https://ivue.dev/guide/standard for the migration.
|
|
402
421
|
* @param className Any Class
|
|
403
422
|
* @param args Class constructor arguments that you would pass to a `new AnyClass(args...)`
|
|
404
423
|
* @returns {IVue<T>}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ivue",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Infinite Vue – Class Based Architecture for Vue 3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"render:form-header": "node docs_v2/scripts/brand-image-generator.mjs form-header",
|
|
48
48
|
"render:banner": "node docs_v2/scripts/brand-image-generator.mjs blog",
|
|
49
49
|
"render:diagram": "node docs_v2/scripts/brand-image-generator.mjs diagram",
|
|
50
|
+
"render:page-og": "node docs_v2/scripts/page-og-generator.mjs",
|
|
50
51
|
"render:embeds": "node docs_v2/scripts/blog-embed-shots.mjs",
|
|
51
52
|
"render:code-shots": "node docs_v2/scripts/blog-code-shots.mjs",
|
|
52
53
|
"sync:blog-dates": "node docs_v2/scripts/blog-dates-generator.mjs",
|
package/skills/ivue/SKILL.md
CHANGED
|
@@ -387,14 +387,32 @@ class $Session {
|
|
|
387
387
|
// Outliving instance: $watch/$watchEffect register in the
|
|
388
388
|
// instance's lazy effectScope — there is no component scope here
|
|
389
389
|
// to reap plain watch.
|
|
390
|
+
// WATCHERS live behind a method, not inline in the constructor —
|
|
391
|
+
// the constructor calls it once, and the instance can RESTART its
|
|
392
|
+
// watchers after a keep-state stop (see suspend() below).
|
|
390
393
|
constructor() {
|
|
394
|
+
this.startWatchers();
|
|
395
|
+
// If constructed INSIDE some scope, auto-wire teardown instead:
|
|
396
|
+
// getCurrentScope() && onScopeDispose(() => this.$stopEffects());
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
startWatchers() {
|
|
391
400
|
this.$watch(
|
|
392
401
|
() => this.user.value,
|
|
393
402
|
(user, previousUser) => this.onUserChanged(user, previousUser),
|
|
394
403
|
);
|
|
395
404
|
this.$watchEffect(() => this.persist());
|
|
396
|
-
|
|
397
|
-
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// SUSPEND / RESUME: { reset: false } stops the watchers ONLY — every
|
|
408
|
+
// cached cell survives with its current value. startWatchers() in a
|
|
409
|
+
// fresh scope resumes. (Default $stopEffects() also CLEARS the cells:
|
|
410
|
+
// the next touch re-runs initializers — disposal is a reset.)
|
|
411
|
+
suspend() {
|
|
412
|
+
this.$stopEffects({ reset: false });
|
|
413
|
+
}
|
|
414
|
+
resume() {
|
|
415
|
+
this.startWatchers();
|
|
398
416
|
}
|
|
399
417
|
|
|
400
418
|
// CLEANUP composes as an ORDINARY method — no hooks, no reserved
|
|
@@ -490,7 +508,11 @@ until mount — use `?.` in watch getters).
|
|
|
490
508
|
its leaf reads subscribe directly (non-intuitive but structural).
|
|
491
509
|
- The source MUST be the FUNCTION form. `watch(instance.plainGetter, cb)` passes a
|
|
492
510
|
dead snapshot and never fires.
|
|
493
|
-
- `$stopEffects()` stops the instance scope and clears cached Refs/Computeds
|
|
511
|
+
- `$stopEffects()` stops the instance scope and clears cached Refs/Computeds
|
|
512
|
+
(the next touch re-materializes — disposal is a reset);
|
|
513
|
+
`$stopEffects({ reset: false })` stops the WATCHERS only — every cached
|
|
514
|
+
cell survives with its current value, and `startWatchers()` in a fresh
|
|
515
|
+
scope resumes (the suspend/resume pattern above);
|
|
494
516
|
instances that never `$watch` allocate no scope. There are NO hooks — richer
|
|
495
517
|
cleanup is an ordinary method that does its work and then calls
|
|
496
518
|
`$stopEffects()` itself. Every outliving instance needs an OWNER that calls
|