Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial tree re-renders #45

Open
Korijn opened this issue May 24, 2022 · 6 comments
Open

Partial tree re-renders #45

Korijn opened this issue May 24, 2022 · 6 comments

Comments

@Korijn
Copy link
Collaborator

Korijn commented May 24, 2022

(I'm mostly wondering how complicated this would be to implement)

When you get an event because of a state change, it should be possible to only re-render the tree from the source of the event down.

Also, I wonder, in the event that you are re-rendering a (sub)tree, can you stop going down the tree when at some point it turns out the props and state for a component have not changed? I think that would guarantee that subtree is not in need of any further attention, right?

Of course it might be that further down below there was another state change... Hmm...

Just thinking 💬 🧠

@berendkleinhaneveld
Copy link
Collaborator

Yeah, I have thought about this a little bit as well.

As you said, we might be able to detect when to stop going down the tree. The key thing here is to know from which point in the tree the data has changed (from root or some component).

Actually, in order to know which items to re-render, we need a way of figuring out which state/props are used in the render function. We might leverage computed from observ somehow to watch the output of the render function. This could provide us the exact components / elements to re-render...

We could start with a slightly better 'watch' mechanism in combination with checking if a subtree has not changed.

@Korijn
Copy link
Collaborator Author

Korijn commented May 25, 2022

Actually, in order to know which items to re-render, we need a way of figuring out which state/props are used in the render function. We might leverage computed from observ somehow to watch the output of the render function. This could provide us the exact components / elements to re-render...

This is exactly what observ.watch can do. You can watch all the render functions, and use the callback to register the component for the next update.

However, that will also render subcomponents, etc.... thinking about this makes my head spin :)

@berendkleinhaneveld
Copy link
Collaborator

berendkleinhaneveld commented Jun 3, 2022

💡 Rendering the whole tree will inevitably happen (although sometimes just from a component down when only local state has changed), but the trick is that we can skip a lot of comparisons (or rather reconciliation) of the rendered VNodes by skipping subtrees that are not marked as changed.

@berendkleinhaneveld
Copy link
Collaborator

berendkleinhaneveld commented Sep 26, 2022

Currently, the render function for each Component (function_component, class_component or host_component) is watched to make them dependent on any reactive state used to generate the resulting VNodes. And when the state changes, then the whole VNode tree is rendered again from the root node.

Instead, we should be able to schedule updates in a queue for specific subtrees (as mentioned in your first post). Then during the update of the subtree, if there is watcher on the VNode, we should be able to 'skip' that subtree. The subtree should just be triggered by changes to its own props.

I think that the fiber structure that is currently in place makes things harder to reason about (with the whole concept of WIP tree and such), so we might benefit from combining fiber + VNode like Vue does?

Another 'trick' from Vue 3 is to 'hoist' static elements, so static parts don't do any reconciliation.
Also, a list of dynamicProperties keys is set by the template compiler, in order to only reconcile dynamic properties, so that static properties can be skipped.
Furthermore, Vue marks some VNodes as 'blocks', when their children structure is 'stable', which means that the number (and type?) of children don't change (which might be used for skipping certain comparisons).

> Since v-if and v-for are the two possible ways node structure can dynamically
> change, once we consider v-if branches and each v-for fragment a block, we
> can divide a template into nested blocks, and within each block the node
> structure would be stable. This allows us to skip most children diffing
> and only worry about the dynamic nodes (indicated by patch flags).

So also a list of dynamicChildren is populated in order to skip the static children.

Edit: interesting read: https://wuchwuw.github.io/vue-next-analysis/runtime-core/vnode.html (in Chinese? Here is a link to google translate: https://wuchwuw-github-io.translate.goog/vue-next-analysis/runtime-core/vnode.html?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en-US&_x_tr_pto=wapp)

@berendkleinhaneveld
Copy link
Collaborator

berendkleinhaneveld commented Oct 11, 2022

Some more interesting reading material for figuring out some of Svelte's tricks: https://lihautan.com/compile-svelte-in-your-head/
(it's not so much about skipping subtrees (they don't have the same problem) but it does give some insight in their approach).

@berendkleinhaneveld
Copy link
Collaborator

Some more tricks from dioxus: https://dioxuslabs.com/blog/templates-diffing/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants