Skip to content
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

New approach for mithril rendering? #2307

Closed
ChrisGitIt opened this issue Nov 20, 2018 · 15 comments
Closed

New approach for mithril rendering? #2307

ChrisGitIt opened this issue Nov 20, 2018 · 15 comments
Labels
Type: Question For issues that are purely questions about Mithril, not necessarily bug reports or suggestions

Comments

@ChrisGitIt
Copy link

Hi everybody,

i wonder what the mithril community is saying about the "new tech" that is currently coming up.

Me, personally, try to wrap my head around the

1: shadow dom/ web component
Because it seems it becomes natively available within the current major browsers and might take a lot of rendering performance done by nativ browser specs.

2: Memoized DOM
Here is a recent article: https://medium.freecodecamp.org/the-virtual-dom-is-slow-meet-the-memoized-dom-bb19f546cc52

Are there any other new approaches that are discussed within the mithril community? Is the current VDOM approach still "state of the art"?

I'm surely very satisfied with mithril and its overall approach, but i wonder if other approaches seem to be more future proof ...

Feedback appreciated!

Greets, Chris

Bye the way: I have made mithril hydration script that takes server side rendered dom (https://github.com/MithrilJS/mithril-node-render/) and hydrates it to be used for mithril. Thats mainly to avoid the FOUC when m.route / m.render initiates the app. Anyone interested? Its currently working buttery smooth on a bigger project.

@project-bot project-bot bot added this to Needs triage in Triage/bugs Nov 20, 2018
@porsager
Copy link
Contributor

Hi @ChrisGitIt ... There's certainly a lot of new exploration going on now that the vdom dust has settled. @isiahmeadows is also working on a new approach, so I'm sure he has a lot of interesting things to say.

My own preference is for mithrils hyperscript and lifecycle methods, so if it's a vdom or some other implementation below, I actually don't care much. I haven't bumped into any perf. issues I wasn't able to solve with mithril easily so I haven't been that drawn by other approaches that have popped up lately.

Your work on a hydration solution sounds very interesting, so if you'd be willing to share that I think a lot of people would be interested! Don't hesitate to come by the chat on gitter as well!

@leeoniya
Copy link
Contributor

leeoniya commented Nov 20, 2018

to do Memoized DOM in any meaningful way, you really have to pre-compile templates, which usually means writing templates that are no longer "just javascript", but a custom DSL*. the two paradigms are fundamentally incompatible. if you look at imba, it has its own template language. perhaps something like Vue or the original AngularJS could have mutated into something like this, but i dont think this can be shoehorned into any js-centric vdom framework.

i think there's room for implementing a detached dom node cache, given that you can solve one of the two most difficult problems in computer science: cache invalidation :) [1]

* i guess with sufficiently complex static analysis, like Closure Compiler level of complexity, perhaps it's possible to transform some part of a JavaScript AST into static templates you'd have to detect loop invariants, context/closures, string-key object accesses, etc. and give the compiler knowledge of vdom-style h functions.

/$0.02

[1] https://martinfowler.com/bliki/TwoHardThings.html

@ChrisGitIt
Copy link
Author

Hi @porsager, and thanks for your feedback! I'm also very satisfied with the current mithril way and smile about other frameworks that are "opinionated" and mostly have a high learning curves.

About my hydration function:
You can see the mithril hydration in action on https://www.firstmallorca.com/en. Its mainly using a barely patched mithril version where

m.route(element, '/', routes);
got patched with a fourth argument like this

m.route(element, '/', routes, mithriHydrateFunction);

Within the mithril "render", there is only one line added that checks if there is currently a vdom created or not. The main problem i'm currently facing is when the rendered DOM is different than the mithril DOM. This only happens when the server side rendered dom is saved and served statically (also via CDN).

@porsager
Copy link
Contributor

Wow.. That's a fast loading and good looking page @ChrisGitIt 👍

I'd love to try out your approach with bss in the mix which I'm also fixing up to support server side rendering currently..

@ChrisGitIt
Copy link
Author

ChrisGitIt commented Nov 20, 2018

Hi @porsager,

thanks for the compliments. The design is not my work. "Only" everything else ;-)

I just noticed that the js on the website is minfied. On test.firstmallorca.com you can see the unminified source code.

About using a JS-CSS approach: Currently working on a approach that works with SSR (server side rendering) AND that spills out the "above the fold" automatically.

I'm thinking about something where there is a special command that you place within your mithrils "main view" to accumulate the css until this point. Would look like this

function view(vnode) {
        return m('main', [
                m(component_nav),
                m(component_header),
                my-js-css.stopInlineCss(),
                m(component_content),
                m(component_footer)
        ])
}

@ACXgit
Copy link
Contributor

ACXgit commented Nov 20, 2018

Hi @ChrisGitIt , I'm definetely interested in your hydration script!

@dead-claudia
Copy link
Member

@ChrisGitIt

1: shadow dom/ web component
Because it seems it becomes natively available within the current major browsers and might take a lot of rendering performance done by nativ browser specs.

Fun fact: web components are slow, often slower than normal DOM elements. Native ≠ fast, and in this case, it's in part thanks to quite a bit of misplaced object-oriented indirection in the form of the shadow DOM itself. (Compare the performance of Vue and Ember with that of Polymer - they're all three similar to use, but they all three have very different performance curves, and Polymer isn't even close to the rest with list updates.) Custom elements and templates are still useful - custom elements make for some nice APIs, and HTML templates are way faster than document.createElement + elem.appendChild for building large DOM fragments.

Also, people suck at correctly managing mutation + asynchrony - most developers struggle how to correctly grasp it and rein it in, especially when using imperative languages. (Try writing a fast HTTP/2 implementation - it's harder than you'd think.)

Really, the main bottleneck here is list updates.

2: Memoized DOM
Here is a recent article: https://medium.freecodecamp.org/the-virtual-dom-is-slow-meet-the-memoized-dom-bb19f546cc52

Are there any other new approaches that are discussed within the mithril community? Is the current VDOM approach still "state of the art"?

Virtual DOM is not the only approach anymore, and hasn't been for the last couple years:

  • Inferno is probably the pinnacle of how fast a virtual DOM library could be while still remaining relatively user-friendly.
  • React first abandoned the traditional virtual DOM implementation with Fiber (which is task-/continuation-based) and has since moved on from some of the core API concepts with Hooks, Time Slicing, Suspense, and concurrent mode (all four currently WIP).
  • Ember's Glimmer is architected as a VM, which makes incremental updates literally trivial. They compete with Inferno in speed because they use static analysis to rule out 99% of potential mutations, but they are a bit slower than it when it comes to list updates and keyed updates.
  • Incremental DOM patches the DOM in-place without using userland data structures, and the API mimics traditional immediate-mode UIs a lot. They designed it in part to work with their Closure templates, but a userland JSX Babel plugin exists for it.
  • Svelte uses a Handlebars-inspired template language and compiles it down largely to raw DOM manipulation (consider this, but look at its output). It's inspired syntactically by web components, but it doesn't use that internally.
  • Data-driven architectures (like entity component systems) are really popular in the game dev community, and they work very well for most things I've encountered that aren't hierarchical, like network visualizers and interactive maps. You don't need a full entity-component framework for this - you only really just need lists of objects representing individual objects that you iterate every so often to read and/or update with. Note that in this kind of scenario, you're interfacing with the DOM directly.

There's been a bit of development in that area recently, so you can be forgiven for not catching on. Angular (starting with v4 IIRC) and Vue both use a traditional virtual DOM reconciler internally, so virtual DOM isn't going anywhere for a while, and this includes Mithril.

@dead-claudia dead-claudia removed this from Needs triage in Triage/bugs Nov 26, 2018
@dead-claudia dead-claudia added the Type: Question For issues that are purely questions about Mithril, not necessarily bug reports or suggestions label Nov 26, 2018
@ghost
Copy link

ghost commented Nov 27, 2018

@isiahmeadows

One thing I thought of that could be very useful, is taking the incremental-dom approach (where you patch the DOM in-place after an initial render) but doing all the pre-work in simple document fragments

You could do a diff against A single document fragment (or many, doens't matter) before truly rendering out to the dom. This would give a couple of things (I been thinking about this alot since I got back from an extended leave)

This would still maintain the strengths (and largely leave the vdom implementation as is, with some optimizations)

  1. asynchrounous patches and renders could be easily achieved, since you only push the fragment(s)

  2. In theory, this could remove the need to have separate render/mount functions, since you could just target fragments on the page (and generate new fragments based on different mountpoints, which could make mounting against say, parent classes rather than element id selectors, easier?)

  3. There is a speed benefit (no repaints is a nice win) this was taken from a stack overflow article

  4. Since its a document fragment, you largely get the same APIs exposed as dealing directly with document, exception the subset of child elements is only the ones you care about (could be useful for building components on?)

  5. One last thing I thought of, which may or may not be of interest: you could fire the life-cycle events as Custom Events too, this way, if that matters at all (I could see how this could, particularly as a consumer of the APIs. For instance: if I wanted something else happening on the page that may not be controlled/rendered by mithril directly, to fire an event that I want to trigger a lifecycle method, perhaps. While you could do this easily by just tieing it to an event attached to an element, this would make it easier, but might not be worth the headache?)

  6. If I understand WebWorkers correctly, you could also do an RPC type thing with mithril where web workers yield fragments for rendering as well, this actually moves it off the main thread (note: I don't mean Service Workers though you could do something there potentially, I imagine. Just noting this for others)

I don't know if you have considered this. I would be very interested in putting together a demo if you want to explore this concept further. I don't know if I articulated this very well (i'm a little squeezed for time at the time of this writing ha!)

Maybe this idea has some legs for making improvements? I'm just happy to hear ideas!

I'm also happy to see mithril is seeing alot of new activity again, and I'm willing to help when/if able!

@leeoniya
Copy link
Contributor

You could do a diff against A single document fragment (or many, doens't matter) before truly rendering out to the dom.

document fragments are emptied once they're inserted into the live dom, so there's nothing left in them to diff against.

@ghost
Copy link

ghost commented Nov 27, 2018

You could do a diff against A single document fragment (or many, doens't matter) before truly rendering out to the dom.

document fragments are emptied once they're inserted into the live dom, so there's nothing left in them to diff against.

Oh fair enough (having not investigated it closely as I thought, didn't now that). Is it possible that it might be worthwhile to explore window.localStorage to cheaply save and compare? or am I just getting out of the realm of what we might want to do with that?

Though, I think rendering against a docfragment and keeping the state elsewhere would serve still be a worthwhile endeavor considering the other factors.

@dead-claudia
Copy link
Member

@ProtonScott If you read the source, we already use fragments for quite a few things, including rendering Mithril fragments.

@ghost
Copy link

ghost commented Nov 28, 2018

@isiahmeadows I see, maybe the aptly named $doc threw me off in my search (or maybe its been too long since I looked at the source code).

So what do you think the way to spearhead is the way to go to improve rendering/performance?

@dead-claudia
Copy link
Member

@ProtonScott To address the other points:

@isiahmeadows

One thing I thought of that could be very useful, is taking the incremental-dom approach (where you patch the DOM in-place after an initial render) but doing all the pre-work in simple document fragments

You could do a diff against A single document fragment (or many, doens't matter) before truly rendering out to the dom. This would give a couple of things (I been thinking about this alot since I got back from an extended leave)

We already do this a lot.

This would still maintain the strengths (and largely leave the vdom implementation as is, with some optimizations)

  1. asynchrounous patches and renders could be easily achieved, since you only push the fragment(s)

We're missing browser APIs for that. That's the real bottleneck in the DOM currently. Browsers already currently render off-thread as much as possible, so async paints aren't an issue. It's been discussed before, since we aren't the only ones who need it (it's something most frameworks could use):

Sadly, nothing meaningful has arisen out of it yet.

  1. In theory, this could remove the need to have separate render/mount functions, since you could just target fragments on the page (and generate new fragments based on different mountpoints, which could make mounting against say, parent classes rather than element id selectors, easier?)

m.render just does a single render + update. m.mount lets you subscribe to updates, update the last tree, and automatically redraw based on various events.

  1. There is a speed benefit (no repaints is a nice win) this was taken from a stack overflow article
  2. Since its a document fragment, you largely get the same APIs exposed as dealing directly with document, exception the subset of child elements is only the ones you care about (could be useful for building components on?)
  3. One last thing I thought of, which may or may not be of interest: you could fire the life-cycle events as Custom Events too, this way, if that matters at all (I could see how this could, particularly as a consumer of the APIs. For instance: if I wanted something else happening on the page that may not be controlled/rendered by mithril directly, to fire an event that I want to trigger a lifecycle method, perhaps. While you could do this easily by just tieing it to an event attached to an element, this would make it easier, but might not be worth the headache?)

Custom events would be slower. Currently, hooks are literally just simple function calls, and custom events would require a lot more ceremony to fire and listen to.

  1. If I understand WebWorkers correctly, you could also do an RPC type thing with mithril where web workers yield fragments for rendering as well, this actually moves it off the main thread (note: I don't mean Service Workers though you could do something there potentially, I imagine. Just noting this for others)

Just trust me in that it's easier said than done. 😉 It'd also take a lot of code.

I don't know if you have considered this. I would be very interested in putting together a demo if you want to explore this concept further. I don't know if I articulated this very well (i'm a little squeezed for time at the time of this writing ha!)

Maybe this idea has some legs for making improvements? I'm just happy to hear ideas!

I'm also happy to see mithril is seeing alot of new activity again, and I'm willing to help when/if able!

@ghost
Copy link

ghost commented Nov 28, 2018

@ProtonScott To address the other points:

@isiahmeadows
One thing I thought of that could be very useful, is taking the incremental-dom approach (where you patch the DOM in-place after an initial render) but doing all the pre-work in simple document fragments
You could do a diff against A single document fragment (or many, doens't matter) before truly rendering out to the dom. This would give a couple of things (I been thinking about this alot since I got back from an extended leave)

We already do this a lot.

This would still maintain the strengths (and largely leave the vdom implementation as is, with some optimizations)

  1. asynchrounous patches and renders could be easily achieved, since you only push the fragment(s)

We're missing browser APIs for that. That's the real bottleneck in the DOM currently. Browsers already currently render off-thread as much as possible, so async paints aren't an issue. It's been discussed before, since we aren't the only ones who need it (it's something most frameworks could use):

Sadly, nothing meaningful has arisen out of it yet.

  1. In theory, this could remove the need to have separate render/mount functions, since you could just target fragments on the page (and generate new fragments based on different mountpoints, which could make mounting against say, parent classes rather than element id selectors, easier?)

m.render just does a single render + update. m.mount lets you subscribe to updates, update the last tree, and automatically redraw based on various events.

  1. There is a speed benefit (no repaints is a nice win) this was taken from a stack overflow article
  2. Since its a document fragment, you largely get the same APIs exposed as dealing directly with document, exception the subset of child elements is only the ones you care about (could be useful for building components on?)
  3. One last thing I thought of, which may or may not be of interest: you could fire the life-cycle events as Custom Events too, this way, if that matters at all (I could see how this could, particularly as a consumer of the APIs. For instance: if I wanted something else happening on the page that may not be controlled/rendered by mithril directly, to fire an event that I want to trigger a lifecycle method, perhaps. While you could do this easily by just tieing it to an event attached to an element, this would make it easier, but might not be worth the headache?)

Custom events would be slower. Currently, hooks are literally just simple function calls, and custom events would require a lot more ceremony to fire and listen to.

  1. If I understand WebWorkers correctly, you could also do an RPC type thing with mithril where web workers yield fragments for rendering as well, this actually moves it off the main thread (note: I don't mean Service Workers though you could do something there potentially, I imagine. Just noting this for others)

Just trust me in that it's easier said than done. 😉 It'd also take a lot of code.

I don't know if you have considered this. I would be very interested in putting together a demo if you want to explore this concept further. I don't know if I articulated this very well (i'm a little squeezed for time at the time of this writing ha!)
Maybe this idea has some legs for making improvements? I'm just happy to hear ideas!
I'm also happy to see mithril is seeing alot of new activity again, and I'm willing to help when/if able!

@isiahmeadows Welp, turns out I'm a complete noob at this, aren't I ? 🙁 😆

I appreciate the detailed response, very much so. I will just put the hat in the bag for now.

@dead-claudia
Copy link
Member

Closing this in favor of #1838.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Question For issues that are purely questions about Mithril, not necessarily bug reports or suggestions
Projects
None yet
Development

No branches or pull requests

5 participants