vanjs-org / van

🍦 VanJS: World's smallest reactive UI framework. Incredibly Powerful, Insanely Small - Everyone can build a useful UI app in an hour.
https://vanjs.org
MIT License
3.77k stars 87 forks source link

Smallest reactive library #293

Open anywhichway opened 5 months ago

anywhichway commented 5 months ago

VanJS is not quite the smallest anymore, see https://github.com/anywhichway/trui. Although VanJS certainly rocks and it likely to get better support and be more pervasive. If there is any interest, I would be willing to take a go at making trui API compatible and explore reducing the size of the VanJS core with a contribution.

Tao-VanJS commented 5 months ago

Thanks @anywhichway for sharing your library! This is definitely interesting work.

First, as of VanJS 1.5.0, the gzipped + minified bundle of VanJS is 1,055 bytes, not 1,704 bytes as mentioned in https://github.com/anywhichway/trui. I guess VanJS is still smaller :-)

Second, without a good document and a well-covered test suite, it's really hard to verify whether there are serious bugs in the code. Also without a good document, it's not even clear how the library is supposed to be used. For one thing, there isn't any garbage collection implemented in trui. I suspect there will be memory leaks in the scenarios described in this section.

Again, thank you for your work and sharing it!

anywhichway commented 5 months ago

Odd, I get 3.19kb with terser and then down to 1704 with gzip. Are you calculating based on a module or non-module file?

Also oddly, if I take your latest minified file from public and gzip it I get 1384 characters, still larger than 1,055 bytes ... so I am not sure what to measure! Although I doubt I would ever match it anyway, so I adjusted the repo :-).

Granted I have not pounded on it yet, but I have not found any leaks using Chrome memory tracking. Primary references are stored in WeakMaps and secondary in regular Maps, these are continuously pruned. And yeah, test suite is not yet in place, which is why it has an "a" in the version.

The library is used pretty much the same way as VanJS, the examples in the doc are just about all based on VanJS examples.

I am sure you don't need any more noise in your repo. OK to close on your next reply.

Tao-VanJS commented 5 months ago

I think it's perfectly ok to keep this thread open :-) These are interesting discussions. I think it's also beneficial to VanJS community to have relevant work discussed.

Odd, I get 3.19kb with terser and then down to 1704 with gzip. Are you calculating based on a module or non-module file?

For the minified file, van-1.5.0.min.js, it's 2035 bytes (the non-ESM counter part, van-1.5.0.nomodule.min.js is 2076 bytes). It is generated by terser with some specialized flags for further size optimization (see the cmdline).

Also oddly, if I take your latest minified file from public and gzip it I get 1384 characters, still larger than 1,055 bytes ... so I am not sure what to measure! Although I doubt I would ever match it anyway, so I adjusted the repo :-).

When van-1.5.0.min.js is gizpped, it will produce a 1055-byte .gz file. I am using gzip -kf <file path> (cmdline). Gzip version: Apple gzip 428, running in macOS 14.1.1 (Sonoma), Apple M1.

Regarding the memory leak, if I understand correctly, in trui, you're using dMap to manage the bindings of states. I only see new bindings being added to dMap, not seeing any of the bindings being cleaned up. For the example listed in this section, whenever the value of renderPre flips, the text state will be registered as a dependency of a new Text node. I am not seeing in your code that these registered bindings are being cleaned up anywhere, so eventually, there will be lots of bindings registered for text state, all except the last registered binding are obsolete. Eventually this will lead to lots of RAM allocated for nothing as the user interacts with the page more and more without reloading.

anywhichway commented 5 months ago

Thanks for the continued dialog.

May have added these after you took your initial pass, not sure. May actually get some garbage from stranded observers.

    for(let d of [...set||[]]) {
        if(typeof d === "function") d(); // dependent is an observer not an element
        else if(!d.isConnected) set.delete(d);
        else if(d.isConnected && d!==this) rMap.get(d)?.call(d); // gets the render function for the dependent element
    }
if (this instanceof Node) {
    rMap.delete(this);
    this.replaceWith(e);
}
Tao-VanJS commented 5 months ago

In the example listed in this section, the mechanism you proposed works when text state changes frequently. However, in a scenario where renderPre state changes far more frequently than text state, the bindings of text state can still be accumulated without getting a chance to be cleaned up as the code you propose will only be executed when text state changes.

Also, it's a bit more complicated to support garbage collection for derived states (observe in trui), which was introduced in VanJS 1.1.0. Otherwise, memory leaks can still occur for derived states for scenarios as the example listed in this section.

anywhichway commented 5 months ago

Thanks!

On Wed, Mar 27, 2024 at 9:09 PM Tao Xin @.***> wrote:

In the example listed in this section https://vanjs.org/advanced#gc, the mechanism you proposed works when text state changes frequently. However, in a scenario where renderPre state changes far more frequently than text state, the bindings of text state can still be accumulated without getting a chance to be cleaned up as the code you propose will only be executed when text state changes.

Also, it's a bit more complicated to support garbage collection for derived states (observe in trui), which was introduced in VanJS 1.1.0 https://github.com/vanjs-org/van/discussions/81. Otherwise, memory leaks can still occur for derived states for scenarios as the example listed in this section https://vanjs.org/advanced#derived-states-and-side-effects-registered-inside-a-binding-function .

— Reply to this email directly, view it on GitHub https://github.com/vanjs-org/van/issues/293#issuecomment-2024359980, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABF2US2MK5QYV6KV6GKHKLTY2OJXNAVCNFSM6AAAAABFLK6XJCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMRUGM2TSOJYGA . You are receiving this because you were mentioned.Message ID: @.***>