Skip to content

Commit d8a676c

Browse files
committed
#7076 manager.VDomUpdate: getAdjustedUpdateDepth()
1 parent c52140f commit d8a676c

4 files changed

Lines changed: 141 additions & 80 deletions

File tree

src/manager/Component.mjs

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -394,81 +394,6 @@ class Component extends Manager {
394394
return parents
395395
}
396396

397-
/**
398-
* Copies a given vdom tree and replaces child component references with the vdom of their matching components
399-
* @param {Object} vdom
400-
* @param {Number} depth=-1
401-
* The component replacement depth.
402-
* -1 will parse the full tree, 1 top level only, 2 include children, 3 include grandchildren
403-
* @returns {Object}
404-
*/
405-
getVdomTree(vdom, depth=-1) {
406-
if (!Neo.isObject(vdom)) {
407-
return vdom
408-
}
409-
410-
let output = {...vdom}, // shallow copy
411-
childDepth;
412-
413-
if (vdom.cn) {
414-
output.cn = [];
415-
416-
vdom.cn.forEach(item => {
417-
childDepth = depth;
418-
419-
if (item.componentId) {
420-
childDepth = depth === -1 ? -1 : depth > 1 ? depth-1 : 1;
421-
422-
if (depth === -1 || depth > 1) {
423-
item = this.get(item.componentId).vdom
424-
}
425-
}
426-
427-
output.cn.push(this.getVdomTree(item, childDepth))
428-
})
429-
}
430-
431-
return output
432-
}
433-
434-
/**
435-
* Copies a given vnode tree and replaces child component references with the vnode of their matching components
436-
* @param {Object} vnode
437-
* @param {Number} depth=-1
438-
* The component replacement depth.
439-
* -1 will parse the full tree, 1 top level only, 2 include children, 3 include grandchildren
440-
* @returns {Object}
441-
*/
442-
getVnodeTree(vnode, depth=-1) {
443-
let output = {...vnode}, // shallow copy
444-
childDepth, component;
445-
446-
if (vnode.childNodes) {
447-
output.childNodes = [];
448-
449-
vnode.childNodes.forEach(item => {
450-
childDepth = depth;
451-
452-
if (item.componentId) {
453-
childDepth = depth === -1 ? -1 : depth > 1 ? depth-1 : 1;
454-
455-
if (depth === -1 || depth > 1) {
456-
component = this.get(item.componentId);
457-
458-
// keep references in case there is no vnode (cmp not mounted)
459-
if (component?.vnode) {
460-
item = component.vnode
461-
}
462-
}
463-
}
464-
465-
output.childNodes.push(this.getVnodeTree(item, childDepth))
466-
})
467-
}
468-
469-
return output
470-
}
471-
472397
/**
473398
* Check if the component had a property of any value somewhere in the Prototype chain
474399
*

src/manager/VDomUpdate.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,19 @@ class VDomUpdate extends Collection {
113113
return null;
114114
}
115115

116+
/**
117+
* Returns a Set of child IDs that have been merged into a parent's update.
118+
* @param {String} ownerId The ID of the parent component owning the update.
119+
* @returns {Set<String>|null} A Set containing the IDs of the merged children, or null.
120+
*/
121+
getMergedChildIds(ownerId) {
122+
const item = this.mergedCallbackMap.get(ownerId);
123+
if (item) {
124+
return new Set(item.children.map(c => c.childId));
125+
}
126+
return null;
127+
}
128+
116129
/**
117130
* @param {String} ownerId
118131
*/

src/mixin/VdomLifecycle.mjs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Base from '../core/Base.mjs';
22
import ComponentManager from '../manager/Component.mjs';
33
import VDomUpdate from '../manager/VDomUpdate.mjs'; // New import
44
import NeoArray from '../util/Array.mjs';
5+
import TreeBuilder from '../util/vdom/TreeBuilder.mjs';
56
import VDomUtil from '../util/VDom.mjs';
67
import VNodeUtil from '../util/VNode.mjs';
78
import {isDescriptor} from '../core/ConfigSymbols.mjs';
@@ -207,12 +208,12 @@ class VdomLifecycle extends Base {
207208
executeVdomUpdate(resolve, reject) {
208209
let me = this,
209210
{vdom, vnode} = me,
210-
opts = {},
211+
mergedChildIds, opts = {},
211212
deltas;
212213

213214
if (currentWorker.isSharedWorker) {
214215
opts.appName = me.appName;
215-
opts.windowId = me.windowId
216+
opts.windowId = me.windowId;
216217
}
217218

218219
me.isVdomUpdating = true;
@@ -222,8 +223,9 @@ class VdomLifecycle extends Base {
222223
me._needsVdomUpdate = false;
223224
me.afterSetNeedsVdomUpdate?.(false, true);
224225

225-
opts.vdom = ComponentManager.getVdomTree(vdom, me.updateDepth);
226-
opts.vnode = ComponentManager.getVnodeTree(vnode, me.updateDepth);
226+
mergedChildIds = VDomUpdate.getMergedChildIds(me.id);
227+
opts.vdom = TreeBuilder.getVdomTree(vdom, me.updateDepth, mergedChildIds);
228+
opts.vnode = TreeBuilder.getVnodeTree(vnode, me.updateDepth, mergedChildIds);
227229

228230
// Reset the updateDepth to the default value for the next update cycle
229231
me._updateDepth = me.constructor.config.updateDepth;
@@ -490,7 +492,7 @@ class VdomLifecycle extends Base {
490492
autoMount,
491493
parentId : autoMount ? me.getMountedParentId() : undefined,
492494
parentIndex: autoMount ? me.getMountedParentIndex() : undefined,
493-
vdom : ComponentManager.getVdomTree(me.vdom),
495+
vdom : TreeBuilder.getVdomTree(me.vdom, -1),
494496
windowId : me.windowId
495497
});
496498

src/util/vdom/TreeBuilder.mjs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import Base from '../../core/Base.mjs';
2+
import ComponentManager from '../../manager/Component.mjs';
3+
4+
/**
5+
* A singleton utility class responsible for recursively building VDOM and VNode trees.
6+
* It can expand component references within a tree structure into their full VDOM/VNode representations,
7+
* supporting selective (asymmetric) tree expansion for optimized updates.
8+
* @class Neo.util.vdom.TreeBuilder
9+
* @extends Neo.core.Base
10+
* @singleton
11+
*/
12+
class TreeBuilder extends Base {
13+
static config = {
14+
/**
15+
* @member {String} className='Neo.util.vdom.TreeBuilder'
16+
* @protected
17+
*/
18+
className: 'Neo.util.vdom.TreeBuilder',
19+
/**
20+
* @member {Boolean} singleton=true
21+
* @protected
22+
*/
23+
singleton: true
24+
}
25+
26+
/**
27+
* Copies a given vdom tree and replaces child component references with the vdom of their matching components
28+
* @param {Object} vdom
29+
* @param {Number} [depth=-1]
30+
* The component replacement depth.
31+
* -1 will parse the full tree, 1 top level only, 2 include children, 3 include grandchildren
32+
* @param {Set<String>|null} [mergedChildIds=null] A set of component IDs to selectively expand.
33+
* @returns {Object}
34+
*/
35+
getVdomTree(vdom, depth = -1, mergedChildIds = null) {
36+
if (!Neo.isObject(vdom)) {
37+
return vdom;
38+
}
39+
40+
let output = {...vdom}, // shallow copy
41+
childDepth;
42+
43+
if (vdom.cn) {
44+
output.cn = [];
45+
46+
vdom.cn.forEach(item => {
47+
let currentItem = item;
48+
49+
if (currentItem.componentId) {
50+
// Expand if:
51+
// 1. We are not at the depth limit (depth > 1 or depth === -1).
52+
// 2. The child component is part of the set of components whose updates were merged into this one.
53+
if ((depth === -1 || depth > 1) && mergedChildIds?.has(currentItem.componentId)) {
54+
const component = ComponentManager.get(currentItem.componentId);
55+
if (component?.vdom) {
56+
currentItem = component.vdom;
57+
}
58+
}
59+
}
60+
61+
if (item.componentId) { // check original item
62+
childDepth = (depth === -1) ? -1 : Math.max(0, depth - 1);
63+
} else {
64+
childDepth = depth;
65+
}
66+
67+
output.cn.push(this.getVdomTree(currentItem, childDepth, mergedChildIds));
68+
});
69+
}
70+
71+
return output
72+
}
73+
74+
/**
75+
* Copies a given vnode tree and replaces child component references with the vnode of their matching components
76+
* @param {Object} vnode
77+
* @param {Number} [depth=-1]
78+
* The component replacement depth.
79+
* -1 will parse the full tree, 1 top level only, 2 include children, 3 include grandchildren
80+
* @param {Set<String>|null} [mergedChildIds=null] A set of component IDs to selectively expand.
81+
* @returns {Object}
82+
*/
83+
getVnodeTree(vnode, depth = -1, mergedChildIds = null) {
84+
let output = {...vnode}, // shallow copy
85+
childDepth, component;
86+
87+
if (vnode.childNodes) {
88+
output.childNodes = [];
89+
90+
vnode.childNodes.forEach(item => {
91+
let currentItem = item;
92+
93+
if (currentItem.componentId) {
94+
// Expand if:
95+
// 1. We are not at the depth limit (depth > 1 or depth === -1).
96+
// 2. The child component is part of the set of components whose updates were merged into this one.
97+
if ((depth === -1 || depth > 1) && mergedChildIds?.has(currentItem.componentId)) {
98+
component = ComponentManager.get(currentItem.componentId);
99+
100+
// keep references in case there is no vnode (e.g. component not mounted yet)
101+
if (component?.vnode) {
102+
currentItem = component.vnode;
103+
}
104+
}
105+
}
106+
107+
if (item.componentId) { // check original item
108+
childDepth = (depth === -1) ? -1 : Math.max(0, depth - 1);
109+
} else {
110+
childDepth = depth;
111+
}
112+
113+
output.childNodes.push(this.getVnodeTree(currentItem, childDepth, mergedChildIds));
114+
});
115+
}
116+
117+
return output
118+
}
119+
}
120+
121+
export default Neo.setupClass(TreeBuilder);

0 commit comments

Comments
 (0)