Skip to content

Commit 8930d63

Browse files
committed
Neo.config.useStringBasedMounting => useDomApiRenderer #6842
1 parent 3727349 commit 8930d63

5 files changed

Lines changed: 45 additions & 30 deletions

File tree

src/DefaultConfig.mjs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,31 @@ const DefaultConfig = {
200200
* @type Boolean
201201
*/
202202
useCanvasWorker: false,
203+
/**
204+
* `true` will enable the advanced, secure, and performant direct DOM API rendering strategy (recommended).
205+
* In this mode, `Neo.vdom.Helper` will create and send structured VNode object graphs to the Main Thread.
206+
* `Neo.main.DeltaUpdates` will then use `Neo.main.render.DomApiRenderer` to directly manipulate the DOM.
207+
* Crucially, `Neo.main.render.DomApiRenderer` builds new **DOM subtrees** (from the received VNode object graphs)
208+
* as detached DocumentFragments or elements, entirely outside the live DOM tree.
209+
* These fully constructed fragments are then inserted into the live document in a **single, atomic operation**.
210+
* This approach inherently minimizes costly browser reflows/repaints, drastically reduces Cross-Site Scripting (XSS) risks,
211+
* and optimizes for surgical, atomic DOM updates for unparalleled performance.
212+
*
213+
* `false` will enable the legacy string-based rendering strategy.
214+
* In this mode, `Neo.vdom.Helper` will generate complete HTML strings (`outerHTML`) for VNode subtrees.
215+
* `Neo.main.DeltaUpdates` will then use `Neo.main.render.StringBasedRenderer` to insert these
216+
* strings into the DOM using methods like `parentNode.insertAdjacentHTML()`.
217+
* While performant for large insertions, this mode is generally less secure due to potential XSS vectors
218+
* and relies on browser HTML parsing, which can be less efficient for granular updates.
219+
*
220+
* This configuration affects both the initial painting of your applications and the creation
221+
* of new component trees at runtime.
222+
* @default true
223+
* @memberOf! module:Neo
224+
* @name config.useDomApiRenderer
225+
* @type Boolean
226+
*/
227+
useDomApiRenderer: true,
203228
/**
204229
* Flag if vdom ids should get mapped into DOM element ids.
205230
* false will convert them into a "data-neo-id" attribute.
@@ -246,18 +271,6 @@ const DefaultConfig = {
246271
* @type Boolean
247272
*/
248273
useSharedWorkers: false,
249-
/**
250-
* `true` will let the `vdom.Helper` create a String-based representation of the vnode tree.
251-
* Main will then use e.g.`parentNode.insertAdjacentHTML('beforeend', delta.outerHTML);`
252-
* This affects the initial painting of your apps, but also the creation of new component trees at run-time.
253-
* `false` will skip the creation of the String, and instead use DOM APIs to generate a fragment inside Main,
254-
* into which the vnode tree will get applied.
255-
* @default false
256-
* @memberOf! module:Neo
257-
* @name config.useStringBasedMounting
258-
* @type Boolean
259-
*/
260-
useStringBasedMounting: false,
261274
/**
262275
* True will generate a new task worker, which can get filled with own expensive remote methods
263276
* @default false

src/main/DeltaUpdates.mjs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ class DeltaUpdates extends Base {
8787
try {
8888
let module;
8989

90-
if (NeoConfig.useStringBasedMounting) {
91-
module = await import('./render/StringBasedRenderer.mjs')
92-
} else {
90+
if (NeoConfig.useDomApiRenderer) {
9391
module = await import('./render/DomApiRenderer.mjs')
92+
} else {
93+
module = await import('./render/StringBasedRenderer.mjs')
9494
}
9595

9696
me.#renderer = module.default
@@ -175,7 +175,7 @@ class DeltaUpdates extends Base {
175175
* - `insertAdjacentHTML()` is generally faster than creating a node via template,
176176
* but it's only available for manipulating children (elements), not `childNodes` (all nodes).
177177
* - For performance, in cases where there are no comment nodes (i.e., no wrapped text nodes),
178-
* the method prioritizes `insertAdjacentHTML()` when `useStringBasedMounting` is true.
178+
* the method prioritizes `insertAdjacentHTML()` when `useDomApiRenderer` is false.
179179
*
180180
* @param {Object} delta
181181
* @param {Boolean} delta.hasLeadingTextChildren Flag to honor leading comments, which require special treatment.
@@ -185,19 +185,21 @@ class DeltaUpdates extends Base {
185185
* @param {Neo.vdom.VNode} [delta.vnode] The VNode representation of the new node (for direct DOM API mounting).
186186
*/
187187
insertNode({hasLeadingTextChildren, index, outerHTML, parentId, vnode}) {
188+
let me = this;
189+
188190
// This method is synchronous and *expects* the renderer to be loaded
189-
if (!this.#renderer) {
191+
if (!me.#renderer) {
190192
console.error('DeltaUpdates renderer not ready during insertNode!');
191193
return
192194
}
193195

194196
const parentNode = DomAccess.getElementOrBody(parentId);
195197

196198
if (parentNode) {
197-
if (!NeoConfig.useStringBasedMounting) {
198-
this.#renderer.createDomTree({index, isRoot: true, parentNode, vnode})
199+
if (NeoConfig.useDomApiRenderer) {
200+
me.#renderer.createDomTree({index, isRoot: true, parentNode, vnode})
199201
} else {
200-
this.#renderer.insertNodeAsString({hasLeadingTextChildren, index, outerHTML, parentNode})
202+
me.#renderer.insertNodeAsString({hasLeadingTextChildren, index, outerHTML, parentNode})
201203
}
202204
}
203205
}

src/main/render/StringBasedRenderer.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const StringBasedRenderer = {
1111

1212
/**
1313
* Handles string-based insertion of a new node into the DOM.
14-
* This method is called by `insertNode()` when `NeoConfig.useStringBasedMounting` is true.
14+
* This method is called by `insertNode()` when `NeoConfig.useDomApiRenderer` is false.
1515
*
1616
* @param {Object} data
1717
* @param {Boolean} data.hasLeadingTextChildren Flag to honor leading comments.

src/vdom/Helper.mjs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ class Helper extends Base {
148148
/**
149149
* Creates a Neo.vdom.VNode tree for the given vdom template.
150150
* The top level vnode contains the outerHTML as a string,
151-
* in case Neo.config.useStringBasedMounting === true
151+
* in case Neo.config.useDomApiRenderer === false
152152
* @param {Object} opts
153153
* @param {String} opts.appName
154154
* @param {Boolean} [opts.autoMount]
@@ -170,7 +170,7 @@ class Helper extends Base {
170170

171171
delete returnValue.vdom;
172172

173-
if (NeoConfig.useStringBasedMounting) {
173+
if (!NeoConfig.useDomApiRenderer) {
174174
returnValue.outerHTML = Neo.vdom.util.StringFromVnode.create(vnode)
175175
}
176176

@@ -474,7 +474,7 @@ class Helper extends Base {
474474
* @protected
475475
*/
476476
async importDomApiVnodeCreator() {
477-
if (!NeoConfig.useStringBasedMounting && !Neo.vdom.util?.DomApiVnodeCreator) {
477+
if (NeoConfig.useDomApiRenderer && !Neo.vdom.util?.DomApiVnodeCreator) {
478478
await import('./util/DomApiVnodeCreator.mjs')
479479
}
480480
}
@@ -485,7 +485,7 @@ class Helper extends Base {
485485
* @protected
486486
*/
487487
async importStringFromVnode() {
488-
if (NeoConfig.useStringBasedMounting && !Neo.vdom.util?.StringFromVnode) {
488+
if (!NeoConfig.useDomApiRenderer && !Neo.vdom.util?.StringFromVnode) {
489489
await import('./util/StringFromVnode.mjs')
490490
}
491491
}
@@ -511,12 +511,12 @@ class Helper extends Base {
511511

512512
Object.assign(delta, {hasLeadingTextChildren, index: physicalIndex});
513513

514-
if (NeoConfig.useStringBasedMounting) {
515-
// For string-based mounting, pass a string excluding moved nodes
516-
delta.outerHTML = Neo.vdom.util.StringFromVnode.create(vnode, movedNodes)
517-
} else {
514+
if (NeoConfig.useDomApiRenderer) {
518515
// For direct DOM API mounting, pass the pruned VNode tree
519516
delta.vnode = Neo.vdom.util.DomApiVnodeCreator.create(vnode, movedNodes)
517+
} else {
518+
// For string-based mounting, pass a string excluding moved nodes
519+
delta.outerHTML = Neo.vdom.util.StringFromVnode.create(vnode, movedNodes)
520520
}
521521

522522
deltas.default.push(delta);

src/vdom/VNode.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class VNode {
8888

8989
// We only apply textContent, in case it has content
9090
else if (Object.hasOwn(config, 'textContent')) {
91-
me.textContent = Neo.config.useStringBasedMounting ? StringUtil.escapeHtml(textContent) : textContent
91+
me.textContent = Neo.config.useDomApiRenderer ? textContent : StringUtil.escapeHtml(textContent)
9292
}
9393
}
9494

0 commit comments

Comments
 (0)