@mintjamsinc/ichigojs 0.1.5 → 0.1.6
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/README.md +71 -16
- package/dist/ichigo.esm.js +57 -8
- package/dist/ichigo.esm.js.map +1 -1
- package/dist/ichigo.esm.min.js +1 -1
- package/dist/ichigo.esm.min.js.map +1 -1
- package/dist/ichigo.umd.js +57 -8
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/ichigo.umd.min.js.map +1 -1
- package/dist/types/ichigo/VNode.d.ts +15 -0
- package/package.json +1 -1
package/README.md
CHANGED
@@ -11,7 +11,8 @@ A simple and intuitive reactive framework. Lightweight, fast, and user-friendly
|
|
11
11
|
- ⚡ **Reactive Proxy System** - Automatic change detection without manual triggers
|
12
12
|
- 🎯 **Computed Properties** - Automatic dependency tracking and re-evaluation
|
13
13
|
- 🔄 **Two-way Binding** - `v-model` with modifiers (`.lazy`, `.number`, `.trim`)
|
14
|
-
- 🔌 **Lifecycle Hooks** - `@mount`, `@mounted`, `@update`, `@updated`, `@unmount`, `@unmounted`
|
14
|
+
- 🔌 **Lifecycle Hooks** - `@mount`, `@mounted`, `@update`, `@updated`, `@unmount`, `@unmounted` with context (`$ctx`)
|
15
|
+
- 💾 **userData Storage** - Proxy-free storage for third-party library instances with auto-cleanup
|
15
16
|
- 📦 **Lightweight** - Minimal bundle size
|
16
17
|
- 🚀 **High Performance** - Efficient batched updates via microtask queue
|
17
18
|
- 💪 **TypeScript** - Written in TypeScript with full type support
|
@@ -161,7 +162,7 @@ Supported modifiers: `.stop`, `.prevent`, `.capture`, `.self`, `.once`
|
|
161
162
|
|
162
163
|
#### Lifecycle Hooks
|
163
164
|
|
164
|
-
Lifecycle hooks allow you to run code at specific stages of an element's lifecycle
|
165
|
+
Lifecycle hooks allow you to run code at specific stages of an element's lifecycle. Each hook receives a **lifecycle context** (`$ctx`) with access to the element, VNode, and userData storage.
|
165
166
|
|
166
167
|
```html
|
167
168
|
<div v-if="show"
|
@@ -184,29 +185,83 @@ Lifecycle hooks allow you to run code at specific stages of an element's lifecyc
|
|
184
185
|
- `@unmount` - Called before the element is removed from the DOM
|
185
186
|
- `@unmounted` - Called after the element is removed from the DOM
|
186
187
|
|
187
|
-
**
|
188
|
+
**Lifecycle Context (`$ctx`):**
|
189
|
+
|
190
|
+
Every lifecycle hook receives a context object with:
|
191
|
+
|
192
|
+
- `$ctx.element` - The DOM element
|
193
|
+
- `$ctx.vnode` - The VNode instance
|
194
|
+
- `$ctx.userData` - Proxy-free storage Map for user data
|
195
|
+
|
196
|
+
**userData Storage:**
|
197
|
+
|
198
|
+
`$ctx.userData` is a safe space to store data associated with the element's lifecycle. It's not affected by Vue's reactive proxy system, making it perfect for storing third-party library instances.
|
188
199
|
|
189
200
|
```javascript
|
190
201
|
methods: {
|
191
|
-
onMounted(
|
192
|
-
// Initialize third-party library
|
193
|
-
const canvas =
|
194
|
-
|
202
|
+
onMounted($ctx) {
|
203
|
+
// Initialize third-party library
|
204
|
+
const canvas = $ctx.element.querySelector('canvas');
|
205
|
+
const chart = new Chart(canvas.getContext('2d'), {
|
206
|
+
type: 'line',
|
207
|
+
data: { /* ... */ }
|
208
|
+
});
|
209
|
+
|
210
|
+
// Store in userData (Proxy-free storage)
|
211
|
+
$ctx.userData.set('chart', chart);
|
195
212
|
},
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
213
|
+
|
214
|
+
onUpdated($ctx) {
|
215
|
+
// Retrieve from userData
|
216
|
+
const chart = $ctx.userData.get('chart');
|
217
|
+
if (chart) {
|
218
|
+
chart.data.labels = [...this.labels];
|
219
|
+
chart.update();
|
220
|
+
}
|
200
221
|
},
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
222
|
+
|
223
|
+
onUnmount($ctx) {
|
224
|
+
// Clean up before removal
|
225
|
+
const chart = $ctx.userData.get('chart');
|
226
|
+
if (chart) {
|
227
|
+
chart.destroy();
|
228
|
+
$ctx.userData.delete('chart');
|
229
|
+
}
|
206
230
|
}
|
207
231
|
}
|
208
232
|
```
|
209
233
|
|
234
|
+
**Automatic Cleanup:**
|
235
|
+
|
236
|
+
Objects with a `close()` method stored in `userData` are automatically cleaned up during the destroy phase:
|
237
|
+
|
238
|
+
```javascript
|
239
|
+
methods: {
|
240
|
+
onMounted($ctx) {
|
241
|
+
// Object with close() method
|
242
|
+
const resource = {
|
243
|
+
data: someData,
|
244
|
+
close() {
|
245
|
+
// Custom cleanup logic
|
246
|
+
console.log('Resource cleaned up');
|
247
|
+
}
|
248
|
+
};
|
249
|
+
|
250
|
+
$ctx.userData.set('myResource', resource);
|
251
|
+
// resource.close() will be called automatically on unmount
|
252
|
+
}
|
253
|
+
}
|
254
|
+
```
|
255
|
+
|
256
|
+
**Cleanup Order:**
|
257
|
+
|
258
|
+
1. `@unmount` hook fires
|
259
|
+
2. `userData` auto-cleanup (close() methods called)
|
260
|
+
3. Child nodes destroyed
|
261
|
+
4. Dependencies unregistered
|
262
|
+
5. Directive manager cleanup
|
263
|
+
6. `@unmounted` hook fires
|
264
|
+
|
210
265
|
**Works with v-if and v-for:**
|
211
266
|
|
212
267
|
```html
|
package/dist/ichigo.esm.js
CHANGED
@@ -7672,6 +7672,13 @@ class VNode {
|
|
7672
7672
|
* This is optional and may be undefined if the node has not been templatized.
|
7673
7673
|
*/
|
7674
7674
|
#templatized;
|
7675
|
+
/**
|
7676
|
+
* User data storage for lifecycle directives.
|
7677
|
+
* This provides a Proxy-free space where developers can store arbitrary data
|
7678
|
+
* associated with this VNode. The data is automatically cleaned up when the
|
7679
|
+
* VNode is destroyed.
|
7680
|
+
*/
|
7681
|
+
#userData;
|
7675
7682
|
/**
|
7676
7683
|
* Creates an instance of the virtual node.
|
7677
7684
|
* @param args The initialization arguments for the virtual node.
|
@@ -7883,6 +7890,18 @@ class VNode {
|
|
7883
7890
|
this.#preparableIdentifiers = preparableIdentifiers.length === 0 ? [] : [...new Set(preparableIdentifiers)];
|
7884
7891
|
return this.#preparableIdentifiers;
|
7885
7892
|
}
|
7893
|
+
/**
|
7894
|
+
* Gets the user data storage for this virtual node.
|
7895
|
+
* This is lazily initialized and provides a Proxy-free space for storing
|
7896
|
+
* arbitrary data associated with lifecycle directives.
|
7897
|
+
* @returns A Map for storing user data.
|
7898
|
+
*/
|
7899
|
+
get userData() {
|
7900
|
+
if (!this.#userData) {
|
7901
|
+
this.#userData = new Map();
|
7902
|
+
}
|
7903
|
+
return this.#userData;
|
7904
|
+
}
|
7886
7905
|
/**
|
7887
7906
|
* The DOM path of this virtual node.
|
7888
7907
|
* This is a string representation of the path from the root to this node,
|
@@ -8089,6 +8108,14 @@ class VNode {
|
|
8089
8108
|
/**
|
8090
8109
|
* Cleans up any resources used by this virtual node.
|
8091
8110
|
* This method is called when the virtual node is no longer needed.
|
8111
|
+
*
|
8112
|
+
* Cleanup order:
|
8113
|
+
* 1. Call onUnmount lifecycle hooks
|
8114
|
+
* 2. Auto-cleanup userData (close() on Closeable objects)
|
8115
|
+
* 3. Recursively destroy child nodes
|
8116
|
+
* 4. Unregister dependencies
|
8117
|
+
* 5. Clean up directive manager
|
8118
|
+
* 6. Call onUnmounted lifecycle hooks
|
8092
8119
|
*/
|
8093
8120
|
destroy() {
|
8094
8121
|
// If no directive requires template preservation, call onUnmount for directives that do not templatize
|
@@ -8097,6 +8124,23 @@ class VNode {
|
|
8097
8124
|
d.onUnmount?.();
|
8098
8125
|
});
|
8099
8126
|
}
|
8127
|
+
// Clean up user data, calling close() on any Closeable objects
|
8128
|
+
// This happens after onUnmount but before other cleanup, allowing users to
|
8129
|
+
// perform custom cleanup in onUnmount while having automatic cleanup of userData
|
8130
|
+
if (this.#userData) {
|
8131
|
+
for (const [key, value] of this.#userData.entries()) {
|
8132
|
+
try {
|
8133
|
+
// If the value has a close() method (Closeable pattern), call it
|
8134
|
+
if (value && typeof value === 'object' && typeof value.close === 'function') {
|
8135
|
+
value.close();
|
8136
|
+
}
|
8137
|
+
}
|
8138
|
+
catch (error) {
|
8139
|
+
this.#vApplication.logManager.getLogger(this.constructor.name).error(`Error closing user data '${key}': ${error}`);
|
8140
|
+
}
|
8141
|
+
}
|
8142
|
+
this.#userData.clear();
|
8143
|
+
}
|
8100
8144
|
// Recursively destroy child nodes
|
8101
8145
|
if (this.#childVNodes) {
|
8102
8146
|
for (const childVNode of this.#childVNodes) {
|
@@ -9501,7 +9545,7 @@ class VOnDirective {
|
|
9501
9545
|
return ['mount', 'mounted', 'update', 'updated', 'unmount', 'unmounted'].includes(eventName);
|
9502
9546
|
}
|
9503
9547
|
/**
|
9504
|
-
* Creates a wrapper function for lifecycle hooks (with
|
9548
|
+
* Creates a wrapper function for lifecycle hooks (with context parameter).
|
9505
9549
|
* @param expression The expression string to evaluate.
|
9506
9550
|
* @returns A function that handles the lifecycle hook.
|
9507
9551
|
*/
|
@@ -9511,22 +9555,27 @@ class VOnDirective {
|
|
9511
9555
|
// Return a function that handles the lifecycle hook with proper scope
|
9512
9556
|
return () => {
|
9513
9557
|
const bindings = vNode.bindings;
|
9514
|
-
const
|
9558
|
+
const $ctx = {
|
9559
|
+
element: vNode.node,
|
9560
|
+
vnode: vNode,
|
9561
|
+
userData: vNode.userData
|
9562
|
+
};
|
9515
9563
|
// If the expression is just a method name, call it with bindings as 'this'
|
9516
9564
|
const trimmedExpr = expression.trim();
|
9517
9565
|
if (identifiers.includes(trimmedExpr) && typeof bindings?.get(trimmedExpr) === 'function') {
|
9518
9566
|
const methodName = trimmedExpr;
|
9519
9567
|
const originalMethod = bindings?.get(methodName);
|
9520
|
-
// Call the method with bindings as 'this' context and
|
9521
|
-
// This allows the method to access the DOM element and
|
9522
|
-
return originalMethod(
|
9568
|
+
// Call the method with bindings as 'this' context and context as parameter
|
9569
|
+
// This allows the method to access the DOM element, VNode, and userData
|
9570
|
+
return originalMethod($ctx);
|
9523
9571
|
}
|
9524
|
-
// For inline expressions, evaluate normally with
|
9572
|
+
// For inline expressions, evaluate normally with $ctx parameter
|
9573
|
+
// Note: $ctx is a reserved variable name for lifecycle context
|
9525
9574
|
const values = identifiers.map(id => vNode.bindings?.get(id));
|
9526
|
-
const args = [...identifiers, '
|
9575
|
+
const args = [...identifiers, '$ctx'].join(", ");
|
9527
9576
|
const funcBody = `return (${expression});`;
|
9528
9577
|
const func = new Function(args, funcBody);
|
9529
|
-
return func.call(bindings?.raw, ...values,
|
9578
|
+
return func.call(bindings?.raw, ...values, $ctx);
|
9530
9579
|
};
|
9531
9580
|
}
|
9532
9581
|
/**
|