@vmz/core 0.3.9 → 0.3.10
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.
|
@@ -99,6 +99,14 @@ export type ComponentCtorLike = (new (props?: object) => DirectInstance) & {
|
|
|
99
99
|
__vmzProps?: string[];
|
|
100
100
|
__vmzWBInstalled?: boolean;
|
|
101
101
|
__vmzCtorAppliesProps?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Compile-time embedded BindingId → deps[] map. When the Rust generator
|
|
104
|
+
* emits this static map on the component class, `registerBind` resolves
|
|
105
|
+
* deps by lookup instead of rebuilding O(N×M) indices from the string[]
|
|
106
|
+
* the artifact passes per call. Absent (today) — falls back to the call
|
|
107
|
+
* site argument unchanged.
|
|
108
|
+
*/
|
|
109
|
+
__vmzDepsMap?: Record<string | number, string[]>;
|
|
102
110
|
name?: string;
|
|
103
111
|
};
|
|
104
112
|
export type DirectEachCtx = {
|
|
@@ -135,6 +143,7 @@ export type DirectApi = {
|
|
|
135
143
|
adoptLeave: () => void;
|
|
136
144
|
projectDefaultSlot: (hostEl: HTMLElement, node: Node) => void;
|
|
137
145
|
setHtml: (el: Element, value: unknown) => void;
|
|
146
|
+
setShow: (el: HTMLElement, condition: unknown) => void;
|
|
138
147
|
bindHtml: (inst: DirectInstance, bindingId: BindingId, deps: string[], get: PatchFn, el: Element) => void;
|
|
139
148
|
bindComponentProp: (inst: DirectInstance, bindingId: BindingId, deps: string[], get: PatchFn, hostInst: DirectInstance, propName: string) => void;
|
|
140
149
|
component: (Ctor: ComponentCtorLike | string, props?: object) => HTMLElement;
|
|
@@ -30,7 +30,7 @@ export declare const directApi: {
|
|
|
30
30
|
attr(el: any, name: any, value: any): void;
|
|
31
31
|
mergeClass(...parts: any[]): string;
|
|
32
32
|
mergeStyle(...parts: any[]): string;
|
|
33
|
-
on(el: any, type: any, handler: any): void;
|
|
33
|
+
on(el: any, type: any, handler: any, options?: any): void;
|
|
34
34
|
/**
|
|
35
35
|
* Bind a named instance method (no arrow / Function#toString).
|
|
36
36
|
* Learns `skipFlush` after a sync invocation that schedules no dirty work
|
|
@@ -46,7 +46,25 @@ export declare const directApi: {
|
|
|
46
46
|
*/
|
|
47
47
|
specFieldText(inst: any, bindingId: any, fieldName: any, textNode: any): void;
|
|
48
48
|
setHtml(el: any, value: any): void;
|
|
49
|
+
/**
|
|
50
|
+
* Vue `v-text` — sets `textContent`, escaping all HTML (unlike `setHtml`).
|
|
51
|
+
* Overrides any existing children on the element. Static create-time path;
|
|
52
|
+
* reactive bindings use {@link bindText}.
|
|
53
|
+
*/
|
|
54
|
+
setText(el: any, value: any): void;
|
|
55
|
+
/**
|
|
56
|
+
* Vue `v-show` — toggles `style.display` between empty string (visible,
|
|
57
|
+
* original CSS wins) and `'none'`. Element stays in the DOM tree (unlike
|
|
58
|
+
* `v-if`). Used for static-show values; reactive bindings emit inline
|
|
59
|
+
* patches in {@link trackPatch}.
|
|
60
|
+
*/
|
|
61
|
+
setShow(el: any, condition: any): void;
|
|
49
62
|
bindHtml(inst: any, bindingId: any, deps: any, get: any, el: any): void;
|
|
63
|
+
/**
|
|
64
|
+
* Reactive `v-text` textContent binding — mirrors {@link bindHtml} but
|
|
65
|
+
* writes `textContent` (HTML-escaped, safe).
|
|
66
|
+
*/
|
|
67
|
+
bindText(inst: any, bindingId: any, deps: any, get: any, el: any): void;
|
|
50
68
|
/**
|
|
51
69
|
* Nested component (sync Direct child or island schedule).
|
|
52
70
|
*/
|
|
@@ -169,7 +169,11 @@ export const directApi = {
|
|
|
169
169
|
mergeStyle(...parts) {
|
|
170
170
|
return mergeStyleParts(...parts);
|
|
171
171
|
},
|
|
172
|
-
on(el, type, handler) {
|
|
172
|
+
on(el, type, handler, options) {
|
|
173
|
+
// options: { capture?: boolean; passive?: boolean; once?: boolean }
|
|
174
|
+
const listenerOpts = options && (options.capture || options.passive || options.once)
|
|
175
|
+
? { capture: !!options.capture, passive: !!options.passive, once: !!options.once }
|
|
176
|
+
: undefined;
|
|
173
177
|
const inst = directApi._inst;
|
|
174
178
|
if (directApi._eachCtx && typeof handler === 'function') {
|
|
175
179
|
const bag = el.__vmzEvt || (el.__vmzEvt = Object.create(null));
|
|
@@ -191,7 +195,7 @@ export const directApi = {
|
|
|
191
195
|
};
|
|
192
196
|
bag[type] = listener;
|
|
193
197
|
directApi._eachCtx.needDelegate(type);
|
|
194
|
-
el.addEventListener(type, listener);
|
|
198
|
+
el.addEventListener(type, listener, listenerOpts);
|
|
195
199
|
return;
|
|
196
200
|
}
|
|
197
201
|
// Infer once at bind time ??never Function#toString on the click hot path.
|
|
@@ -208,7 +212,7 @@ export const directApi = {
|
|
|
208
212
|
return m.call(inst, ev);
|
|
209
213
|
return handler.call(inst, ev);
|
|
210
214
|
});
|
|
211
|
-
});
|
|
215
|
+
}, listenerOpts);
|
|
212
216
|
return;
|
|
213
217
|
}
|
|
214
218
|
el.addEventListener(type, (ev) => {
|
|
@@ -219,7 +223,7 @@ export const directApi = {
|
|
|
219
223
|
if (typeof handler === 'function') {
|
|
220
224
|
runDomEventHandler(inst, methodHint, () => handler.call(inst, ev));
|
|
221
225
|
}
|
|
222
|
-
});
|
|
226
|
+
}, listenerOpts);
|
|
223
227
|
},
|
|
224
228
|
/**
|
|
225
229
|
* Bind a named instance method (no arrow / Function#toString).
|
|
@@ -227,6 +231,10 @@ export const directApi = {
|
|
|
227
231
|
* (stride / transpose self-apply DOM).
|
|
228
232
|
*/
|
|
229
233
|
onMethod(el, type, methodName, opts) {
|
|
234
|
+
// opts: { skipFlush?: boolean; capture?: boolean; passive?: boolean; once?: boolean }
|
|
235
|
+
const listenerOpts = opts && (opts.capture || opts.passive || opts.once)
|
|
236
|
+
? { capture: !!opts.capture, passive: !!opts.passive, once: !!opts.once }
|
|
237
|
+
: undefined;
|
|
230
238
|
// Prefer each-block owner: item create often runs during flush when `_inst` is unset.
|
|
231
239
|
const eachOwner = directApi._eachCtx && directApi._eachCtx.inst;
|
|
232
240
|
const inst = eachOwner || directApi._inst;
|
|
@@ -262,10 +270,10 @@ export const directApi = {
|
|
|
262
270
|
const listener = (ev) => invoke(ev);
|
|
263
271
|
bag[type] = listener;
|
|
264
272
|
directApi._eachCtx.needDelegate(type);
|
|
265
|
-
el.addEventListener(type, listener);
|
|
273
|
+
el.addEventListener(type, listener, listenerOpts);
|
|
266
274
|
return;
|
|
267
275
|
}
|
|
268
|
-
el.addEventListener(type, (ev) => invoke(ev));
|
|
276
|
+
el.addEventListener(type, (ev) => invoke(ev), listenerOpts);
|
|
269
277
|
},
|
|
270
278
|
/**
|
|
271
279
|
* Specialized single-field attr bind (0.1.29): compile-time field name, no generic get closure in artifact.
|
|
@@ -303,6 +311,23 @@ export const directApi = {
|
|
|
303
311
|
setHtml(el, value) {
|
|
304
312
|
el.innerHTML = value == null ? '' : String(value);
|
|
305
313
|
},
|
|
314
|
+
/**
|
|
315
|
+
* Vue `v-text` — sets `textContent`, escaping all HTML (unlike `setHtml`).
|
|
316
|
+
* Overrides any existing children on the element. Static create-time path;
|
|
317
|
+
* reactive bindings use {@link bindText}.
|
|
318
|
+
*/
|
|
319
|
+
setText(el, value) {
|
|
320
|
+
el.textContent = value == null ? '' : String(value);
|
|
321
|
+
},
|
|
322
|
+
/**
|
|
323
|
+
* Vue `v-show` — toggles `style.display` between empty string (visible,
|
|
324
|
+
* original CSS wins) and `'none'`. Element stays in the DOM tree (unlike
|
|
325
|
+
* `v-if`). Used for static-show values; reactive bindings emit inline
|
|
326
|
+
* patches in {@link trackPatch}.
|
|
327
|
+
*/
|
|
328
|
+
setShow(el, condition) {
|
|
329
|
+
el.style.display = condition ? '' : 'none';
|
|
330
|
+
},
|
|
306
331
|
bindHtml(inst, bindingId, deps, get, el) {
|
|
307
332
|
directApi.trackPatch(inst, deps || [], function bindHtmlPatch() {
|
|
308
333
|
let raw;
|
|
@@ -315,6 +340,22 @@ export const directApi = {
|
|
|
315
340
|
el.innerHTML = raw == null ? '' : String(raw);
|
|
316
341
|
}, bindingId);
|
|
317
342
|
},
|
|
343
|
+
/**
|
|
344
|
+
* Reactive `v-text` textContent binding — mirrors {@link bindHtml} but
|
|
345
|
+
* writes `textContent` (HTML-escaped, safe).
|
|
346
|
+
*/
|
|
347
|
+
bindText(inst, bindingId, deps, get, el) {
|
|
348
|
+
directApi.trackPatch(inst, deps || [], function bindTextPatch() {
|
|
349
|
+
let raw;
|
|
350
|
+
try {
|
|
351
|
+
raw = get.call(inst);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
raw = '';
|
|
355
|
+
}
|
|
356
|
+
el.textContent = raw == null ? '' : String(raw);
|
|
357
|
+
}, bindingId);
|
|
358
|
+
},
|
|
318
359
|
/**
|
|
319
360
|
* Nested component (sync Direct child or island schedule).
|
|
320
361
|
*/
|
|
@@ -645,6 +645,9 @@ function reindexBindingDeps(inst, bindingId, deps) {
|
|
|
645
645
|
const entry = inst.__vmzBindings[bindingId];
|
|
646
646
|
if (!entry)
|
|
647
647
|
return;
|
|
648
|
+
// Fast path: deps 完全相同 → 无需重建 dep→bindings 索引。
|
|
649
|
+
if (arraysEqual(entry.deps, deps))
|
|
650
|
+
return;
|
|
648
651
|
for (const dep of entry.deps || []) {
|
|
649
652
|
const list = inst.__vmzDepToBindings[dep];
|
|
650
653
|
if (!list)
|
|
@@ -664,10 +667,47 @@ function reindexBindingDeps(inst, bindingId, deps) {
|
|
|
664
667
|
}
|
|
665
668
|
}
|
|
666
669
|
}
|
|
670
|
+
function arraysEqual(a, b) {
|
|
671
|
+
if (a === b)
|
|
672
|
+
return true;
|
|
673
|
+
const la = a ? a.length : 0;
|
|
674
|
+
const lb = b ? b.length : 0;
|
|
675
|
+
if (la !== lb)
|
|
676
|
+
return false;
|
|
677
|
+
for (let i = 0; i < la; i++)
|
|
678
|
+
if (a[i] !== b[i])
|
|
679
|
+
return false;
|
|
680
|
+
return true;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Resolve deps for a binding: prefer the argument from generated artifact,
|
|
684
|
+
* fall back to a compile-time-embedded static map on the component class.
|
|
685
|
+
*
|
|
686
|
+
* The Rust generator will later emit `Ctor.__vmzDepsMap = { [bindingId]: deps[] }`
|
|
687
|
+
* so runtime can do a single lookup instead of rebuilding O(N×M) dep→binding
|
|
688
|
+
* indices from the string[] the artifact passes today.
|
|
689
|
+
*/
|
|
690
|
+
function resolveDeps(inst, depsArg, bindingId) {
|
|
691
|
+
if (depsArg && depsArg.length)
|
|
692
|
+
return depsArg;
|
|
693
|
+
if (bindingId == null)
|
|
694
|
+
return depsArg || [];
|
|
695
|
+
const ctor = inst && inst.constructor;
|
|
696
|
+
const map = ctor && ctor.__vmzDepsMap;
|
|
697
|
+
if (map) {
|
|
698
|
+
const hit = map[bindingId];
|
|
699
|
+
if (hit)
|
|
700
|
+
return hit;
|
|
701
|
+
}
|
|
702
|
+
return depsArg || [];
|
|
703
|
+
}
|
|
667
704
|
export function registerBind(inst, deps, fn, bindingId = null) {
|
|
705
|
+
// 查表优先: generator 侧若已在 Ctor.__vmzDepsMap 嵌入完整 deps map
|
|
706
|
+
// 则直接取用, 避免每次 registerBind 都用传入 string[] 重建索引.
|
|
707
|
+
const resolvedDeps = resolveDeps(inst, deps, bindingId);
|
|
668
708
|
if (!inst.__vmzBinders)
|
|
669
709
|
inst.__vmzBinders = Object.create(null);
|
|
670
|
-
for (const dep of
|
|
710
|
+
for (const dep of resolvedDeps) {
|
|
671
711
|
if (!inst.__vmzBinders[dep])
|
|
672
712
|
inst.__vmzBinders[dep] = [];
|
|
673
713
|
// Dedup: cf untrack/retrack with identical deps must not grow the binder list.
|
|
@@ -685,7 +725,7 @@ export function registerBind(inst, deps, fn, bindingId = null) {
|
|
|
685
725
|
}
|
|
686
726
|
if (!entry.patches.includes(fn))
|
|
687
727
|
entry.patches.push(fn);
|
|
688
|
-
reindexBindingDeps(inst, bindingId,
|
|
728
|
+
reindexBindingDeps(inst, bindingId, resolvedDeps);
|
|
689
729
|
}
|
|
690
730
|
export function unregisterBind(inst, deps, fn, bindingId = null) {
|
|
691
731
|
const binders = inst.__vmzBinders;
|
package/package.json
CHANGED
|
@@ -68,12 +68,12 @@
|
|
|
68
68
|
"main": "./dist/faces/server.js",
|
|
69
69
|
"name": "@vmz/core",
|
|
70
70
|
"optionalDependencies": {
|
|
71
|
-
"@vmz/vmz-darwin-arm64": "0.3.
|
|
72
|
-
"@vmz/vmz-darwin-x64": "0.3.
|
|
73
|
-
"@vmz/vmz-linux-arm64": "0.3.
|
|
74
|
-
"@vmz/vmz-linux-x64": "0.3.
|
|
75
|
-
"@vmz/vmz-win32-arm64": "0.3.
|
|
76
|
-
"@vmz/vmz-win32-x64": "0.3.
|
|
71
|
+
"@vmz/vmz-darwin-arm64": "0.3.10",
|
|
72
|
+
"@vmz/vmz-darwin-x64": "0.3.10",
|
|
73
|
+
"@vmz/vmz-linux-arm64": "0.3.10",
|
|
74
|
+
"@vmz/vmz-linux-x64": "0.3.10",
|
|
75
|
+
"@vmz/vmz-win32-arm64": "0.3.10",
|
|
76
|
+
"@vmz/vmz-win32-x64": "0.3.10"
|
|
77
77
|
},
|
|
78
78
|
"publishConfig": {
|
|
79
79
|
"access": "public"
|
|
@@ -88,5 +88,5 @@
|
|
|
88
88
|
},
|
|
89
89
|
"type": "module",
|
|
90
90
|
"types": "./dist/faces/index.d.ts",
|
|
91
|
-
"version": "0.3.
|
|
91
|
+
"version": "0.3.10"
|
|
92
92
|
}
|