improve docs on redrawing and keys
This commit is contained in:
parent
eceea4addf
commit
57e9e1557e
4 changed files with 52 additions and 5 deletions
|
|
@ -1,8 +1,24 @@
|
|||
## Integrating with The Auto-Redrawing System
|
||||
## The Auto-Redrawing System
|
||||
|
||||
Mithril is designed around the principle that data always flows from the model to the view. This makes it easy to reason about the state of the UI and to test it. In order to implement this principle, the rendering engine must run a redraw algorithm globally to ensure no parts of the UI are out of sync with the data. While at first glance, it may seem expensive to run a global redraw every time data changes, Mithril makes it possible to do this efficiently thanks to its fast diffing algorithm, which only updates the DOM where it needs to be updated. Because the DOM is by far the largest bottleneck in rendering engines, Mithril's approach of running a diff against a virtual representation of the DOM and only batching changes to the real DOM as needed is surprisingly performant.
|
||||
|
||||
In addition, Mithril attempts to intelligently redraw only when it is appropriate in an application lifecycle. Most frameworks redraw aggressively and err on the side of redrawing too many times because, as it turns out, determining the best time to do a redraw is quite complicated if we want to be as efficient as possible.
|
||||
|
||||
Mithril employs a variety of mechanisms to decide the best time and the best strategy to redraw. By default, Mithril is configured to auto-redraw from scratch after module controllers are initialized, and it is configured to diff after event handlers are triggered. In addition, it's possible for non-Mithril asynchronous callbacks to trigger auto-redrawing by calling `m.startComputation` and `m.endComputation` in appropriate places (see below). Any code that is between a `m.startComputation` and its respective `m.endComputation` call is said to live in the *context* of its respective pair of function calls.
|
||||
|
||||
It's possible to defer a redraw by calling `m.request` or by manually nesting [`m.startComputation` and `m.endComputation`](mithril.computation.md) contexts. The way the redrawing engine defers redrawing is by keeping an internal counter that is incremented by `m.startComputation` and decremented by `m.endComputation`. Once that counter reaches zero, Mithril redraws. By strategically placing calls to this pair of functions, it is possible to stack asynchronous data services in any number of ways within a context without the need to pass state variables around the entire application. The end result is that you can call `m.request` and other integrated data services seamlessly, and Mithril will wait for all of the asynchronous operations to complete before attempting to redraw.
|
||||
|
||||
In addition to being aware of data availability when deciding to redraw, Mithril is also aware of browser availability: if several redraws are triggered in a short amount of time, Mithril batches them so that at most only one redraw happens within a single animation frame (around 16ms). Since computer screens are not able to display changes faster than a frame, this optimization saves CPU cycles and helps UIs stay responsive even in the face of spammy data changes.
|
||||
|
||||
Mithril also provides several hooks to control its redrawing behavior with a deep level of granularity: [`m.startComputation` and `m.endComputation`](mithril.computation.md) create redrawable contexts. [`m.redraw`](mithril.redraw.md) forces a redraw to happen in the next available frame (or optionally, it can redraw immediately for synchronous processing). [`m.redraw.strategy`](mithril.redraw.md#strategy) can change the way Mithril runs the next scheduled redraw. Finally, the low-level [`m.render`](mithril.render.md) can also be used if a developer chooses to opt out of rest of the framework altogether.
|
||||
|
||||
---
|
||||
|
||||
### Integrating with The Auto-Redrawing System
|
||||
|
||||
If you need to do custom asynchronous calls without using Mithril's API, and find that your views are not redrawing, or that you're being forced to call [`m.redraw`](mithril.redraw.md) manually, you should consider using `m.startComputation` / `m.endComputation` so that Mithril can intelligently auto-redraw once your custom code finishes running.
|
||||
|
||||
In order to integrate asynchronous code to Mithril's autoredrawing system, you should call `m.startComputation` BEFORE making an asynchronous call, and `m.endComputation` after the asynchronous callback completes.
|
||||
In order to integrate asynchronous code to Mithril's autoredrawing system, you should call `m.startComputation` BEFORE making an asynchronous call, and `m.endComputation` at the end of the asynchronous callback.
|
||||
|
||||
```javascript
|
||||
//this service waits 1 second, logs "hello" and then notifies the view that
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
[Destructors](#destructors)
|
||||
[SVG](#svg)
|
||||
[Dealing with focus](#dealing-with-focus)
|
||||
[Dealing with sorting and deleting in lists](#dealing-with-sorting-and-deleting-in-lists)
|
||||
[Signature](#signature)
|
||||
|
||||
---
|
||||
|
|
@ -321,6 +322,24 @@ m("li", {class: selected ? "active" : ""})
|
|||
|
||||
---
|
||||
|
||||
### Dealing with sorting and deleting in lists
|
||||
|
||||
As with input focus, we can maintain referential integrity between data in a list and the respective DOM representation by using keys.
|
||||
|
||||
```javascript
|
||||
m("ul", [
|
||||
ctrl.items.map(function(item) {
|
||||
return m("li", {key: item.id}, [
|
||||
m("input")
|
||||
]);
|
||||
})
|
||||
]);
|
||||
```
|
||||
|
||||
You should always use keys if you need to sort lists, remove items from them or splice them in any way.
|
||||
|
||||
---
|
||||
|
||||
### Signature
|
||||
|
||||
[How to read signatures](how-to-read-signatures.md)
|
||||
|
|
|
|||
|
|
@ -29,12 +29,18 @@ If you are developing an asynchronous model-level service and finding that Mithr
|
|||
|
||||
### Changing redraw strategy
|
||||
|
||||
If you need to change how Mithril performs redraws, you can change the value of the `m.redraw.strategy` getter-setter to either `"all"`, `"diff"` or `"none"`. By default, this value is set to `"all"` when running controller constructors, and it's set to `"diff"` for all subsequent redraws.
|
||||
|
||||
The strategy flag is meant to only be changed in a context where Mithril auto-redraws. This means `m.redraw.strategy` can be called from controller constructors and from template event handlers. Note that changing this flag only affects the next scheduled redraw.
|
||||
If you need to change how Mithril performs a redraw, you can change the value of the `m.redraw.strategy` getter-setter to either `"all"`, `"diff"` or `"none"`. The new strategy will apply to the next scheduled redraw, if any. By default, Mithril sets this value to `"all"` before running controller constructors, and it sets it to `"diff"` before event handlers are triggered.
|
||||
|
||||
After the redraw, Mithril resets the value of the flag to either "all" or "diff", depending on whether the redraw was due to a route change or not.
|
||||
|
||||
Changing the flag outside of a redrawable context does nothing since the flag gets reset when entering one of the documented redrawable contexts above.
|
||||
|
||||
When the flag is set to "all", Mithril throws away the current view and redraws from scratch. This is the default for going from one route to another.
|
||||
|
||||
When the flag is set to "diff", Mithril performs a diff between the old view and the new view and applies patches to the DOM only where needed.
|
||||
|
||||
When the flag is set to "none", Mithril skips the next redraw. You don't need to change this flag to something else again later, since Mithril does that for you.
|
||||
|
||||
```javascript
|
||||
var module1 = {}
|
||||
module1.controller = function() {
|
||||
|
|
|
|||
|
|
@ -104,3 +104,9 @@ If you call this method more often than that, Mithril may ignore calls or defer
|
|||
If calls are more expensive than a repaint window, the browser may drop frames, resulting in choppy animations. It's your responsibility to make sure single iterations of animation rendering code don't take longer than 16ms (for a frequency of 60 frames-per-second).
|
||||
|
||||
In addition, note that template performance, both in Mithril templates as well as in general, is dependent on markup complexity. You are responsible for ensuring that templates aren't too big to render efficiently.
|
||||
|
||||
---
|
||||
|
||||
## Usage of keys
|
||||
|
||||
If you need to sort lists, or delete items from them, or splice them in any way, you should [use the `key` attribute](mithril.md#dealing-with-sorting-and-deleting-in-lists) to maintain referential integrity between the data and the DOM.
|
||||
Loading…
Add table
Add a link
Reference in a new issue