luwes / sinuous

🧬 Light, fast, reactive UI library
https://sinuous.netlify.app
1.03k stars 34 forks source link

Subscribe without initial run #109

Closed lastonoga closed 4 years ago

lastonoga commented 4 years ago

Hey!

I am developing SFC and something like NuxtJS for sinuous with partial hydration.

Partial hydration is hydration that works only with dynamic and statefull parts of application. It helps to use component and reactive paradigm for website development (not applications and SPA).

Its pre-alpha and under development (https://github.com/lastonoga/sinuous-components)

Well.. I need to make api.subscribe but without initial run, because initial run is made by server (SSR). That should speed up First Input Delay.

I will really appreciate if you help me with that. @luwes

lastonoga commented 4 years ago

Can be solved with that snippet

function addSubscriber(value, fn, skip = true)
{
    api.subscribe(() => {
        let v = value();

        if(skip) {
            skip = false;
            return;
        }

        fn(value);
    });
}
luwes commented 4 years ago

@lastonoga thanks for reaching out. your project looks very cool, love the performance stats :) I see you already found a solution, by the way this was included in sinuous/observable as well, see https://github.com/luwes/sinuous/tree/master/packages/sinuous/observable#on Discussed in issue https://github.com/luwes/sinuous/issues/54

lastonoga commented 4 years ago

@luwes Thanks!

Yes, i found that method but it works a little bit different.

Maybe you know some good ways (or project) to test hydration like https://github.com/krausest/js-framework-benchmark?

Because now it's too dirty, we need normal tests.

luwes commented 4 years ago

I actually never tried to benchmark hydration, and I haven't seen any benchmarks on that yet. maybe @ryansolid, have you seen any benchmarks around this?

ryansolid commented 4 years ago

I was looking for this recently as this has been an area I've been exploring a ton with Solid recently. I was thinking of building some. There are SSR tests definitely around the speed of rendering but I"m not sure they actually test hydration speed. I think establishing a baseline is hard. So far I've been just looking at demo apps like the real world demo, and looking at Lighthouse Audits, or Chrome Timeline to see TTI, FCP etc.. I haven't come across any benchmarks specifically for this. MarkoJS has good SSR benchmarks I need to dig a bit to see if they have anything comparing with hydration.

@lastonoga this is a cool project. SSR and Hydration is not my strength(I have almost no experience using these sort of libraries) but I've felt that I've needed to look into it since well no one else was for reactive fine-grained libraries. Yet it is one of the most asked for features. I managed to put something simple together for progressive hydration and async hydration with code splitting. I've been using that to build the SolidJS website but I haven't made any progress on the partial hydration side. I hope to learn from the work you are doing. Really cool work.

lastonoga commented 4 years ago

@ryansolid Thanks!

There is a benchmark (https://github.com/marko-js/isomorphic-ui-benchmarks) that MarkoJS uses. I gonna try it to get stats.

I hope too, but i need to make refactoring first.

lastonoga commented 4 years ago

@luwes Sorry for interrupting you.

Is there any change to make lightweight subscriber? Method On is cool and doing exactly what i want (with little difference), but there is huge overhead that slows down partial hydration.

Creation of 10 000 computed functions is a bit long process. And i need to manage observes without usage computed function (binding observables without function call by hand)

ryansolid commented 4 years ago

You need to manage disposal though too. These patterns have the classic observer pattern issue if the observables outlive their observers. While it's possible to subscribe directly to an observable without executing a tracking function execution(mind you computed(obsv) more or less is the same thing since it only executes the reading of the observable), the system is setup to cleanup after itself keeping a copy of the subscription on both sides.

A lighter computation could skip on keeping track of its observed signals but would need to manually unsubscribe from them. It also could not host the creation of any nested computations. Ideally it wouldn't hold a value either. S.js does a lot of similar optimizations (although it keeps it linked on both sides, but it avoids creating unnecessary arrays especially on single subscribers). Would need to test how much creation time it saves I've seen this save upwards of 20ms on limited benchmarks over 1000 rows, 10k I imagine could be more than that (although I suspect not 10x, probably like 3-4x). I think the lack of automatic book keeping is problematic but it could be used to sort of test an idea out.

Computations manage their own cleanup (upon re-execution). It would be interesting to just remove that out of the equation for on or createDependentEffect. Although this i an update cost savings not a creation one. You are already skipping first run (which these sort of methods allow because they know dependencies upfront) I don't think it saves anything additional up front.... Yeah the only thing I can think of to reduce initial cost is potentially delegate cleanup, but if you are managing the cleanup yourself that probably isn't much of a savings. Computeds are fairly simple. There are some memory optimizations that Sinuous could do here at the cost of size. @luwes I did play with a version of Sinuous Observable for a bit during my reactive rewrite, and was able to get some pretty decent numbers(like double the score in S.js benchmark) but at like 25-30% code size increase. But there are probably places that could be addressed before that made a meaningful enough impact.

luwes commented 4 years ago

@lastonoga I'm not sure how to improve the perf for that. The proposal of @ryansolid seems like it could help but 25% increase in size will be too much for adding to the Sinuous prod build I believe.

why are there 10000 computations added on the page? is it just for the bench?