* Recast the router API to be a lot more intuitive. Fixes #2387 Fixes #2072 Fixes quite a few issues reported on Gitter. For `m.route.Link`: - More intuitive - More accessible - More ergonomic - It can be disabled - It can be cancelled - It can be changed - Oh, and you can use it isomorphically. For `m.route.prefix` - You can *read* it. - You can write to it, of course. - It's literally just setting a property. For the router itself (and the rest of Mithril): - You can now `require("mithril")` and all its submodules without a DOM at all. There is a catch: you can't instantiate any routes, you can't mount anything, and you can't invoke `m.render` in any capacity. You can only use `m.route.Link`, `m.route.prefix`, hyperscript stuff, and `mithril/stream`, and you can use `m.request` with `background: true` if you use a global XHR polyfill. (You can't use `m.request` without `background: true` except with a DOM to redraw with.) The goal here is to try to get out of the way for simple testing and to defer the inevitable `TypeError`s for the relevant DOM methods to runtime. The factory requires no arguments, and in terms of globals, you can just figure out based on what errors are thrown what globals to define. Their values don't matter - they just need to be set to *something*, even if it's just `null` or `undefined`, before Mithril executes. Had to make quite a few other changes throughout the docs and tests to update them accordingly. Oh, and that massive router overhaul enabled me to do all this. Also, slip in a few drive-by fixes to the mocks so they're a little easier to work with and can accept more URLs. This was required for a few of the tests. * Update changelog + numbers, add forgotten bundle option * Add PR numbers to changelog [skip ci] * Allow continuing to the next match by returning `false`. * Update numbers again
126 lines
4 KiB
Markdown
126 lines
4 KiB
Markdown
# The auto-redraw system
|
|
|
|
Mithril implements a virtual DOM diffing system for fast rendering, and in addition, it offers various mechanisms to gain granular control over the rendering of an application.
|
|
|
|
When used idiomatically, Mithril employs an auto-redraw system that synchronizes the DOM whenever changes are made in the data layer. The auto-redraw system becomes enabled when you call `m.mount` or `m.route` (but it stays disabled if your app is bootstrapped solely via `m.render` calls).
|
|
|
|
The auto-redraw system simply consists of triggering a re-render function behind the scenes after certain functions complete.
|
|
|
|
### After event handlers
|
|
|
|
Mithril automatically redraws after DOM event handlers that are defined in a Mithril view:
|
|
|
|
```javascript
|
|
var MyComponent = {
|
|
view: function() {
|
|
return m("div", {onclick: doSomething})
|
|
}
|
|
}
|
|
|
|
function doSomething() {
|
|
// a redraw happens synchronously after this function runs
|
|
}
|
|
|
|
m.mount(document.body, MyComponent)
|
|
```
|
|
|
|
You can disable an auto-redraw for specific events by setting `e.redraw` to `false`.
|
|
|
|
```javascript
|
|
var MyComponent = {
|
|
view: function() {
|
|
return m("div", {onclick: doSomething})
|
|
}
|
|
}
|
|
|
|
function doSomething(e) {
|
|
e.redraw = false
|
|
// no longer triggers a redraw when the div is clicked
|
|
}
|
|
|
|
m.mount(document.body, MyComponent)
|
|
```
|
|
|
|
|
|
### After m.request
|
|
|
|
Mithril automatically redraws after [`m.request`](request.md) completes:
|
|
|
|
```javascript
|
|
m.request("/api/v1/users").then(function() {
|
|
// a redraw happens after this function runs
|
|
})
|
|
```
|
|
|
|
You can disable an auto-redraw for a specific request by setting the `background` option to true:
|
|
|
|
```javascript
|
|
m.request("/api/v1/users", {background: true}).then(function() {
|
|
// does not trigger a redraw
|
|
})
|
|
```
|
|
|
|
|
|
### After route changes
|
|
|
|
Mithril automatically redraws after [`m.route.set()`](route.md#mrouteset) calls and after route changes via links using [`m.route.Link`](route.md#mroutelink).
|
|
|
|
```javascript
|
|
var RoutedComponent = {
|
|
view: function() {
|
|
return [
|
|
// a redraw happens asynchronously after the route changes
|
|
m(m.route.Link, {href: "/"}),
|
|
m("div", {
|
|
onclick: function() {
|
|
m.route.set("/")
|
|
}
|
|
}),
|
|
]
|
|
}
|
|
}
|
|
|
|
m.route(document.body, "/", {
|
|
"/": RoutedComponent,
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
### When Mithril does not redraw
|
|
|
|
Mithril does not redraw after `setTimeout`, `setInterval`, `requestAnimationFrame`, raw `Promise` resolutions and 3rd party library event handlers (e.g. Socket.io callbacks). In those cases, you must manually call [`m.redraw()`](redraw.md).
|
|
|
|
Mithril also does not redraw after lifecycle methods. Parts of the UI may be redrawn after an `oninit` handler, but other parts of the UI may already have been redrawn when a given `oninit` handler fires. Handlers like `oncreate` and `onupdate` fire after the UI has been redrawn.
|
|
|
|
If you need to explicitly trigger a redraw within a lifecycle method, you should call `m.redraw()`, which will trigger an asynchronous redraw.
|
|
|
|
```javascript
|
|
function StableComponent() {
|
|
var height = 0
|
|
|
|
return {
|
|
oncreate: function(vnode) {
|
|
height = vnode.dom.offsetHeight
|
|
m.redraw()
|
|
},
|
|
view: function() {
|
|
return m("div", "This component is " + height + "px tall")
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Mithril does not auto-redraw vnode trees that are rendered via `m.render`. This means redraws do not occur after event changes and `m.request` calls for templates that were rendered via `m.render`. Thus, if your architecture requires manual control over when rendering occurs (as can sometimes be the case when using libraries like Redux), you should use `m.render` instead of `m.mount`.
|
|
|
|
Remember that `m.render` expects a vnode tree, and `m.mount` expects a component:
|
|
|
|
```javascript
|
|
// wrap the component in a m() call for m.render
|
|
m.render(document.body, m(MyComponent))
|
|
|
|
// don't wrap the component for m.mount
|
|
m.mount(document.body, MyComponent)
|
|
```
|
|
|
|
Mithril may also avoid auto-redrawing if the frequency of requested redraws is higher than one animation frame (typically around 16ms). This means, for example, that when using fast-firing events like `onresize` or `onscroll`, Mithril will automatically throttle the number of redraws to avoid lag.
|