Skip to content

Commit ac77ab2

Browse files
committed
perf(DeltaUpdates): Enable insertNodeBatch for StringBasedRenderer (#8618)
1 parent 9e0e788 commit ac77ab2

2 files changed

Lines changed: 90 additions & 16 deletions

File tree

src/main/DeltaUpdates.mjs

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -413,31 +413,52 @@ class DeltaUpdates extends Base {
413413
allPostMountUpdates = [];
414414

415415
batch.forEach(delta => {
416-
const postMountUpdates = delta.postMountUpdates || [];
417-
418-
const node = render.DomApiRenderer.createDomTree({
419-
index : -1,
420-
isRoot : true,
421-
parentNode : null, // detached
422-
postMountUpdates,
423-
vnode : delta.vnode
424-
});
416+
let localPostMountUpdates = delta.postMountUpdates || [],
417+
node;
418+
419+
if (NeoConfig.useDomApiRenderer) {
420+
node = render.DomApiRenderer.createDomTree({
421+
index : -1,
422+
isRoot : true,
423+
parentNode : null, // detached
424+
postMountUpdates: localPostMountUpdates,
425+
vnode : delta.vnode
426+
})
427+
} else {
428+
node = render.StringBasedRenderer.createNode({outerHTML: delta.outerHTML})
429+
}
425430

426431
if (node) {
427432
fragment.appendChild(node);
428-
allPostMountUpdates.push(...postMountUpdates);
433+
if (localPostMountUpdates.length > 0) {
434+
allPostMountUpdates.push(...localPostMountUpdates)
435+
}
429436
} else {
430-
console.error('insertNodeBatch: Failed to create node', delta.vnode);
437+
console.error('insertNodeBatch: Failed to create node', delta);
431438
}
432439
});
433440

434441
parentNode.insertBefore(fragment, siblingRef || null);
435442

436443
// Apply all post-mount updates (e.g. scroll positions) after the batch insertion
437-
allPostMountUpdates.forEach(({node, vnode}) => {
438-
if (vnode.scrollLeft) {node.scrollLeft = vnode.scrollLeft}
439-
if (vnode.scrollTop) {node.scrollTop = vnode.scrollTop}
440-
})
444+
if (allPostMountUpdates.length > 0) {
445+
allPostMountUpdates.forEach(update => {
446+
// DomApiRenderer format: {node, vnode}
447+
if (update.node) {
448+
if (update.vnode.scrollLeft) {update.node.scrollLeft = update.vnode.scrollLeft}
449+
if (update.vnode.scrollTop) {update.node.scrollTop = update.vnode.scrollTop}
450+
}
451+
// StringBasedRenderer format: {id, scrollLeft, scrollTop}
452+
else {
453+
let node = DomAccess.getElement(update.id);
454+
455+
if (node) {
456+
if (update.scrollLeft) {node.scrollLeft = update.scrollLeft}
457+
if (update.scrollTop) {node.scrollTop = update.scrollTop}
458+
}
459+
}
460+
})
461+
}
441462
} else {
442463
console.error('insertNodeBatch: Parent not found', {parentId, index});
443464
}
@@ -839,7 +860,7 @@ class DeltaUpdates extends Base {
839860
const delta = deltas[i];
840861

841862
// Batching optimization for sequential insertNode operations
842-
if (NeoConfig.useDomApiRenderer && delta.action === 'insertNode' && i < len - 1) {
863+
if (delta.action === 'insertNode' && i < len - 1) {
843864
let j = i + 1,
844865
batch = [delta];
845866

test/playwright/component/container/FragmentDeltaUpdates.spec.mjs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,59 @@ test.describe('Neo.main.DeltaUpdates (Fragment Support)', () => {
183183
expect(result.order).toBe(true);
184184
});
185185

186+
test('Manual Fragment Batch Insert Update (StringBasedRenderer)', async ({page}) => {
187+
await page.evaluate(async () => {
188+
Neo.config.useDomApiRenderer = false;
189+
await Neo.main.DeltaUpdates.importRenderer();
190+
191+
const root = document.getElementById('component-test-viewport');
192+
root.innerHTML = '';
193+
194+
const start = document.createComment(' frag-sb-start ');
195+
root.appendChild(start);
196+
197+
const end = document.createComment(' frag-sb-end ');
198+
root.appendChild(end);
199+
});
200+
201+
await page.evaluate(async () => {
202+
Neo.main.DeltaUpdates.update({
203+
deltas: [{
204+
action: 'insertNode',
205+
parentId: 'frag-sb',
206+
index: 0,
207+
outerHTML: '<div id="child-sb-1">Child SB 1</div>'
208+
}, {
209+
action: 'insertNode',
210+
parentId: 'frag-sb',
211+
index: 1,
212+
outerHTML: '<div id="child-sb-2">Child SB 2</div>'
213+
}]
214+
});
215+
});
216+
217+
const result = await page.evaluate(() => {
218+
const item1 = document.getElementById('child-sb-1');
219+
const item2 = document.getElementById('child-sb-2');
220+
const start = document.evaluate(`//comment()[contains(., ' frag-sb-start ')]`, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
221+
const end = document.evaluate(`//comment()[contains(., ' frag-sb-end ')]`, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
222+
223+
if (!item1 || !item2) return { exists: false };
224+
225+
const startBefore1 = (start.compareDocumentPosition(item1) & Node.DOCUMENT_POSITION_FOLLOWING);
226+
const item1Before2 = (item1.compareDocumentPosition(item2) & Node.DOCUMENT_POSITION_FOLLOWING);
227+
const item2BeforeEnd = (item2.compareDocumentPosition(end) & Node.DOCUMENT_POSITION_FOLLOWING);
228+
229+
return {
230+
exists: true,
231+
order: !!(startBefore1 && item1Before2 && item2BeforeEnd)
232+
};
233+
});
234+
235+
expect(result.exists).toBe(true);
236+
expect(result.order).toBe(true);
237+
});
238+
186239
test('Standard DOM Forward Move (Same Parent)', async ({page}) => {
187240
await page.evaluate(() => {
188241
const root = document.getElementById('component-test-viewport');

0 commit comments

Comments
 (0)