-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
For element nodes with a single text child, directly set textContent
#3939
Conversation
📊 Tachometer Benchmark ResultsSummaryduration
usedJSHeapSize
Results02_replace1k
duration
usedJSHeapSize
run-warmup-0
run-warmup-1
run-warmup-2
run-warmup-3
run-warmup-4
run-final
03_update10th1k_x16
duration
usedJSHeapSize
07_create10k
duration
usedJSHeapSize
filter_list
duration
usedJSHeapSize
hydrate1k
duration
usedJSHeapSize
many_updates
duration
usedJSHeapSize
text_update
duration
usedJSHeapSize
todo
duration
usedJSHeapSize
|
Size Change: +374 B (0%) Total Size: 54.7 kB
ℹ️ View Unchanged
|
'<div>.appendChild(#text)', | ||
'<div>1.insertBefore(<div>3, #text)', | ||
'<div>.appendChild(#text)', | ||
'<div>31.insertBefore(<div>4, #text)', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the test changes here are just removing calls to appendChild(#text)
. But this test case invokes something more interesting I think it worth examining.
Here, we are transitioning from a tree like:
<div>1</div>
<div>2</div>
to
<div class="wrapper">
<div>3</div>
<div>4</div>
</div>
Our diff matches <div>1
with the new div.wrapper
. So it sees a div with a single text child (1
) transitioning to a div with multiple children <div>3
and <div>4
. So with this update, instead of creating and inserting the new children before the old children, it removes the text node and appends the new children to the now empty div.
[ | ||
'<div>34.insertBefore(#text, <div>3)', | ||
'<div>4.remove()', | ||
'<div>3.remove()', | ||
'<div>.appendChild(#text)', | ||
'<div>1.appendChild(<div>2)' | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, we are transition from a tree like:
<div class="wrapper">
<div>3</div>
<div>4</div>
</div>
to
<div>1</div>
<div>2</div>
Our diff matches the old div.wrapper
with the new <div>1
node. So it sees a div with multiple children transitioning to a div with a single text child. To handle this, it clears away the old children by setting the textContent
of div.wrapper
to the new single text child. That change is why you no longer see the calls to .remove()
. Also since we set textContent
to the new text child, you don't see the relevant insertBefore
call of the text node. In one operation we unmount old children and set new children.
We do still call unmount
on old children so any lifecycle methods are called, but no DOM is removed in those calls since setting textContent
will take care of that.
5242ac0
to
2a3bba9
Compare
Superseded by #4523 |
This PR adds a (hopefully) fast path for element VNodes with a single text child. Given how common this kind of tree structure (after all, UI is ultimately about fancy ways to display text lol), I hypothesize this should improve our rendering runtime.
Two significant internal changes that comes out of this are:
Single text children no longer are represented as VNodes
With this change, single text children no longer get a VNode created representing them. Hopefully this will help contribute to better runtime and memory performance by removing lots of allocations. But it does have implications for the VNode tree (which leads to the next point).
Elements with a single text child have a
null
_children
propertySince single text childs no longer have a VNode created, the
_children
property of their parents is empty (akanull
). Inspecting the tree VNode tree just using_children
would then lead to an incomplete tree. We could fix this by adding the string to the_children
array, but then that breaks the assumption the codebase has had for a while that_children
is alwaysArray<VNode | null>
. But perhaps that is a better approach?For now, I'm going to try this implementation and see if any performance improvements are observable. If so, then we can further pursue what the right thing do here is.