improve docs about redrawing
This commit is contained in:
parent
15c0686b27
commit
c9be4b30ce
4 changed files with 73 additions and 44 deletions
|
|
@ -10,13 +10,13 @@ It's possible to defer a redraw by calling `m.request` or by manually nesting [`
|
|||
|
||||
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.
|
||||
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). The [config's retain flag](mithril.md#persisting-dom-elements-across-route-changes) can be used to change how specific elements are redrawn when routes change. [`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.
|
||||
If you need to do custom asynchronous calls without using Mithril's API, and find that your views are not redrawing automatically, 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` at the end of the asynchronous callback.
|
||||
|
||||
|
|
@ -48,8 +48,17 @@ window.onfocus = function() {
|
|||
|
||||
For each `m.startComputation` call a library makes, it MUST also make one and ONLY one corresponding `m.endComputation` call.
|
||||
|
||||
You should not use these methods if your code is intended to run repeatedly (e.g. by using `setInterval`). If you want to repeatedly redraw the view without necessarily waiting for user input, you should manually call [`m.redraw`](mithril.redraw.md) within the repeatable context.
|
||||
If you want to a recurring callback (such as `setInterval` or a web socket event handler) to trigger redraws, you should call `m.startComputation` at the beginning of the function, not outside of it.
|
||||
|
||||
```
|
||||
setInterval(function() {
|
||||
m.startComputation(); //call before everything else in the event handler
|
||||
|
||||
doStuff();
|
||||
|
||||
m.endComputation(); //call after everything else in the event handler
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -487,7 +487,9 @@ Since controllers can call model methods, it's possible for nested components to
|
|||
|
||||
When a component has asynchronous payloads and they are queued by the [auto-redrawing system](auto-redrawing.md), its view is NOT rendered until all asynchronous operations complete. When the component's asynchronous operations complete, another redraw is triggered and the entire template tree is evaluated again. This means that the virtual dom tree may take two or more redraws (depending on how many nested asynchronous components there are) to be fully rendered.
|
||||
|
||||
There are [different ways to organize components](#application-architecture-with-components) that can side-step the need for multiple redraws (although you could still force multiple redraws to happen by using the [`background`](mithril.request.md#rendering-before-web-service-requests-finish) and `initialValue` options in `m.request`.)
|
||||
There are [different ways to organize components](components.md#application-architecture-with-components) that can side-step the need for multiple redraws. Regardless, you could also force multiple redraws to happen by using the [`background`](mithril.request.md#rendering-before-web-service-requests-finish) and `initialValue` options in `m.request`, or by manually calling [`m.redraw()`](mithril.redraw.md).
|
||||
|
||||
If a component A contains another component B that calls asynchronous services, then when component A is rendered, a `<placeholder>` tag is rendered in place of component B until B's asynchronous services resolve. Once they do, the placeholder is replaced with component B's view.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
---
|
||||
|
||||
- [How auto-redrawing works](#how-auto-redrawing-works)
|
||||
- [Difference between computation methods and m.redraw](#difference-between-computation-methods-and-m-redraw)
|
||||
- [Integrating multiple execution threads](#integrating-multiple-execution-threads)
|
||||
- [Integrating to legacy code](#integrating-to-legacy-code)
|
||||
- [Signature](#signature)
|
||||
|
|
@ -11,7 +12,7 @@
|
|||
|
||||
Typically, `m.startComputation` / `m.endComputation` don't need to be called from application space. These methods are only intended to be used by people who are writing libraries that do things asynchronously, or when calling vanilla javascript asynchronous functions from template [`config`](mithril.md#accessing-the-real-dom) functions.
|
||||
|
||||
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.
|
||||
If you need to do custom asynchronous calls without using Mithril's API, and find that your views are not redrawing, 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 an 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.
|
||||
|
||||
|
|
@ -145,6 +146,18 @@ var view = function() {
|
|||
|
||||
---
|
||||
|
||||
### Difference between computation methods and m.redraw
|
||||
|
||||
The `m.startComputation` / `m.endComputation` pair is designed to be "stacked", i.e. multiple asynchronous services can each call this pair of functions to indicate that they want the redrawing algorithm to wait for them to finish before a redraw occurs. In contrast, `m.redraw` is "aggressive": it redraws as many times as it is called (with the caveat that redraws are batched if they occur less than one animation frame apart in time). In practice, this means that calling `m.redraw` may cause a redraw to happen before some AJAX calls have finished, which in turn, may cause null reference exceptions in templates that try to use the data from these requests without first checking that the data exists.
|
||||
|
||||
Therefore, using the computation methods is recommended in order to reduce the amount of intermediate redraws that would otherwise occur as multiple asynchronous services are resolved.
|
||||
|
||||
When computation methods are used dilligently and religiously, templates are never redrawn with incomplete data. However, it's important to always write conditional tests in templates to account for the possibility of nullables, because redraws may come to occur more aggressively than data is available (perhaps because a newly introduced 3rd party library calls `m.redraw`, or because you might want a more aggressive redraw policy to implement a specific feature down the road).
|
||||
|
||||
Defending against nullables can typically be achieved via the `initialValue` option in [`m.request`](mithril.request.md) and basic null checks (e.g. `data ? m("div", data) : null`).
|
||||
|
||||
---
|
||||
|
||||
### Integrating multiple execution threads
|
||||
|
||||
When [integrating with third party libraries](integration.md), you might find that you need to call asynchronous methods from outside of Mithril's API.
|
||||
|
|
|
|||
|
|
@ -3,28 +3,33 @@
|
|||
---
|
||||
|
||||
- [Changing redraw strategy](#changing-redraw-strategy)
|
||||
- [Difference between computation methods and m.redraw](#difference-between-computation-methods-and-m-redraw)
|
||||
- [Preventing redraws on events](#preventing-redraws-on-events)
|
||||
- [Forcing redraw](#forcing-redraw)
|
||||
- [Signature](#signature)
|
||||
|
||||
---
|
||||
|
||||
Redraws the view for the currently active component. Use [`m.mount()`](mithril.mount.md) to activate a component.
|
||||
Redraws the view for the currently active component. Use [`m.mount()`](mithril.mount.md) or [`m.route()`](mithril.route.md) to activate a component.
|
||||
|
||||
This method is called internally by Mithril's auto-redrawing system. Usually you don't need to call it manually unless you are doing recurring asynchronous operations (i.e. using `setInterval`) or if you want to decouple slow running background requests from the rendering context (see the `background` option in [`m.request`](mithril.request.md).
|
||||
|
||||
By default, if you're using either [`m.route`](mithril.route.md) or [`m.mount`](mithril.mount.md), `m.redraw()` is called automatically by Mithril's auto-redrawing system once the controller finishes executing.
|
||||
|
||||
`m.redraw` is also called automatically on event handlers defined in virtual elements.
|
||||
|
||||
Note that calling this method will not do anything if a component was not activated via either [`m.mount()`](mithril.mount.md) or [`m.route()`](mithril.route.md). This means that `m.redraw` doesn't do anything when instantiating controllers and rendering views via `m.render` manually.
|
||||
|
||||
If there are pending [`m.request`](mithril.request.md) calls in either a controller constructor or event handler, the auto-redrawing system waits for all the AJAX requests to complete before calling `m.redraw`.
|
||||
|
||||
This method may also be called manually from within a controller if more granular updates to the view are needed, however doing so is generally not recommended, as it may degrade performance. Model classes should never call this method.
|
||||
Calling `m.redraw` triggers a redraw regardless of whether AJAX requests (and other asynchronous services) are completed. Therefore, you should ensure that templates have null checks in place to account for the possibility of variables being unitialized when the forced redraw occurs.
|
||||
|
||||
If you are developing an asynchronous model-level service and finding that Mithril is not redrawing the view after your code runs, you should use [`m.startComputation` and `m.endComputation`](mithril.computation.md) to integrate with Mithril's auto-redrawing system instead.
|
||||
|
||||
Assuming your templates have appropriate null checks in place, `m.redraw` is useful for transient state such as loading indicators and to commit state to the DOM for the purposes of reading back computed values (for example, `offsetWidth` or `scrollHeight`)
|
||||
|
||||
---
|
||||
|
||||
### Difference between computation methods and m.redraw
|
||||
|
||||
The [`m.startComputation` / `m.endComputation` pair](mithril.computation.md) is designed to be "stacked", i.e. multiple asynchronous services can each call this pair of functions to indicate that they want the redrawing algorithm to wait for them to finish before a redraw occurs. In contrast, `m.redraw` is "aggressive": it redraws as many times as it is called (with the caveat that redraws are batched if they occur less than one animation frame apart in time). In practice, this means that calling `m.redraw` may cause a redraw to happen before some AJAX calls have finished, which in turn, may cause null reference exceptions in templates that try to use the data from these requests without first checking that the data exists.
|
||||
|
||||
Therefore, using the computation methods is recommended in order to reduce the amount of intermediate redraws that would otherwise occur as multiple asynchronous services are resolved.
|
||||
|
||||
When computation methods are used dilligently and religiously, templates are never redrawn with incomplete data. However, it's important to always write conditional tests in templates to account for the possibility of nullables, because redraws may come to occur more aggressively than data is available (perhaps because a newly introduced 3rd party library calls `m.redraw`, or because you might want a more aggressive redraw policy to implement a specific feature down the road).
|
||||
|
||||
Defending against nullables can typically be achieved via the `initialValue` option in [`m.request`](mithril.request.md) and basic null checks (e.g. `data ? m("div", data) : null`).
|
||||
|
||||
---
|
||||
|
||||
### Changing redraw strategy
|
||||
|
|
@ -59,7 +64,32 @@ var Component1 = {
|
|||
|
||||
Common reasons why one might need to change redraw strategy are:
|
||||
|
||||
- in order to avoid the full-page recreation when changing routes, for the sake of performance of global 3rd party components
|
||||
- in order to prevent redraw when dealing with `keypress` events where the event's keyCode is not of interest
|
||||
|
||||
```javascript
|
||||
//model
|
||||
var saved = false
|
||||
function save(e) {
|
||||
if (e.keyCode == 13) {
|
||||
//this causes a redraw, since event handlers active auto-redrawing by default
|
||||
saved = true
|
||||
}
|
||||
else {
|
||||
//we don't care about other keys, so don't redraw
|
||||
m.redraw.strategy("none")
|
||||
}
|
||||
}
|
||||
|
||||
//view
|
||||
var view = function() {
|
||||
return m("div", [
|
||||
m("button[type=button]", {onkeypress: save}, "Save"),
|
||||
saved ? "Saved" : ""
|
||||
])
|
||||
}
|
||||
```
|
||||
|
||||
- in order to avoid the full-page recreation when changing routes, for the sake of performance of global 3rd party components.
|
||||
|
||||
```javascript
|
||||
//diff when routing, instead of redrawing from scratch
|
||||
|
|
@ -94,34 +124,9 @@ Common reasons why one might need to change redraw strategy are:
|
|||
})
|
||||
```
|
||||
|
||||
- in order to prevent redraw when dealing with `keypress` events where the event's keyCode is not of interest
|
||||
|
||||
```javascript
|
||||
//model
|
||||
var saved = false
|
||||
function save(e) {
|
||||
if (e.keyCode == 13) {
|
||||
//this causes a redraw, since event handlers active auto-redrawing by default
|
||||
saved = true
|
||||
}
|
||||
else {
|
||||
//we don't care about other keys, so don't redraw
|
||||
m.redraw.strategy("none")
|
||||
}
|
||||
}
|
||||
|
||||
//view
|
||||
var view = function() {
|
||||
return m("div", [
|
||||
m("button[type=button]", {onkeypress: save}, "Save"),
|
||||
saved ? "Saved" : ""
|
||||
])
|
||||
}
|
||||
```
|
||||
|
||||
Note that the redraw strategy is a global setting that affects the entire template trees of all components on the page. In order to prevent redraws in *some parts* of an application, but not others, see [subtree directives](mithril.render.md#subtree-directives)
|
||||
|
||||
You can also configure individual elements to always be diffed, instead of recreated from scratch (even across route changes), by using the [`ctx.retain` flag](mithril.md#persising-dom-elements-across-route-changes)
|
||||
You can also configure individual elements to always be diffed, instead of recreated from scratch (even across route changes), by using the [`ctx.retain` flag](mithril.md#persising-dom-elements-across-route-changes). If you need to persist DOM state across route changes, it's recommended that you use the `ctx.retain` flag instead of `m.redraw.strategy("diff")`.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue