mroe docs
This commit is contained in:
parent
5815a590a8
commit
e35fc79f09
3 changed files with 264 additions and 3 deletions
79
docs/keys.md
79
docs/keys.md
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
- [What are keys](#what-are-keys)
|
||||
- [How to use](#how-to-use)
|
||||
- [Debugging key related issues](#debugging-key-related-issues)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -11,7 +12,7 @@ Keys are a mechanism that allows re-ordering DOM elements within a NodeList, and
|
|||
|
||||
In other words, a `key` is a way of saying "this DOM element is for the data object with this id".
|
||||
|
||||
Typically, `key` should be the unique identifier field of the objects in the data array.
|
||||
Typically, a `key` property should be the unique identifier field of the objects in the data array.
|
||||
|
||||
```javascript
|
||||
var users = [
|
||||
|
|
@ -64,7 +65,7 @@ Worse still, if there were stateful jQuery plugins attached to these buttons, th
|
|||
|
||||
Even though in this particular example, we humans intuitively guess that the first item in the list was the one being removed, it's actually impossible for a computer to automatically solve this problem for all possible inputs.
|
||||
|
||||
Therefore, in the cases when a list of vnodes is derived from a *mutable* array of data, you should add a `key` property to each virtual node that maps to a uniquely identifiable field in the source data. This will allow Mithril to intelligently re-order the DOM to maintain each DOM element correctly mapped to its respective item in the data source.
|
||||
Therefore, in the cases when a list of vnodes is derived from a dynamic array of data, you should add a `key` property to each virtual node that maps to a uniquely identifiable field in the source data. This will allow Mithril to intelligently re-order the DOM to maintain each DOM element correctly mapped to its respective item in the data source.
|
||||
|
||||
```javascript
|
||||
function correctUserList(users) {
|
||||
|
|
@ -74,7 +75,79 @@ function correctUserList(users) {
|
|||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Debugging key related issues
|
||||
|
||||
Keys can cause confusing issues if they are misunderstood. A typical symptom of key related issues is that application state appears to become corrupted after a few user interactions (usually involving a deletion).
|
||||
|
||||
#### Avoid wrapper elements around keyed elements
|
||||
|
||||
Keys must be placed on the virtual node that is an immediate child of the array. This means that if you wrap the `button` in an `div` in the example above, the key must be moved to the `div`.
|
||||
|
||||
```javascript
|
||||
// AVOID
|
||||
users.map(function(u) {
|
||||
return m("div", [ //key should be in `div`
|
||||
m("button", {key: u.id}, u.name)
|
||||
])
|
||||
})
|
||||
```
|
||||
|
||||
#### Avoid hiding keys in component root elements
|
||||
|
||||
If you refactor the code and put the button inside a component, the key must be moved out of the component and placed back where the component took the place of the button.
|
||||
|
||||
```javascript
|
||||
// AVOID
|
||||
var Button = {
|
||||
view: function(vnode) {
|
||||
return m("button", {key: vnode.attrs.id}, u.name)
|
||||
}
|
||||
}
|
||||
users.map(function(u) {
|
||||
return m("div", [
|
||||
m(Button, {id: u.id}, u.name) //key should be here, not in component
|
||||
])
|
||||
})
|
||||
```
|
||||
|
||||
#### Avoid wrapping keyed elements in arrays
|
||||
|
||||
Arrays are [vnodes](vnodes.md), and therefore keyable. You should not wrap arrays around keyed elements
|
||||
|
||||
```javascript
|
||||
// AVOID
|
||||
users.map(function(u) {
|
||||
return [ //fragment is a vnode, and therefore keyable
|
||||
m("button", {key: u.id}, u.name)
|
||||
]
|
||||
})
|
||||
|
||||
// PREFER
|
||||
users.map(function(u) {
|
||||
return m("button", {key: u.id}, u.name)
|
||||
})
|
||||
|
||||
// PREFER
|
||||
users.map(function(u) {
|
||||
return {tag: "[", key: u.id, children: [
|
||||
m("button", u.name)
|
||||
]}
|
||||
})
|
||||
```
|
||||
|
||||
#### Avoid variable types
|
||||
|
||||
Keys must be strings if present or they will be cast to strings if they are not. Therefore, `"1"` (string) and `1` (number) are considered the same key.
|
||||
|
||||
In addition, keys must be placed on the virtual node that is an immediate child of the array. This means that if you wrap the `button` in an `div` in the example above, the key must be moved to the `div`. Likewise, if you refactor the code and put the button inside a component, the key must be moved out of the component and placed back where the component took place of the button.
|
||||
You should use either strings or numbers as keys in one array, but not mix both.
|
||||
|
||||
```javascript
|
||||
// AVOID
|
||||
var things = [
|
||||
{id: "1", name: "Book"},
|
||||
{id: 1, name: "Cup"},
|
||||
]
|
||||
```
|
||||
|
||||
|
|
|
|||
36
docs/render.md
Normal file
36
docs/render.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# render(element, vnodes)
|
||||
|
||||
- [Signature](#signature)
|
||||
- [How it works](#how-it-works)
|
||||
- [Standalone usage](#standalone-usage)
|
||||
|
||||
---
|
||||
|
||||
### Signature
|
||||
|
||||
`render(element, vnodes)`
|
||||
|
||||
Argument | Type | Required | Description
|
||||
----------- | -------------------- | -------- | ---
|
||||
`element` | `Element` | Yes | A DOM element that will be the parent node to the subtree
|
||||
`vnodes` | `Array<Vnode>|Vnode` | Yes | The [vnodes](vnodes.md) to be rendered
|
||||
**returns** | | | Returns nothing
|
||||
|
||||
[How to read signatures](signatures.md)
|
||||
|
||||
---
|
||||
|
||||
### How it works
|
||||
|
||||
The `m.render(element, vnodes)` method takes a virtual DOM tree (typically generated via the [`m()` hyperscript function](hyperscript.md), generates a DOM tree and appends it to `element`.
|
||||
|
||||
This method is internally called by [`m.mount()`](mount.md) and [`m.route()`](route.md).
|
||||
|
||||
---
|
||||
|
||||
### Standalone usage
|
||||
|
||||
The `m.render` module is similar in scope to view libraries like Knockout, React and Vue. It is less than 500 lines of code (3kb min+gzip) and 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 and can be used as a standalone library.
|
||||
|
||||
Despite being incredibly small, the render module is fully functional and self-suficient. 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](components.md) and [lifecycle methods](lifecycle-methods.md).
|
||||
|
||||
152
docs/trust.md
Normal file
152
docs/trust.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# trust(html)
|
||||
|
||||
- [Signature](#signature)
|
||||
- [How it works](#how-it-works)
|
||||
- [Security considerations](#security-considerations)
|
||||
- [Scripts that do not run](#scripts-that-do-not-run)
|
||||
- [Avoid trusting HTML](#avoid-trusting-html)
|
||||
|
||||
---
|
||||
|
||||
### Signature
|
||||
|
||||
`render(element, vnodes)`
|
||||
|
||||
Argument | Type | Required | Description
|
||||
----------- | -------------------- | -------- | ---
|
||||
`html` | `String` | Yes | A string containing HTML text
|
||||
**returns** | `Vnode` | | A trusted HTML [vnode](vnodes.md) that represents the input string
|
||||
|
||||
[How to read signatures](signatures.md)
|
||||
|
||||
---
|
||||
|
||||
### How it works
|
||||
|
||||
By default, Mithril escapes all values in order to prevent a class of security problems called [XSS injections](https://en.wikipedia.org/wiki/Cross-site_scripting).
|
||||
|
||||
However, sometimes it is desirable to render rich text and formatting markup. To fill that need, `m.trust` creates trusted HTML [vnodes](vnodes.md) which are rendered as HTML.
|
||||
|
||||
```javascript
|
||||
var view = m("div", [
|
||||
m.trust("<h1>Here's some <em>HTML</em></h1>")
|
||||
])
|
||||
|
||||
m.render(document.body, view)
|
||||
|
||||
// equivalent HTML
|
||||
// <div><h1>Here's some <em>HTML</em></h1></div>
|
||||
```
|
||||
|
||||
Trusted HTML vnodes are objects, not strings; therefore they cannot be concatenated with regular strings.
|
||||
|
||||
---
|
||||
|
||||
### Security considerations
|
||||
|
||||
You **must sanitize the input** of `m.trust` to ensure there's no user-generated Javascript in the HTML string. If you don't sanitize an HTML string and mark it as a trusted string, any asynchronous javascript call points within the HTML string will be triggered and run with the authorization level of the user viewing the page.
|
||||
|
||||
There are many ways in which an HTML string may contain executable code. The most common ways to inject security attacks are to add an `onload` or `onerror` attributes in `<img>` or `<iframe>` tags, and to use unbalanced quotes such as `" onerror="alert(1)` to inject executable contexts in unsanitized string interpolations.
|
||||
|
||||
```javascript
|
||||
// Sample vulnerable HTML string
|
||||
var description = "<img alt='" + data.title + "'> <span>" + data.description + "</span>"
|
||||
|
||||
// An attack using javascript-related attributes
|
||||
data.description = "<img onload='alert(1)'>"
|
||||
|
||||
// An attack using unbalanced tags
|
||||
data.description = "</span><img onload='alert(1)'><span"
|
||||
|
||||
// An attack using unbalanced quotes
|
||||
data.title = "' onerror='alert(1)"
|
||||
|
||||
// An attack using a different attribute
|
||||
data.title = "' onmouseover='alert(1)"
|
||||
```
|
||||
|
||||
There are several non-obvious ways of declaring executable code, so it is highly recommended that you use a [whitelist](https://en.wikipedia.org/wiki/Whitelist) of permitted HTML tags, attributes and attribute values, as opposed to a [blacklist](https://en.wikipedia.org/wiki/Blacklisting) to sanitize the user input. It's also highly recommended that you use a proper HTML parser, instead of regular expressions for sanitization, because regular expressions are extremely difficult to test for all edge cases.
|
||||
|
||||
---
|
||||
|
||||
### Scripts that do not run
|
||||
|
||||
Even though there are many obscure ways to make an HTML string run Javascript, `<script>` tags are one thing that does not run when it appears in an HTML string.
|
||||
|
||||
For historical reasons, browsers ignore `<script>` tags that are inserted into the DOM via innerHTML. They do this because once the element is ready (and thus, has an accessible innerHTML property), the rendering engines cannot backtrack to the parsing-stage if the script calls something like document.write("</body>").
|
||||
|
||||
This browser behavior may seem surprising to a developer coming from jQuery, because jQuery implements code specifically to find script tags and run them in this scenario. Mithril follows the browser behavior. If jQuery behavior is desired, you should consider moving the code out of the HTML string and into an `oncreate` [lifecycle method](lifecycle-methods.md), and either use jQuery or re-implement its script parsing routine.
|
||||
|
||||
---
|
||||
|
||||
### Avoid trusting HTML
|
||||
|
||||
As a general rule of thumb, you should avoid using `m.trust` unless you are explicitly rendering rich text and there's no other way to get the results that you want.
|
||||
|
||||
#### Avoid blind copying and pasting
|
||||
|
||||
One common way to misuse `m.trust` is when working with third party services whose tutorials include HTML code to be copied and pasted. In most cases, HTML should be written using vnodes (typically via the [`m()`](hyperscript.md) utility)
|
||||
|
||||
Here's the example snippet for the [Facebook Like button](https://developers.facebook.com/docs/plugins/like-button):
|
||||
|
||||
```markup
|
||||
<!-- Load Facebook SDK for JavaScript -->
|
||||
<div id="fb-root"></div>
|
||||
<script>(function(d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) return;
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));</script>
|
||||
|
||||
<!-- Your like button code -->
|
||||
<div class="fb-like"
|
||||
data-href="http://www.your-domain.com/your-page.html"
|
||||
data-layout="standard"
|
||||
data-action="like"
|
||||
data-show-faces="true">
|
||||
</div>
|
||||
```
|
||||
|
||||
And here's how to refactor into a Mithril component in a way that avoids `m.trust`:
|
||||
|
||||
```javascript
|
||||
var FacebookLikeButton = {
|
||||
oncreate: function() {
|
||||
(function(d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) return;
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));
|
||||
}
|
||||
view: function() {
|
||||
return [
|
||||
m("#fb-root"),
|
||||
m("#fb-like[data-href=http://www.your-domain.com/your-page.html][data-layout=standard][data-action=like][data-show-faces=true]")
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Mithril component above simply pulls out the script tag's exact content into the `oncreate` hook and declares the remaining HTML tags using Mithril's `m()` syntax.
|
||||
|
||||
#### Avoid HTML entities
|
||||
|
||||
A common way to misuse `m.trust` is to use it for HTML entities. A better approach is to use the corresponding unicode characters:
|
||||
|
||||
```javascript
|
||||
// AVOID
|
||||
m("h1", "Coca-Cola", m.trust("™"))
|
||||
|
||||
// PREFER
|
||||
m("h1", "Coca-Cola™")
|
||||
```
|
||||
|
||||
Unicode characters for accented characters can be typed using a keyboard layout for an applicable language, and one may also choose to memorize keyboard shortcuts to produce commonly used symbols (e.g. `Alt+0153` in Windows, or `Option+2` on Mac for the ™ symbol). Another simple method to produce them is to simply copy and paste the desired character from a [unicode character table](https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references). Yet another related method is to type an escaped unicode codepoint (e.g. `"\u2122"` for the ™ symbol).
|
||||
|
||||
All characters that are representable as HTML entities have unicode counterparts, including non-visible characters such as ` ` and `­`.
|
||||
|
||||
To avoid encoding issues, you should set the file encoding to UTF-8 on the Javascript file, as well as add the `<meta charset="utf-8">` meta tag in the host HTML file.
|
||||
Loading…
Add table
Add a link
Reference in a new issue