-
Notifications
You must be signed in to change notification settings - Fork 49
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
Rewrite implementation #170
Conversation
I haven't had time to look at this closely and won't in the near/not-so-near future :( IMO, if you've been using it successfully in projects already, you should feel free to merge this PR. |
No problem @goto-bus-stop. Actually, I'll go ahead and close this. I have a way less bulky version in the works but I'm hesitant to wether this is a reasonable evolution of If development on |
This is pretty much a complete rewrite of the implementation aimed at solving three specific issues with nanohtml:
This is not a finished implementation, but I wish to get a discussion going and would like to get feedback on this from the community. Included in the PR is a sample implementation of a component interface as well as a wrapper for lazy (async) components with fallback – these are in a state of experimentation.
API
Performance
Nanohtml is commonly used with nanomorph. Though the DOM isn't necessarily slow, rebuilding the complete DOM tree on each update and diffing + morphing that tree does impose a significant performance overhead.
This implementation is based on an implementation by @goto-bus-stop (https://gist.github.com/goto-bus-stop/5b54d652af860f614a1dcba28eb80691). In short, it identifies which parts of the template might change and creates bindings for updating these parts. The bindings are stored in a
WeakMap
with the element as key, meaning they can be garbage collected when the element is removed. Templates are only parsed once and cached with the template itself as key – thanks to the neat feature of template literals that the template array is unique per template, not per invocation.This approach has resulted in a ~11x performance improvement over nanohtml + nanomorph.
Components
The most popular implementation of components for nanohtml is nanocomponent. The interplay between nanomnorph and nanocomponent sometimes results in leaking proxy nodes and a complex and flawed diffing algorithm. Nanocomponent in itself comes with a sometimes overly verbose API and demands quite complex implementations to determine if the component should update. Authors also have to manually manage component instances.
This implementation does not completely replace nanocomponent, but it offers an API for doing so. By extending the built in
Partial
class (requires implementing therender
andupdate
methods) one can create any kind of component interface. I have taken a stab at it with nanohtml/component. A few examples of use can be seen in this gist.See an example implementation of a stateful component
Asynchronous render
As part of choo the most prominent behaviour for async render to emit an event, render some loading state and once data is available, issue another render. In my experience, this works fine for consumer websites and simple web apps, but as complexity increases this has proven quite the bottleneck. The issue is most prominent when you have nested async dependencies, e.g. fetch list of things and then fetch item from list. This problem is especially troublesome when doing SSR. One solution, used by bankai is to perform a double render pass, once to collect promises and once again to render with resolved data. The API for this isn't really ideal and I find that it is prone to race condition, especially when having nested async dependencies.
This implementation addresses promises in tandem with generators. This is so that a component can be both asynchronous and synchronous, exposing promises when the data is not cached, but being rendered synchronously once it is.
The server side implementation unwinds generators to find promises and returns a promise which resolves to the rendered HTML string – pretty much what @diffcunha proposes in choojs/choo#646.
I've started work on a way to render fallback content while a promise is being resolved but need to work out how to handle race conditions which easily arise when content changes while awaiting promise resolution, see nanohtml/lazy and accompanying tests.
Other benefits
Trade-offs