Merge pull request #1433 from barneycarroll/document-equality-diff-skip
Document equality diff skip - fixes #1429
This commit is contained in:
commit
1ccef85e92
2 changed files with 71 additions and 1 deletions
|
|
@ -261,7 +261,9 @@ m(BetterHeader, {
|
|||
})
|
||||
```
|
||||
|
||||
#### Avoid component factories
|
||||
#### Define components statically, call them dynamically
|
||||
|
||||
##### Avoid creating component definitions inside views
|
||||
|
||||
If you create a component from within a `view` method (either directly inline or by calling a function that does so), each redraw will have a different clone of the component. When diffing component vnodes, if the component referenced by the new vnode is not strictly equal to the one referenced by the old component, the two are assumed to be different components even if they ultimately run equivalent code. This means components created dynamically via a factory will always be re-created from scratch.
|
||||
|
||||
|
|
@ -291,3 +293,65 @@ m.render(document.body, m(Component, {greeting: "hello"}))
|
|||
// calling a second time does not modify DOM
|
||||
m.render(document.body, m(Component, {greeting: "hello"}))
|
||||
```
|
||||
|
||||
##### Avoid creating component instances outside views
|
||||
|
||||
Conversely, for similar reasons, if a component instance is created outside of a view, future redraws will perform an equality check on the node and skip it. Therefore component instances should always be created inside views:
|
||||
|
||||
```javascript
|
||||
// AVOID
|
||||
var Counter = {
|
||||
count: 0,
|
||||
view: function(vnode) {
|
||||
return m("div",
|
||||
m("p", "Count: " + vnode.state.count ),
|
||||
|
||||
m("button", {
|
||||
onclick: function() {
|
||||
vnode.state.count++
|
||||
}
|
||||
}, "Increase count")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var counter = m(Counter)
|
||||
|
||||
m.mount(document.body, {
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m("h1", "My app"),
|
||||
counter
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
In the example above, clicking the counter component button will increase its state count, but its view will not be triggered because the vnode representing the component shares the same reference, and therefore the render process doesn't diff them. You should always call components in the view to ensure a new vnode is created:
|
||||
|
||||
```javascript
|
||||
// PREFER
|
||||
var Counter = {
|
||||
count: 0,
|
||||
view: function(vnode) {
|
||||
return m("div",
|
||||
m("p", "Count: " + vnode.state.count ),
|
||||
|
||||
m("button", {
|
||||
onclick: function() {
|
||||
vnode.state.count++
|
||||
}
|
||||
}, "Increase count")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
m.mount(document.body, {
|
||||
view: function(vnode) {
|
||||
return [
|
||||
m("h1", "My app"),
|
||||
m(Counter)
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
|
|
|||
|
|
@ -408,3 +408,9 @@ var BetterListComponent = {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Avoid creating vnodes outside views
|
||||
|
||||
When a redraw encounters a vnode which is strictly equal to the one in the previous render, it will be skipped and its contents will not be updated. While this may seem like an opportunity for performance optimisation, it should be avoided because it prevents dynamic changes in that node's tree - this leads to side-effects such as downstream lifecycle methods failing to trigger on redraw. In this sense, Mithril vnodes are immutable: new vnodes are compared to old ones; mutations to vnodes are not persisted.
|
||||
|
||||
The component documentation contains [more detail and an example of this anti-pattern](components.md#avoid-creating-component-instances-outside-views).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue