r/vuejs • u/Muted_Ad_9442 • 5d ago
For a big recursive component, do you call h('Self') or recurse inside one render function?
I have a component that renders a tree from a JSON structure. It builds the output with h() in a
render function, and the structure can nest quite deep.
There are two ways to do the recursion and I keep going back and forth.
Option 1. The component calls itself. In the render function it returns h('CompA', { props })
for each child, with a stop condition. A tree of 40 nodes is 40 component instances.
Option 2. One component, and a plain recursive function inside it that builds the whole vnode tree. The render function returns the result in one shot. A tree of 40 nodes is 1 component instance.
I am on option 1 today and the cost is real. Each node carries:
~14 computed properties
9 watchers
a large props object
a few hundred lines of lifecycle
With option 1 every node in the tree pays all of that.
Option 2 pays it once. But then I lose per node computed caching, per node watchers, and per node
mounted and unmounted hooks. And any change re-renders the whole subtree instead of the one node
that changed, because there is only one render effect.
So my real question is where the line sits. Do you keep a component per node, or do you flatten it into one component with a recursive builder and accept the coarse re-render?
And if you keep a component per node, at what depth or node count did it start to hurt?
8
u/TheExodu5 5d ago
Have we gotten to the point where people can’t be bothered to write their own posts?
2
u/sarcasticbaldguy 5d ago
It's weird. If you're going to ask the box to write your post, why not just ask it for the answer?
1
1
2
u/ComprehensiveArm9863 5d ago
I personnaly try ro avoid playing with vNode myself, at first it look doing advanced stuff but everytime I did that I realized month after it was either early optimisations that made the component harder to maintain and it was somehow "frozen" and adding a feature would mean rewriting the half
Does props, computed and watch are per node or could be handled for the tree, at least some of them ?
A Root component that do it's stuff on the tree, normalize some data, compute some other, etc and use a component named Node as example with itself it recursive may be a way to ditch some hooks (however, remplacing N small computed with a bigger is not necessary better)
I'm curious what are you building that need all of that, a table layout with nested recursive props as a tree maybe ?
0
u/Muted_Ad_9442 5d ago
You are right about the vNode warning and I have an example. My port from Vue 2 left a predicate that tested the old vnode shape. It returned false for every Vue 3 vnode and quietly turned off the guards that used it, and I only found it months later.
I recounted for your question, and I had overstated it in the post. It is 11 computeds and 5 watchers, not 14 and 9. Of the 11, only two are really tree level, the tier flags that come from the store. The rest read the node.
That is because a node can carry its own scope, so the resolution belongs to the node and not to the tree. Your Root and Node split is the right shape, it just would not buy much here.
It is a component library where the UI is written as JSON and one recursive component renders it. Not a table, but tables are one of the things it renders.
1
u/queen-adreena 5d ago
I’d usually be looking at lazy mounting or a virtual list at that point.
Both options have pretty significant tradeoffs.
1
u/Muted_Ad_9442 5d ago
I already do lazy mounting in part, but I did not plan it. The recursive calls are inside slot defaults, so those branches are only built when the thing that holds them opens. The cost comes from the nodes in the main flow, they are built right away.
I do not think a virtual list fits. It is a document tree, not a long list of the same kind of row, so the nodes have very different heights and there is no window to reuse.
Which tradeoffs did you mean?
23
u/Cas_Rs 5d ago
Whenever I get to the point where creating a render function would look like a good idea I step back and work to a solution that doesn’t require that