wilsonpage / fastdom

Eliminates layout thrashing by batching DOM measurement and mutation tasks
6.85k stars 240 forks source link

Why fastdom? #35

Open oskarrough opened 11 years ago

oskarrough commented 11 years ago

Fastdom sounds so efficient that I need to ask what the possible downsides are. Why is this not the default behaviour implemented in modern browsers?

you should wrap any DOM reads with fastdom.read and writes with fastdom.write. They will get batched & executed in next frame...

paullewis commented 11 years ago

The simple reason is that browsers are wired for sync ops. You can set offsetWidth (write) and then ask for its value (read) and that is allowed. FastDOM lets you opt out of that and makes everything async. That is you would get the old value of offsetWidth before the new one was set. That's one example, and there are many properties that would trigger layout that you can Google for.

Async is great, and it would be lovely if this were the normal behaviour. But things would break if this were the default. FWIW Dart actually does DOM updates async, not that it solves this issue, but just so you know there are places where async DOM updates are normal and don't require libraries like FastDOM.

wilsonpage commented 11 years ago

Would it be helpful to have an answer like this on the main readme?

paullewis commented 11 years ago

I think so. Setting out why this even a thing is going to help devs less familiar with the area.

renatoi commented 10 years ago

Sorry to resuscitate the issue but I have a question:

What's the difference between using requestAnimationFrame directly and using fastdom?

wilsonpage commented 10 years ago

Using rAF might be OK for very small issues, but it doesn't ensure you won't be interleaving DOM read and writes, which is the thing we want to avoid.

How can you be sure that another part of your app hasn't scheduled conflicting work for the same frame?

FastDOM 'globally' coordinates when your read/write tasks are run, meaning that they are always run in the most performant way. All you have to know is that when your callback fires, the job is done :)

serapath commented 9 years ago

how does the performance of fastdom compare to virtualdom?

wilsonpage commented 9 years ago

A virtual DOM library could consume a library like Fastdom to help it schedule operations at the correct time.

A virtual DOM library may also do a lot more. Think of fastdom as a lower level tool and a virtual DOM library as higher level abstraction.

All performance comparisons are relative. You'd need to try for yourself.

W. On 7 Feb 2015 03:57, "serapath" notifications@github.com wrote:

how does the performance of fastdom compare to virtualdom?

— Reply to this email directly or view it on GitHub https://github.com/wilsonpage/fastdom/issues/35#issuecomment-73302141.

leandromoreira commented 8 years ago

It might sound silly (just naming and curiosity) but why measure and mutate functions instead of read and write?

Forget about it, I think I know why.

wilsonpage commented 8 years ago

I think they are a little clearer in their intent. For example, you can 'read' an via .getAttribute() without making DOM 'measurements'. With 'mutate' I also feel it's more explicit. Sounds more damaging and should be treated with care.

allyraza commented 6 years ago

what I don't understand why do we need a library when browser actually queue dom operations before doing a repaint.

paullewis commented 6 years ago

@allyraza because the browser won't stop you breaking that behaviour. If you mutate some styles and then request -- say -- offsetLeft, the browser will run layout at that point. So while it's true that browsers do enqueue DOM operations, and that by default they will get applied in a batch, it's also true that you can break that behaviour with JavaScript, and that's what FastDOM is attempting to help with.

crazy4groovy commented 6 years ago

I see a lot of performance solutions suggest a double/inner raf, but fastdom isn't using that technique. Is there a reason? I presume it was tried and measured.

wilsonpage commented 6 years ago

Interesting, can you give more detail/links?

crazy4groovy commented 6 years ago

https://stackoverflow.com/questions/44145740/how-does-double-requestanimationframe-work

wilsonpage commented 6 years ago

This is a really interesting find, I've been giving it some thought. IIUC the issue is that if the DOM is manipulated outside of rAF and then you schedule a rAF with some other work inside, when browser will not actually paint the output of the DOM manipulation until that next frame, so essentially both tasks can end up running in the same frame.

So this issue is really about running blocking code inside rAF handlers delaying the browser beginning animations. I think we need a concrete example to make sense of this; so let's take clicking a navigation button, loading some data and navigating:

button.addEventListener('click', () => {
  fastdom.mutate(() => {
    button.classList.toggle('ripple');
    setTimeout(loadDataAndNavigateTo('some-page'));    
  });
});

I'm mutating the DOM and then, scheduling the loading and navigation at the end of the task queue so that is doesn't block the animation from starting. You could optionally use rAF instead of setTimeout(), but then you risk blocking other animations and render work.

This is a bit gross looking, I think I'd rather have a way to run something after the fastdom task is complete (AKA the animation has begun). Like:

button.addEventListener('click', async () => {
  await fastdom.mutate(() => button.classList.toggle('ripple'));
  loadDataAndNavigateTo('some-page');
});

That would require than we return a Promise once the fastdom task has been run, and might make this kind of use-case a bit prettier. We'd have to make sure this was after the internalrAF is fully complete.

Does that make sense? Have I understood the problem? :)

crazy4groovy commented 6 years ago

That's an interesting thought. Honestly I don't know enough low-level to give an opinion, but returning a "blocking" promise that resolves when the mutation is complete makes sense.

I was just looking particularly at https://github.com/wilsonpage/fastdom/blob/master/fastdom.js#L158 and wondered if fastdom.raf(fastdom.raf(flush.bind(null, fastdom))); would be more performant? I think only empirical perf tests would determine that.

Mapiac commented 5 years ago

Is this still a commonly (and more so recommended) library in 2019? With render, layout and paint speeds essentially so fast and with RAF, just wondering if this great library is still recommended? Maybe the benefits of batch reads and writes persist no matter how fast V8 engine or device CPU are?

wilsonpage commented 5 years ago

I hope fastdom disappears over time. I wouldn't reach for fastdom without diagnosing that you have a render per issue with your app. As with everything it depends. If jank is still a think on the mobile web, then there will likely be a place for fastdom.

If the DOM is abstracted away from you via a framework like React, you probably don't need fastdom.

Mapiac commented 5 years ago

Thanks. Not using any VDOM or really any frameworks @wilsonpage. Just Cordova. Long scrolling lists and unfortunately large DOM even though we have made significant strides to reduce. However we've had FastDOM in for years and I was considering taking it out in consideration of the advancements these past CPL years.

yellow1912 commented 4 years ago

For long scroll you may want to check virtual scroll technic.