-
-
Notifications
You must be signed in to change notification settings - Fork 1.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
⚡ Improves x-for performance #4361
base: main
Are you sure you want to change the base?
Conversation
while I don't necessarily disagree with some of the formatting changes in here, wouldn't it be best to remove them for now to make the PR easier to review? |
Sure. I think there's like 2 or 3 that just got caught up in the mix. They're quite separate from the main code so it shouldn't be too much noise though. When I get the test in, I can correct some of those. |
templateEl._x_lookup = lookup | ||
const scopeEntries = [] | ||
const hasStringKeys = isObject(items) | ||
Object.entries(items).forEach(([index, item]) => { |
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.
Using Object.keys
and then doing items[key]
is more verbose but may be faster and more memory efficient, though I am not sure.
Also the forEach could be replaced with a standard for
loop which is faster too.
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.
For loops are generally not faster than array methods in modern JS runtimes. The engine can more efficiently loop than doing it in your own code. Since the method doesn't need to be concerned about breaks and continues and returns and such. It's quite interesting getting into the meat of that, but mostly the array methods are just clearer logically.
Though really this should probably be a map
for maximum efficiency 🤔
The Object.keys is to make it work with Arrays and Objects with a single code path.
Could maybe be handled in a different way, like just separately replacing items when it's an object with an array of entries.
I need to get that test made and other tweaks.
This also solves #4192 #4157 related to using
x-sort
withx-for
.The core issue is that x-for never checks if any elements have moved, due to how it is optimized.
See in playground
I looked at just solving that directly, but it would definitely just increase work and increase code.
I identified that, if we want to just always ensure we put the elements in the right place, then we could also massively simplify the logic while maintaining the vast majority of the logic and optimizations, and solve this problem.
Looks like my test for the x-sort issue is missing so I'll get that in place.
Note: As I worked through this, I was quite impressed with how optimized Alpine actually was in the first place. It wasn't a joke when you said it was complexity with a purpose. I was quite worried I'd end up in a situation where the code logic was uglier, longer, slower, and there were times when the old version was doing way better at things I wasn't sure how to handle effectively. Had to do a lot of profiling to find the simplest fixes. Wrote a blog post about the steps, process, logic.
I think I got there. Smaller, faster, and I think many would agree clearer logic.
Synthetic Benchmarks
Some synthetic benchmarks that isolated just the x-for logic. This number includes the browser repaint/reflow, not purely logical gains. For most, the repaint/reflow is the same/similar for both.
JS Framework Benchmarks
Extra Huge Lists
For fun.