* De-servicify router (mostly) Still uses the redraw service, but it no longer has an intermediate service of its own. Also, did a *lot* of test deduplication in this. About 30-40% of the router service tests were already tested on the main router API instance itself. Bundle size decreased from 9560 to 9548 bytes min+gzip. * Merge `m.mount` + `m.redraw`, update router Simplifies the router and redraw mechanism, and makes it much easier to keep predictable. Bundle size down to 9433 bytes min+gzip, docs updated accordingly. * Make `mithril/render` just return the `m.render` function directly. * Deservicify `m.render`, revise `m.route` - You now have to use `mithril/render/render` directly if you want an implicit redraw function. (This will likely be going away in v3.) - Revise `m.route` to only `key` components * Add `redraw` to `m.render`, deservicify requests * Test error logging * Update docs + changelog [skip ci]
4.5 KiB
render(element, vnodes)
- Description
- Signature
- How it works
- Why Virtual DOM
- Differences from other API methods
- Standalone usage
Description
Renders a template to the DOM
m.render(document.body, "hello")
// <body>hello</body>
Signature
m.render(element, vnodes, redraw)
| Argument | Type | Required | Description |
|---|---|---|---|
element |
Element |
Yes | A DOM element that will be the parent node to the subtree |
vnodes |
`Array | Vnode` | Yes |
redraw |
() -> any |
No | A callback invoked each time an event handler in the subtree is invoked |
| returns | Returns undefined |
How it works
The m.render(element, vnodes) method takes a virtual DOM tree (typically generated via the m() hyperscript function), generates a DOM tree and mounts it on element. If element already has a DOM tree mounted via a previous m.render() call, vnodes is diffed against the previous vnodes tree and the existing DOM tree is modified only where needed to reflect the changes. Unchanged DOM nodes are not touched at all.
If you pass the optional redraw argument, that is invoked each time an event handler anywhere in the subtree is called. This is used by m.mount and m.redraw to implement the autoredraw functionality, but it's also exposed for more advanced use cases like integration with some third-party frameworks.
m.render is synchronous.
Why Virtual DOM
It may seem wasteful to generate a vnode tree on every redraw, but as it turns out, creating and comparing JavaScript data structures is surprisingly cheap compared to reading and modifying the DOM.
Touching the DOM can be extremely expensive for a couple of reasons. Alternating reads and writes can adversely affect performance by causing several browser repaints to occur in quick succession, whereas comparing virtual dom trees allows writes to be batched into a single repaint. Also, the performance characteristics of various DOM operations vary between implementations and can be difficult to learn and optimize for all browsers. For example, in some implementations, reading childNodes.length has a complexity of O(n); in some, reading parentNode causes a repaint, etc.
In contrast, traversing a JavaScript data structure has a much more predictable and sane performance profile, and in addition, a vnode tree is implemented in such a way that enables modern JavaScript engines to apply aggressive optimizations such as hidden classes for even better performance.
Differences from other API methods
m.render() method is internally called by m.mount(), m.route(), m.redraw() and m.request(). It is not called after stream updates
Unlike with m.mount() and m.route(), a vnode tree rendered via m.render() does not auto-redraw in response to view events, m.redraw() calls or m.request() calls. It is a low level mechanism suitable for library authors who wish to manually control rendering instead of relying on Mithril's built-in auto-redrawing system.
Another difference is that m.render method expects a vnode (or a array of vnodes) as its second parameter, whereas m.mount() and m.route() expect components.
Standalone usage
var render = require("mithril/render")
The m.render module is similar in scope to view libraries like Knockout, React and Vue. It implements a virtual DOM diffing engine with a modern search space reduction algorithm and DOM recycling, which translate to top-of-class performance, both in terms of initial page load and re-rendering. It has no dependencies on other parts of Mithril aside from normalization exposed via require("mithril/render/vnode") and can be used as a standalone library.
Despite being relatively small, the render module is fully functional and self-sufficient. It supports everything you might expect: SVG, custom elements, and all valid attributes and events - without any weird case-sensitive edge cases or exceptions. Of course, it also fully supports components and lifecycle methods.