Allow to skip sub tree patching.
See original GitHub issueI’m trying to use preact as rendering tool for web components custom elements. I saw the proposal solution on https://github.com/bspaulding/preact-custom-element
But in my case it is not works properly. First is that I do not using shadow-dom (shadowDOM is imperative and do not allow prerender custom-elements on server side, what I want to achieve). Second is that I’m trying to mix preact app and preact base custom-elements, or same effect will be when I will nest the preact base custom-elements within preact base custom-elements.
I prepared example https://www.webpackbin.com/bins/-Ki44_xg5yie0o-jHaXv
Problem is that there is no any isolation between the parent render scope (eg root vnodes tree) and nested render scope (eg. some subbranch of vnode tree).
Custom elements should care about render cycle by itself, diff algorithm should never tray to patch the content of custom-elements (even the custom-element will not use the preact). The proposal https://github.com/bspaulding/preact-custom-element toVdom is not solving the problem and is very inefficient.
I tried locally simple patch:
https://github.com/developit/preact/blob/master/src/vdom/diff.js#L128 replaced by:
var isCE = !!window.customElements.get(vnode.nodeName);
if (!isCE) {
// Optimization: fast-path for elements containing a single TextNode:
if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' &&
fc!=null && fc.splitText!==undefined && fc.nextSibling==null) {
if (fc.nodeValue!=vchildren[0]) {
fc.nodeValue = vchildren[0];
}
}
// otherwise, if there are existing or new children, diff them:
else if (vchildren && vchildren.length || fc!=null) {
innerDiffNode(out, vchildren, context, mountAll, hydrating ||
props.dangerouslySetInnerHTML!=null);
}
}
And after such change everything is works fine. I know this is not something what preact team will accept 😃 But maybe you can add some handler to the options like:
options.skip: (vnode, dom)
and use it:
var skip= options.skip && options.skip(vnode, dom);
if (!skip) {
// Optimization: fast-path for elements containing a single TextNode:
if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' &&
fc!=null && fc.splitText!==undefined && fc.nextSibling==null) {
if (fc.nodeValue!=vchildren[0]) {
fc.nodeValue = vchildren[0];
}
}
// otherwise, if there are existing or new children, diff them:
else if (vchildren && vchildren.length || fc!=null) {
innerDiffNode(out, vchildren, context, mountAll, hydrating ||
props.dangerouslySetInnerHTML!=null);
}
}
and usage for custom elements:
preact.options.skip = (vnode) => typeof vnode == 'string' && !!window.customElements.get(vnode.nodeName);
Such skip option can be used also as a solution for ‘render once’ when we know that some sub tree do not have to be never rerendered:
// preact-one
function once(vnode) {
vnode.renderOnce = true;
return vnode
}
preact.options.skip = (vnode, dom) => vnode.renderOnce && dom
and usage
renderApp() {
render(
<div>
once(<div>Static header which will be never rerendered</div>)
<div>Content rerendered always<div>
</div>, document.body);
}
The other possibility is to add the property to vnode and instead of condition on options.skip check the property.
if (!vnode.skipChildren) {
// Optimization: fast-path for elements containing a single TextNode:
if (!hydrating && vchildren && vchildren.length===1 && typeof vchildren[0]==='string' &&
fc!=null && fc.splitText!==undefined && fc.nextSibling==null) {
if (fc.nodeValue!=vchildren[0]) {
fc.nodeValue = vchildren[0];
}
}
// otherwise, if there are existing or new children, diff them:
else if (vchildren && vchildren.length || fc!=null) {
innerDiffNode(out, vchildren, context, mountAll, hydrating ||
props.dangerouslySetInnerHTML!=null);
}
}
Then options.vnode can be use to decorate vnode with such options.
As a last word, I can say that many virtual dom implementations provides possibility to ‘skip’ the subtree patching: https://github.com/google/incremental-dom/blob/master/test/functional/skip.js https://github.com/Matt-Esch/virtual-dom/blob/master/docs/thunk.md …
Best Regards
Issue Analytics
- State:
- Created 6 years ago
- Comments:12 (4 by maintainers)
Top GitHub Comments
Just for posterity, we allow this now via a custom
renderRoot
property.Wow, so skate going in same direction, but probably because they are always using shadow dom they do not see this problem. (I will try to convince them to add possibility to render also without shadow dom 😃 This will give a possibility to render skate.js components on server side by domino)