diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index 3d78ccb50..df2c9ba35 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -1,6 +1,6 @@ --- name: "\U0001F41B Bug" -about: Report a bug in Mithril +about: Report a bug in Mithril.js title: '' labels: 'Type: Bug' assignees: isiahmeadows @@ -9,12 +9,12 @@ assignees: isiahmeadows -**Mithril version:** +**Mithril.js version:** -**Mithril version:** +**Mithril.js version:** diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index a2cb60e1b..45b04f73f 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,6 +1,6 @@ --- name: "\U0001F64B‍♀️ Question" -about: Ask a question about Mithril +about: Ask a question about Mithril.js title: '' labels: 'Type: Question' assignees: '' @@ -9,10 +9,10 @@ assignees: '' -**Mithril version:** +**Mithril.js version:** 9.79 KB gzipped), fast and provides routing and XHR utilities out of the box. +A modern client-side JavaScript framework for building Single Page Applications. It's small (10.04 KB gzipped), fast and provides routing and XHR utilities out of the box. -Mithril is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍. +Mithril.js is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍. -Mithril supports IE11, Firefox ESR, and the last two versions of Firefox, Edge, Safari, and Chrome. No polyfills required. 👌 +Mithril.js supports IE11, Firefox ESR, and the last two versions of Firefox, Edge, Safari, and Chrome. No polyfills required. 👌 ## Installation @@ -60,11 +60,11 @@ You may be interested in the [API Docs](https://mithril.js.org/api.html), a [Sim ## Getting Help -Mithril has an active & welcoming community on [Gitter](https://gitter.im/mithriljs/mithril.js), or feel free to ask questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/mithril.js) using the `mithril.js` tag. +Mithril.js has an active & welcoming community on [Gitter](https://gitter.im/mithriljs/mithril.js), or feel free to ask questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/mithril.js) using the `mithril.js` tag. ## Contributing -There's a [Contributing FAQ](https://mithril.js.org/contributing.html) on the mithril site that hopefully helps, but if not definitely hop into the [Gitter Room](https://gitter.im/mithriljs/mithril.js) and ask away! +There's a [Contributing FAQ](https://mithril.js.org/contributing.html) on the Mithril.js site that hopefully helps, but if not definitely hop into the [Gitter Room](https://gitter.im/mithriljs/mithril.js) and ask away! --- diff --git a/api/router.js b/api/router.js index 54134b281..650a86eb6 100644 --- a/api/router.js +++ b/api/router.js @@ -22,7 +22,7 @@ function decodeURIComponentSave(component) { module.exports = function($window, mountRedraw) { var callAsync = $window == null - // In case Mithril's loaded globally without the DOM, let's not break + // In case Mithril.js' loaded globally without the DOM, let's not break ? null : typeof $window.setImmediate === "function" ? $window.setImmediate : $window.setTimeout var p = Promise.resolve() diff --git a/docs/animation.md b/docs/animation.md index de99a8eb6..530a247e3 100644 --- a/docs/animation.md +++ b/docs/animation.md @@ -1,3 +1,7 @@ + + # Animations - [Technology choices](#technology-choices) @@ -11,7 +15,7 @@ Animations are often used to make applications come alive. Nowadays, browsers have good support for CSS animations, and there are [various](https://greensock.com/gsap) [libraries](https://github.com/julianshapiro/velocity) that provide fast JavaScript-based animations. There's also an upcoming [Web API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Using_the_Web_Animations_API) and a [polyfill](https://github.com/web-animations/web-animations-js) if you like living on the bleeding edge. -Mithril does not provide any animation APIs per se, since these other options are more than sufficient to achieve rich, complex animations. Mithril does, however, offer hooks to make life easier in some specific cases where it's traditionally difficult to make animations work. +Mithril.js does not provide any animation APIs per se, since these other options are more than sufficient to achieve rich, complex animations. Mithril.js does, however, offer hooks to make life easier in some specific cases where it's traditionally difficult to make animations work. --- @@ -41,7 +45,7 @@ m.mount(document.body, FancyComponent) ### Animation on element removal -The problem with animating before removing an element is that we must wait until the animation is complete before we can actually remove the element. Fortunately, Mithril offers the [`onbeforeremove`](lifecycle-methods.md#onbeforeremove) hook that allows us to defer the removal of an element. +The problem with animating before removing an element is that we must wait until the animation is complete before we can actually remove the element. Fortunately, Mithril.js offers the [`onbeforeremove`](lifecycle-methods.md#onbeforeremove) hook that allows us to defer the removal of an element. Let's create an `exit` animation that fades `opacity` from 1 to 0. @@ -86,7 +90,7 @@ var FancyComponent = { `vnode.dom` points to the root DOM element of the component (`
`). We use the classList API here to add an `exit` class to `
`. -Then we return a [Promise](promise.md) that resolves when the `animationend` event fires. When we return a promise from `onbeforeremove`, Mithril waits until the promise is resolved and only then it removes the element. In this case, it waits for the exit animation to finish. +Then we return a [Promise](promise.md) that resolves when the `animationend` event fires. When we return a promise from `onbeforeremove`, Mithril.js waits until the promise is resolved and only then it removes the element. In this case, it waits for the exit animation to finish. We can verify that both the enter and exit animations work by mounting the `Toggler` component: diff --git a/docs/api.md b/docs/api.md index 2f946a197..758b0cce7 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,3 +1,7 @@ + + # API ### Cheatsheet diff --git a/docs/autoredraw.md b/docs/autoredraw.md index a7a891d71..b5b508905 100644 --- a/docs/autoredraw.md +++ b/docs/autoredraw.md @@ -1,14 +1,18 @@ + + # 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. +Mithril.js 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). +When used idiomatically, Mithril.js 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: +Mithril.js automatically redraws after DOM event handlers that are defined in a Mithril.js view: ```javascript var MyComponent = { @@ -44,7 +48,7 @@ m.mount(document.body, MyComponent) ### After m.request -Mithril automatically redraws after [`m.request`](request.md) completes: +Mithril.js automatically redraws after [`m.request`](request.md) completes: ```javascript m.request("/api/v1/users").then(function() { @@ -63,7 +67,7 @@ m.request("/api/v1/users", {background: true}).then(function() { ### 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). +Mithril.js 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 = { @@ -87,11 +91,11 @@ m.route(document.body, "/", { --- -### When Mithril does not redraw +### When Mithril.js 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.js 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. +Mithril.js 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. @@ -111,7 +115,7 @@ function StableComponent() { } ``` -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`. +Mithril.js 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: @@ -123,4 +127,4 @@ m.render(document.body, m(MyComponent)) 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. +Mithril.js 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.js will automatically throttle the number of redraws to avoid lag. diff --git a/docs/buildPathname.md b/docs/buildPathname.md index 35049a4c1..dfc087e9a 100644 --- a/docs/buildPathname.md +++ b/docs/buildPathname.md @@ -1,3 +1,6 @@ + # buildPathname(object) - [Description](#description) @@ -11,7 +14,7 @@ Turns a [path template](paths.md) and a parameters object into a string of form `/path/user?a=1&b=2` ```javascript -var querystring = m.buildPathname("/path/:id", {id: "user", a: "1", b: "2"}) +var pathname = m.buildPathname("/path/:id", {id: "user", a: "1", b: "2"}) // "/path/user?a=1&b=2" ``` @@ -19,12 +22,13 @@ var querystring = m.buildPathname("/path/:id", {id: "user", a: "1", b: "2"}) ### Signature -`querystring = m.buildPathname(object)` +`pathname = m.buildPathname(object)` Argument | Type | Required | Description ------------ | ------------------------------------------ | -------- | --- -`object` | `Object` | Yes | A key-value map to be converted into a string -**returns** | `String` | | A string representing the input object +`path` | `String` | Yes | A URL path +`query ` | `Object` | Yes | A key-value map to be converted into a string +**returns** | `String` | | A string representing the URL with the query string [How to read signatures](signatures.md) @@ -35,7 +39,7 @@ Argument | Type | Required | Descripti The `m.buildPathname` creates a [path name](paths.md) from a path template and a parameters object. It's useful for building URLs, and it's what [`m.route`](route.md), [`m.request`](request.md), and [`m.jsonp`](jsonp.md) all use internally to interpolate paths. It uses [`m.buildQueryString`](buildQueryString.md) to generate the query parameters to append to the path name. ```javascript -var querystring = m.buildPathname("/path/:id", {id: "user", a: 1, b: 2}) +var pathname = m.buildPathname("/path/:id", {id: "user", a: 1, b: 2}) -// querystring is "/path/user?a=1&b=2" +// pathname is "/path/user?a=1&b=2" ``` diff --git a/docs/buildQueryString.md b/docs/buildQueryString.md index 03f723fac..1bbb8f5f9 100644 --- a/docs/buildQueryString.md +++ b/docs/buildQueryString.md @@ -1,3 +1,7 @@ + + # buildQueryString(object) - [Description](#description) @@ -23,7 +27,7 @@ var querystring = m.buildQueryString({a: "1", b: "2"}) Argument | Type | Required | Description ------------ | ------------------------------------------ | -------- | --- -`object` | `Object` | Yes | A key-value map to be converted into a string +`query` | `Object` | Yes | A key-value map to be converted into a string **returns** | `String` | | A string representing the input object [How to read signatures](signatures.md) diff --git a/docs/censor.md b/docs/censor.md index 4ece0066d..96dde5697 100644 --- a/docs/censor.md +++ b/docs/censor.md @@ -1,3 +1,7 @@ + + # censor(object, extra) - [Description](#description) @@ -109,7 +113,7 @@ function Layout() { } ``` -This would end up [throwing an error](keys.md#key-restrictions) because here's what Mithril sees when creating the `Layout` vnode: +This would end up [throwing an error](keys.md#key-restrictions) because here's what Mithril.js sees when creating the `Layout` vnode: ```javascript return [ diff --git a/docs/changelog.md b/docs/changelog.md index d5761ba69..b1f53b094 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,6 @@ + # Change log - [v2.0.4](#v204) @@ -25,7 +28,7 @@ PSA: changes to [`mithril/stream`](stream.md) are now specified in this changelo - Reduced package download size substantially by removing the archive of previous releases' documentation. ([#2561](https://github.com/MithrilJS/mithril.js/pull/2561) [@cztomsik](https://github.com/cztomsik)) - Improved error messages in multiple places. ([#2536](https://github.com/MithrilJS/mithril.js/pull/2536) [@isiahmeadows](https://github.com/isiahmeadows)) - The redraw reentrancy check was moved from `m.mount` to `m.render` and its error message was updated accordingly. ([#2536](https://github.com/MithrilJS/mithril.js/pull/2536) [@isiahmeadows](https://github.com/isiahmeadows)) - - This is unlikely to break people because if you were to do it with `m.render` directly before now, you'd corrupt Mithril's internal representation and internal errors could occur as a result. Now, it just warns you. + - This is unlikely to break people because if you were to do it with `m.render` directly before now, you'd corrupt Mithril.js' internal representation and internal errors could occur as a result. Now, it just warns you. - For a better debugging experience with `m.route` route resolvers, errors on `onmatch` in the default route are left unhandled and errors in `onmatch` in other routes are logged to the console before redirecting. ([#2536](https://github.com/MithrilJS/mithril.js/pull/2536) [@isiahmeadows](https://github.com/isiahmeadows)) - Bug fix with `m.redraw` where if you removed a root that was previously visited in the current redraw pass, it would lose its place and skip the next root. - Add `params:` attribute to `m.route.Link`. ([#2537](https://github.com/MithrilJS/mithril.js/pull/2537) [@isiahmeadows](https://github.com/isiahmeadows)) @@ -36,7 +39,7 @@ PSA: changes to [`mithril/stream`](stream.md) are now specified in this changelo - Fix issue where new redraw handlers weren't copied over on update. ([#2578](https://github.com/MithrilJS/mithril.js/pull/2578) [@isiahmeadows](https://github.com/isiahmeadows)) - Make changes to file inputs gracefully handled, and don't break if the current value and old value mismatch (and the new value isn't empty), but instead just log an error. ([#2578](https://github.com/MithrilJS/mithril.js/pull/2578) [@isiahmeadows](https://github.com/isiahmeadows)) - This mainly exists just to kick the can down the road - this is the only case I'm aware of where the DOM itself would be responsible for throwing an error. A proper fix to the greater issue of error handling is much more complex, and I'd rather not block users any longer over this one specific issue. -- Allow Mithril to be loaded in non-browser environments without modification. ([#2633](https://github.com/MithrilJS/mithril.js/pull/2633) [@isiahmeadows](https://github.com/isiahmeadows)) +- Allow Mithril.js to be loaded in non-browser environments without modification. ([#2633](https://github.com/MithrilJS/mithril.js/pull/2633) [@isiahmeadows](https://github.com/isiahmeadows)) - Work around a bundler bug that corrupted RegExps [#2647](https://github.com/MithrilJS/mithril.js/issues/) ([#2655](https://github.com/MithrilJS/mithril.js/pull/2655)) - Adapt handling of no content (204) responses to match XHR Spec ([#2624](https://github.com/MithrilJS/mithril.js/pull/2641)) [@Evoke-PHP](https://github.com/Evoke-PHP) - Add `URLSearchParams` support to `m.request` ([#2695](https://github.com/MithrilJS/mithril.js/pull/2695) [@Coteh](https://github.com/Coteh)) @@ -45,11 +48,11 @@ PSA: changes to [`mithril/stream`](stream.md) are now specified in this changelo Important note: if you were using any of these undocumented tools, they are no longer available as of this release. This is not considered a breaking change as they were written for internal usage and as of v2 are all 100% unsupported in userland. -- Mithril's internal bundler, previously available at `mithril/bundler` +- Mithril.js' internal bundler, previously available at `mithril/bundler` - Prefer using a dedicated bundler like Webpack or Rollup instead. -- Mithril's CommonJS sham polyfill, previously available at `mithril/module` +- Mithril.js' CommonJS sham polyfill, previously available at `mithril/module` - Prefer using native `import`/`export` and/or Budo instead. -- Mithril's internal test mocks, previously available at `mithril/test-utils` +- Mithril.js' internal test mocks, previously available at `mithril/test-utils` - Prefer using JSDOM or similar instead. I'd like to apologize for missing these deprecations in the initial 2.0.0 change log. This was a major policy change we had been communicating the entire time and we should've let you all know this there in the change log as well. @@ -115,7 +118,7 @@ _2019-07-24_ - This lets you ensure global layouts used in `render` still render by diff. - redraw: `mithril/redraw` now just exposes the `m.redraw` callback ([#2458](https://github.com/MithrilJS/mithril.js/pull/2458) [@isiahmeadows](https://github.com/isiahmeadows)) - The `.schedule`, `.unschedule`, and `.render` properties of the former `redrawService` are all removed. - - If you want to know how to work around it, look at the call to `mount` in Mithril's source for `m.route`. That should help you in finding ways around the removed feature. (It doesn't take that much more code.) + - If you want to know how to work around it, look at the call to `mount` in Mithril.js' source for `m.route`. That should help you in finding ways around the removed feature. (It doesn't take that much more code.) - api: `m.version` has been removed. If you really need the version for whatever reason, just read the `version` field of `mithril/package.json` directly. ([#2466](https://github.com/MithrilJS/mithril.js/pull/2466) [@isiahmeadows](https://github.com/isiahmeadows)) - route: `m.route.prefix(...)` is now `m.route.prefix = ...`. ([#2469](https://github.com/MithrilJS/mithril.js/pull/2469) [@isiahmeadows](https://github.com/isiahmeadows)) - This is a fully fledged property, so you can not only write to it, but you can also read from it. @@ -128,7 +131,7 @@ _2019-07-24_ #### News -- Mithril now only officially supports IE11, Firefox ESR, and the last two versions of Chrome/FF/Edge/Safari. ([#2296](https://github.com/MithrilJS/mithril.js/pull/2296)) +- Mithril.js now only officially supports IE11, Firefox ESR, and the last two versions of Chrome/FF/Edge/Safari. ([#2296](https://github.com/MithrilJS/mithril.js/pull/2296)) - API: Introduction of `m.redraw.sync()` ([#1592](https://github.com/MithrilJS/mithril.js/pull/1592)) - API: Event handlers may also be objects with `handleEvent` methods ([#1949](https://github.com/MithrilJS/mithril.js/pull/1949), [#2222](https://github.com/MithrilJS/mithril.js/pull/2222)). - API: `m.request` better error message on JSON parse error - ([#2195](https://github.com/MithrilJS/mithril.js/pull/2195), [@codeclown](https://github.com/codeclown)) @@ -195,7 +198,7 @@ _2019-07-24_ ## `mithril-stream` change log -Formerly, `mithril/stream` was available standalone as the package `mithril-stream`, but this package has been deprecated and is no longer updated. The changelog for that package prior to being merged back into Mithril proper is below. +Formerly, `mithril/stream` was available standalone as the package `mithril-stream`, but this package has been deprecated and is no longer updated. The changelog for that package prior to being merged back into Mithril.js proper is below. ### 2.0.0 _2019-02-07_ diff --git a/docs/code-of-conduct.md b/docs/code-of-conduct.md index 70fbcb0ad..0f41b58de 100644 --- a/docs/code-of-conduct.md +++ b/docs/code-of-conduct.md @@ -1,3 +1,7 @@ + + # Contributor Covenant Code of Conduct ## Our Pledge diff --git a/docs/components.md b/docs/components.md index 88bd707ac..7ef3733ff 100644 --- a/docs/components.md +++ b/docs/components.md @@ -1,3 +1,7 @@ + + # Components - [Structure](#structure) @@ -15,7 +19,7 @@ Components are a mechanism to encapsulate parts of a view to make code easier to organize and/or reuse. -Any JavaScript object that has a `view` method is a Mithril component. Components can be consumed via the [`m()`](hyperscript.md) utility: +Any JavaScript object that has a `view` method is a Mithril.js component. Components can be consumed via the [`m()`](hyperscript.md) utility: ```javascript // define your component @@ -104,7 +108,7 @@ var Example = { } ``` -NOTE: Lifecycle methods can also be defined in the `attrs` object, so you should avoid using their names for your own callbacks as they would also be invoked by Mithril itself. Use them in `attrs` only when you specifically wish to use them as lifecycle methods. +NOTE: Lifecycle methods can also be defined in the `attrs` object, so you should avoid using their names for your own callbacks as they would also be invoked by Mithril.js itself. Use them in `attrs` only when you specifically wish to use them as lifecycle methods. --- @@ -112,13 +116,13 @@ NOTE: Lifecycle methods can also be defined in the `attrs` object, so you should Like all virtual DOM nodes, component vnodes can have state. Component state is useful for supporting object-oriented architectures, for encapsulation and for separation of concerns. -Note that unlike many other frameworks, mutating component state does *not* trigger [redraws](autoredraw.md) or DOM updates. Instead, redraws are performed when event handlers fire, when HTTP requests made by [m.request](request.md) complete or when the browser navigates to different routes. Mithril's component state mechanisms simply exist as a convenience for applications. +Note that unlike many other frameworks, mutating component state does *not* trigger [redraws](autoredraw.md) or DOM updates. Instead, redraws are performed when event handlers fire, when HTTP requests made by [m.request](request.md) complete or when the browser navigates to different routes. Mithril.js' component state mechanisms simply exist as a convenience for applications. If a state change occurs that is not as a result of any of the above conditions (e.g. after a `setTimeout`), then you can use `m.redraw()` to trigger a redraw manually. #### Closure component state -In the above examples, each component is defined as a POJO (Plain Old JavaScript Object), which is used by Mithril internally as the prototype for that component's instances. It's possible to use component state with a POJO (as we'll discuss below), but it's not the cleanest or simplest approach. For that we'll use a **_closure component_**, which is simply a wrapper function which _returns_ a POJO component instance, which in turn carries its own, closed-over scope. +In the above examples, each component is defined as a POJO (Plain Old JavaScript Object), which is used by Mithril.js internally as the prototype for that component's instances. It's possible to use component state with a POJO (as we'll discuss below), but it's not the cleanest or simplest approach. For that we'll use a **_closure component_**, which is simply a wrapper function which _returns_ a POJO component instance, which in turn carries its own, closed-over scope. With a closure component, state can simply be maintained by variables that are declared within the outer function: @@ -337,7 +341,7 @@ Components can be freely mixed. A class component can have closure or POJO compo ### Special attributes -Mithril places special semantics on several property keys, so you should normally avoid using them in normal component attributes. +Mithril.js places special semantics on several property keys, so you should normally avoid using them in normal component attributes. - [Lifecycle methods](lifecycle-methods.md): `oninit`, `oncreate`, `onbeforeupdate`, `onupdate`, `onbeforeremove`, and `onremove` - `key`, which is used to track identity in keyed fragments @@ -347,7 +351,7 @@ Mithril places special semantics on several property keys, so you should normall ### Avoid anti-patterns -Although Mithril is flexible, some code patterns are discouraged: +Although Mithril.js is flexible, some code patterns are discouraged: #### Avoid fat components diff --git a/docs/contributing.md b/docs/contributing.md index acc2345c4..0d33ba369 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,17 +1,20 @@ + # Contributing FAQs - [How do I go about contributing ideas or new features?](#how-do-i-go-about-contributing-ideas-or-new-features?) - [How should I report bugs?](#how-should-i-report-bugs?) - [How do I send a pull request?](#how-do-i-send-a-pull-request?) - [I'm submitting a PR. How do I run tests?](#i'm-submitting-a-pr-how-do-i-run-tests?) -- [How do I build Mithril?](#how-do-i-build-mithril?) +- [How do I build Mithril.js?](#how-do-i-build-mithril?) - [Is there a style guide?](#is-there-a-style-guide?) - [How do I embed live previews in docs?](#how-do-I-embed-live-previews-in-docs?) - [Why do tests mock the browser APIs?](#why-do-tests-mock-the-browser-apis?) -- [Why does Mithril use its own testing framework and not Mocha/Jasmine/Tape?](#why-does-mithril-use-its-own-testing-framework-and-not-mochajasminetape?) -- [Why doesn't the Mithril codebase use ES6 via Babel or Bublé? Would a PR to upgrade be welcome?](#why-doesn't-the-mithril-codebase-use-es6-via-babel-or-bublé?-would-a-pr-to-upgrade-be-welcome?) -- [Why doesn't the Mithril codebase use trailing semi-colons? Would a PR to add them be welcome?](#why-doesn't-the-mithril-codebase-use-trailing-semi-colons?-would-a-pr-to-add-them-be-welcome?) -- [Why does the Mithril codebase use a mix of `instanceof` and `typeof` checks instead of `Object.prototype.toString.call`, `Array.isArray`, etc? Would a PR to refactor those checks be welcome?](#why-does-the-mithril-codebase-use-a-mix-of-instanceof-and-typeof-checks-instead-of-objectprototypetostringcall,-arrayisarray,-etc?-would-a-pr-to-refactor-those-checks-be-welcome?) +- [Why does Mithril.js use its own testing framework and not Mocha/Jasmine/Tape?](#why-does-mithril-use-its-own-testing-framework-and-not-mochajasminetape?) +- [Why doesn't the Mithril.js codebase use ES6 via Babel or Bublé? Would a PR to upgrade be welcome?](#why-doesn't-the-mithril-codebase-use-es6-via-babel-or-bublé?-would-a-pr-to-upgrade-be-welcome?) +- [Why doesn't the Mithril.js codebase use trailing semi-colons? Would a PR to add them be welcome?](#why-doesn't-the-mithril-codebase-use-trailing-semi-colons?-would-a-pr-to-add-them-be-welcome?) +- [Why does the Mithril.js codebase use a mix of `instanceof` and `typeof` checks instead of `Object.prototype.toString.call`, `Array.isArray`, etc? Would a PR to refactor those checks be welcome?](#why-does-the-mithril-codebase-use-a-mix-of-instanceof-and-typeof-checks-instead-of-objectprototypetostringcall,-arrayisarray,-etc?-would-a-pr-to-refactor-those-checks-be-welcome?) - [What should I know in advance when attempting a performance related contribution?](#What-should-I-know-in-advance-when-attempting-a-performance-related-contribution?) - [Do you all accept donations?](#do-you-all-accept-donations?) @@ -54,9 +57,9 @@ There is no need to `npm install` anything in order to run the test suite, howev -## How do I build Mithril? +## How do I build Mithril.js? -If all you're trying to do is run examples in the codebase, you don't need to build Mithril, you can just open the various html files and things should just work. +If all you're trying to do is run examples in the codebase, you don't need to build Mithril.js, you can just open the various html files and things should just work. To generate the bundled file for testing, run `npm run dev` from the command line. To generate the minified file, run `npm run build`. There is no need to `npm install` anything, but NodeJS is required to run the build scripts. @@ -84,27 +87,27 @@ Another important reason is that it allows us to document browser API quirks via -## Why does Mithril use its own testing framework and not Mocha/Jasmine/Tape? +## Why does Mithril.js use its own testing framework and not Mocha/Jasmine/Tape? Mainly to avoid requiring dependencies. `ospec` is customized to provide only essential information for common testing workflows (namely, no spamming ok's on pass, and accurate noiseless errors on failure) -## Why doesn't the Mithril codebase use ES6 via Babel or Bublé? Would a PR to upgrade be welcome? +## Why doesn't the Mithril.js codebase use ES6 via Babel or Bublé? Would a PR to upgrade be welcome? -Being able to run Mithril's raw source code in all supported browsers is a requirement for all browser-related modules in this repo. In addition, transpiled code is generally much bulkier. +Being able to run Mithril.js' raw source code in all supported browsers is a requirement for all browser-related modules in this repo. In addition, transpiled code is generally much bulkier. -## Why doesn't the Mithril codebase use trailing semi-colons? Would a PR to add them be welcome? +## Why doesn't the Mithril.js codebase use trailing semi-colons? Would a PR to add them be welcome? I don't use them. Adding them means the semi-colon usage in the codebase will eventually become inconsistent. Besides, [we aren't the only one who've decided to drop the semicolon](https://standardjs.com/#who-uses-javascript-standard-style). (We don't use Standard, though.) -## Why does the Mithril codebase use a mix of `instanceof` and `typeof` checks instead of `Object.prototype.toString.call`, `Array.isArray`, etc? Would a PR to refactor those checks be welcome? +## Why does the Mithril.js codebase use a mix of `instanceof` and `typeof` checks instead of `Object.prototype.toString.call`, `Array.isArray`, etc? Would a PR to refactor those checks be welcome? -Mithril avoids peeking at objects' [[class]] string for performance considerations. Many type checks are seemingly inconsistent, weird or convoluted because those specific constructs demonstrated the best performance profile in benchmarks compared to alternatives. +Mithril.js avoids peeking at objects' [[class]] string for performance considerations. Many type checks are seemingly inconsistent, weird or convoluted because those specific constructs demonstrated the best performance profile in benchmarks compared to alternatives. Type checks are generally already irreducible expressions and having micro-modules for type checking subroutines would add maintenance overhead. diff --git a/docs/credits.md b/docs/credits.md index df95dca5c..bb2a3e77b 100644 --- a/docs/credits.md +++ b/docs/credits.md @@ -1,21 +1,25 @@ + + # Credits -Mithril was originally written by Leo Horie, but it is where it is today thanks to the hard work and great ideas of many people. +Mithril.js was originally written by Leo Horie, but it is where it is today thanks to the hard work and great ideas of many people. Special thanks to: -- Pat Cavit, who exposed most of the public API for Mithril 1.0, brought in test coverage and automated the publishing process +- Pat Cavit, who exposed most of the public API for Mithril.js 1.0, brought in test coverage and automated the publishing process - Isiah Meadows, who brought in linting, modernized the test suite and has been a strong voice in design discussions - Zoli Kahan, who replaced the original Promise implementation with one that actually worked properly - Alec Embke, who single-handedly wrote the JSON-P implementation -- Barney Carroll, who suggested many great ideas and relentlessly pushed Mithril to the limit to uncover design issues prior to Mithril 1.0 +- Barney Carroll, who suggested many great ideas and relentlessly pushed Mithril.js to the limit to uncover design issues prior to Mithril.js 1.0 - Dominic Gannaway, who offered insanely meticulous technical insight into rendering performance -- Boris Letocha, whose search space reduction algorithm is the basis for Mithril's virtual DOM engine -- Joel Richard, whose monomorphic virtual DOM structure is the basis for Mithril's vnode implementation -- Simon Friis Vindum, whose open source work was an inspiration to many design decisions for Mithril 1.0 -- Boris Kaul, for his awesome work on the benchmarking tools used to develop Mithril -- Leon Sorokin, for writing a DOM instrumentation tool that helped improve performance in Mithril 1.0 -- Jordan Walke, whose work on React was prior art to the implementation of keys in Mithril +- Boris Letocha, whose search space reduction algorithm is the basis for Mithril.js' virtual DOM engine +- Joel Richard, whose monomorphic virtual DOM structure is the basis for Mithril.js' vnode implementation +- Simon Friis Vindum, whose open source work was an inspiration to many design decisions for Mithril.js 1.0 +- Boris Kaul, for his awesome work on the benchmarking tools used to develop Mithril.js +- Leon Sorokin, for writing a DOM instrumentation tool that helped improve performance in Mithril.js 1.0 +- Jordan Walke, whose work on React was prior art to the implementation of keys in Mithril.js - Pierre-Yves Gérardy, who consistently makes high quality contributions - Gyandeep Singh, who contributed significant IE performance improvements @@ -23,4 +27,4 @@ Other people who also deserve recognition: - Arthur Clemens - creator of [Polythene](https://github.com/ArthurClemens/Polythene) and the [HTML-to-Mithril converter](https://arthurclemens.github.io/mithril-template-converter/index.html) - Stephan Hoyer - creator of [mithril-node-render](https://github.com/StephanHoyer/mithril-node-render), [mithril-query](https://github.com/StephanHoyer/mithril-query) and [mithril-source-hint](https://github.com/StephanHoyer/mithril-source-hint) -- the countless people who have reported and fixed bugs, participated in discussions, and helped promote Mithril +- the countless people who have reported and fixed bugs, participated in discussions, and helped promote Mithril.js diff --git a/docs/es6.md b/docs/es6.md index 305f57798..b673f7d1d 100644 --- a/docs/es6.md +++ b/docs/es6.md @@ -1,3 +1,6 @@ + # ES6+ on legacy browsers - [Setup](#setup) @@ -5,7 +8,7 @@ --- -Mithril is written in ES5, but it's fully compatible with ES6 and later as well. All modern browsers do support it natively, up to and even including native module syntax. (They don't support Node's magic module resolution, so you can't use `import * as _ from "lodash-es"` or similar. They just support relative and URL paths.) And so you can feel free to use [arrow functions for your closure components and classes for your class components](components.md). +Mithril.js is written in ES5, but it's fully compatible with ES6 and later as well. All modern browsers do support it natively, up to and even including native module syntax. (They don't support Node's magic module resolution, so you can't use `import * as _ from "lodash-es"` or similar. They just support relative and URL paths.) And so you can feel free to use [arrow functions for your closure components and classes for your class components](components.md). But, if like many of us, you still need to support older browsers like Internet Explorer, you'll need to transpile that down to ES5, and this is what this page is all about, using [Babel](https://babeljs.io) to make modern ES6+ code work on older browsers. diff --git a/docs/examples.md b/docs/examples.md index 4776f3de7..e591936b4 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -1,6 +1,10 @@ + + # Examples -Here are some examples of Mithril in action +Here are some examples of Mithril.js in action - [Animation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/animation/mosaic.html) - [Community Added Examples](https://how-to-mithril.js.org) diff --git a/docs/fragment.md b/docs/fragment.md index f6269d073..86602d17c 100644 --- a/docs/fragment.md +++ b/docs/fragment.md @@ -1,3 +1,7 @@ + + # fragment(attrs, children) - [Description](#description) diff --git a/docs/framework-comparison.md b/docs/framework-comparison.md index 786afc7ec..1612baa4c 100644 --- a/docs/framework-comparison.md +++ b/docs/framework-comparison.md @@ -1,30 +1,34 @@ + + # Framework comparison - [Why not X?](#why-not-insert-favorite-framework-here?) -- [Why use Mithril?](#why-use-mithril?) +- [Why use Mithril.js?](#why-use-mithril?) - [React](#react) - [Angular](#angular) - [Vue](#vue) -If you're reading this page, you probably have used other frameworks to build applications, and you want to know if Mithril would help you solve your problems more effectively. +If you're reading this page, you probably have used other frameworks to build applications, and you want to know if Mithril.js would help you solve your problems more effectively. --- ## Why not [insert favorite framework here]? -The reality is that most modern frameworks are fast, well-suited to build complex applications, and maintainable if you know how to use them effectively. There are examples of highly complex applications in the wild using just about every popular framework: Udemy uses Angular, AirBnB uses React, Gitlab uses Vue, Guild Wars 2 uses Mithril (yes, inside the game!). Clearly, these are all production-quality frameworks. +The reality is that most modern frameworks are fast, well-suited to build complex applications, and maintainable if you know how to use them effectively. There are examples of highly complex applications in the wild using just about every popular framework: Udemy uses Angular, AirBnB uses React, Gitlab uses Vue, Guild Wars 2 uses Mithril.js (yes, inside the game!). Clearly, these are all production-quality frameworks. As a rule of thumb, if your team is already heavily invested in another framework/library/stack, it makes more sense to stick with it, unless your team agrees that there's a very strong reason to justify a costly rewrite. -However, if you're starting something new, do consider giving Mithril a try, if nothing else, to see how much value Mithril adopters have been getting out of under 10kb (gzipped) of code. Mithril is used by many well-known companies (e.g. Vimeo, Nike, Fitbit), and it powers large open-sourced platforms too (e.g. Lichess, Flarum). +However, if you're starting something new, do consider giving Mithril.js a try, if nothing else, to see how much value Mithril.js adopters have been getting out of under 10kb (gzipped) of code. Mithril.js is used by many well-known companies (e.g. Vimeo, Nike, Fitbit), and it powers large open-sourced platforms too (e.g. Lichess, Flarum). --- -## Why use Mithril? +## Why use Mithril.js? -In one sentence: because **Mithril is pragmatic**. This [10 minute guide](index.md) is a good example: that's how long it takes to learn components, XHR and routing - and that's just about the right amount of knowledge needed to build useful applications. +In one sentence: because **Mithril.js is pragmatic**. This [10 minute guide](index.md) is a good example: that's how long it takes to learn components, XHR and routing - and that's just about the right amount of knowledge needed to build useful applications. -Mithril is all about getting meaningful work done efficiently. Doing file uploads? [The docs show you how](request.md#file-uploads). Authentication? [Documented too](route.md#authentication). Exit animations? [You got it](animation.md). No extra libraries, no magic. +Mithril.js is all about getting meaningful work done efficiently. Doing file uploads? [The docs show you how](request.md#file-uploads). Authentication? [Documented too](route.md#authentication). Exit animations? [You got it](animation.md). No extra libraries, no magic. --- @@ -34,25 +38,25 @@ Mithril is all about getting meaningful work done efficiently. Doing file upload React is a view library maintained by Facebook. -React and Mithril share a lot of similarities. If you already learned React, you already know almost all you need to build apps with Mithril. +React and Mithril.js share a lot of similarities. If you already learned React, you already know almost all you need to build apps with Mithril. - They both use virtual DOM, lifecycle methods and key-based reconciliation - They both organize views via components - They both use JavaScript as a flow control mechanism within views -The most obvious difference between React and Mithril is in their scope. React is a view library, so a typical React-based application relies on third-party libraries for routing, XHR and state management. Using a library oriented approach allows developers to customize their stack to precisely match their needs. The not-so-nice way of saying that is that React-based architectures can vary wildly from project to project, and that those projects are that much more likely to cross the 1MB size line. +The most obvious difference between React and Mithril.js is in their scope. React is a view library, so a typical React-based application relies on third-party libraries for routing, XHR and state management. Using a library oriented approach allows developers to customize their stack to precisely match their needs. The not-so-nice way of saying that is that React-based architectures can vary wildly from project to project, and that those projects are that much more likely to cross the 1MB size line. -Mithril has built-in modules for common necessities such as routing and XHR, and the [guide](simple-application.md) demonstrates idiomatic usage. This approach is preferable for teams that value consistency and ease of onboarding. +Mithril.js has built-in modules for common necessities such as routing and XHR, and the [guide](simple-application.md) demonstrates idiomatic usage. This approach is preferable for teams that value consistency and ease of onboarding. #### Performance -Both React and Mithril care strongly about rendering performance, but go about it in different ways. In the past React had two DOM rendering implementations (one using the DOM API, and one using `innerHTML`). Its upcoming fiber architecture introduces scheduling and prioritization of units of work. React also has a sophisticated build system that disables various checks and error messages for production deployments, and various browser-specific optimizations. In addition, there are also several performance-oriented libraries that leverage React's `shouldComponentUpdate` hook and immutable data structure libraries' fast object equality checking properties to reduce virtual DOM reconciliation times. Generally speaking, React's approach to performance is to engineer relatively complex solutions. +Both React and Mithril.js care strongly about rendering performance, but go about it in different ways. In the past React had two DOM rendering implementations (one using the DOM API, and one using `innerHTML`). Its upcoming fiber architecture introduces scheduling and prioritization of units of work. React also has a sophisticated build system that disables various checks and error messages for production deployments, and various browser-specific optimizations. In addition, there are also several performance-oriented libraries that leverage React's `shouldComponentUpdate` hook and immutable data structure libraries' fast object equality checking properties to reduce virtual DOM reconciliation times. Generally speaking, React's approach to performance is to engineer relatively complex solutions. -Mithril follows the less-is-more school of thought. It has a substantially smaller, aggressively optimized codebase. The rationale is that a small codebase is easier to audit and optimize, and ultimately results in less code being run. +Mithril.js follows the less-is-more school of thought. It has a substantially smaller, aggressively optimized codebase. The rationale is that a small codebase is easier to audit and optimize, and ultimately results in less code being run. Here's a comparison of library load times, i.e. the time it takes to parse and run the JavaScript code for each framework, by adding a `console.time()` call on the first line and a `console.timeEnd()` call on the last of a script that is composed solely of framework code. For your reading convenience, here are best-of-20 results with logging code manually added to bundled scripts, running from the filesystem, in Chrome on a modest 2010 PC desktop: -React | Mithril +React | Mithril.js ------- | ------- 55.8 ms | 4.5 ms @@ -60,23 +64,23 @@ Library load times matter in applications that don't stay open for long periods Since this is a micro-benchmark, you are encouraged to replicate these tests yourself since hardware can heavily affect the numbers. Note that bundler frameworks like Webpack can move dependencies out before the timer calls to emulate static module resolution, so you should either copy the code from the compiled CDN files or open the output file from the bundler library, and manually add the high resolution timer calls `console.time` and `console.timeEnd` to the bundled script. Avoid using `new Date` and `performance.now`, as those mechanisms are not as statistically accurate. -For your reading convenience, here's a version of that benchmark adapted to use CDNs on the web: the [benchmark for React is here](https://jsfiddle.net/0ovkv64u/), and the [benchmark for Mithril is here](https://jsfiddle.net/o7hxooqL/). Note that we're benchmarking all of Mithril rather than benchmarking only the rendering module (which would be equivalent in scope to React). Also note that this CDN-driven setup incurs some overheads due to fetching resources from disk cache (~2ms per resource). Due to those reasons, the numbers here are not entirely accurate, but they should be sufficient to observe that Mithril's initialization speed is noticeably better than React. +For your reading convenience, here's a version of that benchmark adapted to use CDNs on the web: the [benchmark for React is here](https://jsfiddle.net/0ovkv64u/), and the [benchmark for Mithril.js is here](https://jsfiddle.net/o7hxooqL/). Note that we're benchmarking all of Mithril.js rather than benchmarking only the rendering module (which would be equivalent in scope to React). Also note that this CDN-driven setup incurs some overheads due to fetching resources from disk cache (~2ms per resource). Due to those reasons, the numbers here are not entirely accurate, but they should be sufficient to observe that Mithril.js' initialization speed is noticeably better than React. -Here's a slightly more meaningful benchmark: measuring the scripting time for creating 10,000 divs (and 10,000 text nodes). Again, here's the benchmark code for [React](https://jsfiddle.net/bfoeay4f/) and [Mithril](https://jsfiddle.net/fft0ht7n/). Their best results are shown below: +Here's a slightly more meaningful benchmark: measuring the scripting time for creating 10,000 divs (and 10,000 text nodes). Again, here's the benchmark code for [React](https://jsfiddle.net/bfoeay4f/) and [Mithril.js](https://jsfiddle.net/fft0ht7n/). Their best results are shown below: -React | Mithril +React | Mithril.js ------- | ------- 99.7 ms | 42.8 ms -What these numbers show is that not only does Mithril initializes significantly faster, it can process upwards of 20,000 virtual DOM nodes before React is ready to use. +What these numbers show is that not only does Mithril.js initializes significantly faster, it can process upwards of 20,000 virtual DOM nodes before React is ready to use. ##### Update performance Update performance can be even more important than first-render performance, since updates can happen many times while a Single Page Application is running. -A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare a [React implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/react/index.html) and a [Mithril implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Sample results are shown below: +A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare a [React implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/react/index.html) and a [Mithril.js implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Sample results are shown below: -React | Mithril +React | Mithril.js ------- | ------- 12.1 ms | 6.4 ms @@ -88,7 +92,7 @@ Another thing to keep in mind is that because React adds extra checks and helpfu There are [several](https://preactjs.com/) [projects](https://github.com/Lucifier129/react-lite) [that](https://infernojs.org/) [claim](https://github.com/alibaba/rax) API parity with React (some via compatibility layer libraries), but they are not fully compatible (e.g. PropType support is usually stubbed out, synthetic events are sometimes not supported, and some APIs have different semantics). Note that these libraries typically also include features of their own that are not part of the official React API, which may become problematic down the road if one decides to switch back to React Fiber. -Claims about small download size (compared to React) are accurate, but most of these libraries are slightly larger than Mithril's renderer module. Preact is the only exception. +Claims about small download size (compared to React) are accurate, but most of these libraries are slightly larger than Mithril.js' renderer module. Preact is the only exception. Be wary of aggressive performance claims, as benchmarks used by some of these projects are known to be out-of-date and flawed (in the sense that they can be - and are - exploited). Boris Kaul (author of some of the benchmarks) has [written in detail about how benchmarks are gamed](https://medium.com/@localvoid/how-to-win-in-web-framework-benchmarks-8bc31af76ce7). Another thing to keep in mind is that some benchmarks aggressively use advanced optimization features and thus demonstrate *potential* performance, i.e. performance that is possible given some caveats, but realistically unlikely unless you actively spend the time to go over your entire codebase identifying optimization candidates and evaluating the regression risks brought by the optimization caveats. @@ -96,13 +100,13 @@ In the spirit of demonstrating *typical* performance characteristics, the benchm #### Complexity -Both React and Mithril have relatively small API surfaces compared to other frameworks, which help ease learning curve. However, whereas idiomatic Mithril can be written without loss of readability using plain ES5 and no other dependencies, idiomatic React relies heavily on complex tooling (e.g. Babel, JSX plugin, etc), and this level of complexity frequently extends to popular parts of its ecosystem, be it in the form of syntax extensions (e.g. non-standard object spread syntax in Redux), architectures (e.g. ones using immutable data libraries), or bells and whistles (e.g. hot module reloading). +Both React and Mithril.js have relatively small API surfaces compared to other frameworks, which help ease learning curve. However, whereas idiomatic Mithril.js can be written without loss of readability using plain ES5 and no other dependencies, idiomatic React relies heavily on complex tooling (e.g. Babel, JSX plugin, etc), and this level of complexity frequently extends to popular parts of its ecosystem, be it in the form of syntax extensions (e.g. non-standard object spread syntax in Redux), architectures (e.g. ones using immutable data libraries), or bells and whistles (e.g. hot module reloading). -While complex toolchains are also possible with Mithril and other frameworks alike, it's *strongly* recommended that you follow the [KISS](https://en.wikipedia.org/wiki/KISS_principle) and [YAGNI](https://en.wikipedia.org/wiki/You_aren't_gonna_need_it) principles when using Mithril. +While complex toolchains are also possible with Mithril.js and other frameworks alike, it's *strongly* recommended that you follow the [KISS](https://en.wikipedia.org/wiki/KISS_principle) and [YAGNI](https://en.wikipedia.org/wiki/You_aren't_gonna_need_it) principles when using Mithril. #### Learning curve -Both React and Mithril have relatively small learning curves. React's learning curve mostly involves understanding components and their lifecycle. The learning curve for Mithril components is nearly identical. There are obviously more APIs to learn in Mithril, since Mithril also includes routing and XHR, but the learning curve would be fairly similar to learning React, React Router and a XHR library like superagent or axios. +Both React and Mithril.js have relatively small learning curves. React's learning curve mostly involves understanding components and their lifecycle. The learning curve for Mithril.js components is nearly identical. There are obviously more APIs to learn in Mithril.js, since Mithril.js also includes routing and XHR, but the learning curve would be fairly similar to learning React, React Router and a XHR library like superagent or axios. Idiomatic React requires working knowledge of JSX and its caveats, and therefore there's also a small learning curve related to Babel. @@ -110,9 +114,9 @@ Idiomatic React requires working knowledge of JSX and its caveats, and therefore React documentation is clear and well written, and includes a good API reference, tutorials for getting started, as well as pages covering various advanced concepts. Unfortunately, since React is limited to being only a view library, its documentation does not explore how to use React idiomatically in the context of a real-life application. As a result, there are many popular state management libraries and thus architectures using React can differ drastically from company to company (or even between projects). -Mithril documentation also includes [introductory](index.md) [tutorials](simple-application.md), pages about advanced concepts, and an extensive API reference section, which includes input/output type information, examples for various common use cases and advice against misuse and anti-patterns. It also includes a cheatsheet for quick reference. +Mithril.js documentation also includes [introductory](index.md) [tutorials](simple-application.md), pages about advanced concepts, and an extensive API reference section, which includes input/output type information, examples for various common use cases and advice against misuse and anti-patterns. It also includes a cheatsheet for quick reference. -Mithril documentation also demonstrates simple, close-to-the-metal solutions to common use cases in real-life applications where it's appropriate to inform a developer that web standards may be now on par with larger established libraries. +Mithril.js documentation also demonstrates simple, close-to-the-metal solutions to common use cases in real-life applications where it's appropriate to inform a developer that web standards may be now on par with larger established libraries. --- @@ -120,50 +124,50 @@ Mithril documentation also demonstrates simple, close-to-the-metal solutions to Angular is a web application framework maintained by Google. -Angular and Mithril are fairly different, but they share a few similarities: +Angular and Mithril.js are fairly different, but they share a few similarities: - Both support componentization - Both have an array of tools for various aspects of web applications (e.g. routing, XHR) -The most obvious difference between Angular and Mithril is in their complexity. This can be seen most easily in how views are implemented. Mithril views are plain JavaScript, and flow control is done with JavaScript built-in mechanisms such as ternary operators or `Array.prototype.map`. Angular, on the other hand, implements a directive system to extend HTML views so that it's possible to evaluate JavaScript-like expressions within HTML attributes and interpolations. Angular actually ships with a parser and a compiler written in JavaScript to achieve that. If that doesn't seem complex enough, there's actually two compilation modes (a default mode that generates JavaScript functions dynamically for performance, and [a slower mode](https://docs.angularjs.org/api/ng/directive/ngCsp) for dealing with Content Security Policy restrictions). +The most obvious difference between Angular and Mithril.js is in their complexity. This can be seen most easily in how views are implemented. Mithril.js views are plain JavaScript, and flow control is done with JavaScript built-in mechanisms such as ternary operators or `Array.prototype.map`. Angular, on the other hand, implements a directive system to extend HTML views so that it's possible to evaluate JavaScript-like expressions within HTML attributes and interpolations. Angular actually ships with a parser and a compiler written in JavaScript to achieve that. If that doesn't seem complex enough, there's actually two compilation modes (a default mode that generates JavaScript functions dynamically for performance, and [a slower mode](https://docs.angularjs.org/api/ng/directive/ngCsp) for dealing with Content Security Policy restrictions). #### Performance -Angular has made a lot of progress in terms of performance over the years. Angular 1 used a mechanism known as dirty checking which tended to get slow due to the need to constantly diff large `$scope` structures. Angular 2 uses a template change detection mechanism that is much more performant. However, even despite Angular's improvements, Mithril is often faster than Angular, due to the ease of auditing that Mithril's small codebase size affords. +Angular has made a lot of progress in terms of performance over the years. Angular 1 used a mechanism known as dirty checking which tended to get slow due to the need to constantly diff large `$scope` structures. Angular 2 uses a template change detection mechanism that is much more performant. However, even despite Angular's improvements, Mithril.js is often faster than Angular, due to the ease of auditing that Mithril.js' small codebase size affords. -It's difficult to make a comparison of load times between Angular and Mithril for a couple of reasons. The first is that Angular 1 and 2 are in fact completely different codebases, and both versions are officially supported and maintained (and the vast majority of Angular codebases in the wild currently still use version 1). The second reason is that both Angular and Mithril are modular. In both cases, it's possible to remove a significant part of the framework that is not used in a given application. +It's difficult to make a comparison of load times between Angular and Mithril.js for a couple of reasons. The first is that Angular 1 and 2 are in fact completely different codebases, and both versions are officially supported and maintained (and the vast majority of Angular codebases in the wild currently still use version 1). The second reason is that both Angular and Mithril.js are modular. In both cases, it's possible to remove a significant part of the framework that is not used in a given application. -With that being said, the smallest known Angular 2 bundle is a [29kb hello world](https://www.lucidchart.com/techblog/2016/09/26/improving-angular-2-load-times/) compressed w/ the Brotli algorithm (it's 35kb using standard gzip), and with most of Angular's useful functionality removed. By comparison, a Mithril hello world - including the entire Mithril core with batteries and everything - would be about 10kb gzipped. +With that being said, the smallest known Angular 2 bundle is a [29kb hello world](https://www.lucidchart.com/techblog/2016/09/26/improving-angular-2-load-times/) compressed w/ the Brotli algorithm (it's 35kb using standard gzip), and with most of Angular's useful functionality removed. By comparison, a Mithril.js hello world - including the entire Mithril.js core with batteries and everything - would be about 10kb gzipped. -Also, remember that frameworks like Angular and Mithril are designed for non-trivial application, so an application that managed to use all of Angular's API surface would need to download several hundred kb of framework code, rather than merely 29kb. +Also, remember that frameworks like Angular and Mithril.js are designed for non-trivial application, so an application that managed to use all of Angular's API surface would need to download several hundred kb of framework code, rather than merely 29kb. ##### Update performance -A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare an [Angular implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/angular/index.html) and a [Mithril implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Both implementations are naive (i.e. no optimizations). Sample results are shown below: +A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare an [Angular implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/angular/index.html) and a [Mithril.js implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Both implementations are naive (i.e. no optimizations). Sample results are shown below: -Angular | Mithril +Angular | Mithril.js ------- | ------- 11.5 ms | 6.4 ms #### Complexity -Angular is superior to Mithril in the amount of tools it offers (in the form of various directives and services), but it is also far more complex. Compare [Angular's API surface](https://angular.io/docs/ts/latest/api/) with [Mithril's](api.md). You can make your own judgment on which API is more self-descriptive and more relevant to your needs. +Angular is superior to Mithril.js in the amount of tools it offers (in the form of various directives and services), but it is also far more complex. Compare [Angular's API surface](https://angular.io/docs/ts/latest/api/) with [Mithril.js'](api.md). You can make your own judgment on which API is more self-descriptive and more relevant to your needs. Angular 2 has a lot more concepts to understand: on the language level, Typescript is the recommended language, and on top of that there's also Angular-specific template syntax such as bindings, pipes, "safe navigator operator". You also need to learn about architectural concepts such as modules, components, services, directives, etc, and where it's appropriate to use what. #### Learning curve -If we compare apples to apples, Angular 2 and Mithril have similar learning curves: in both, components are a central aspect of architecture, and both have reasonable routing and XHR tools. +If we compare apples to apples, Angular 2 and Mithril.js have similar learning curves: in both, components are a central aspect of architecture, and both have reasonable routing and XHR tools. -With that being said, Angular has a lot more concepts to learn than Mithril. It offers Angular-specific APIs for many things that often can be trivially implemented (e.g. pluralization is essentially a switch statement, "required" validation is simply an equality check, etc). Angular templates also have several layers of abstractions to emulate what JavaScript does natively in Mithril - Angular's `ng-if`/`ngIf` is a *directive*, which uses a custom *parser* and *compiler* to evaluate an expression string and emulate lexical scoping... and so on. Mithril tends to be a lot more transparent, and therefore easier to reason about. +With that being said, Angular has a lot more concepts to learn than Mithril. It offers Angular-specific APIs for many things that often can be trivially implemented (e.g. pluralization is essentially a switch statement, "required" validation is simply an equality check, etc). Angular templates also have several layers of abstractions to emulate what JavaScript does natively in Mithril.js - Angular's `ng-if`/`ngIf` is a *directive*, which uses a custom *parser* and *compiler* to evaluate an expression string and emulate lexical scoping... and so on. Mithril.js tends to be a lot more transparent, and therefore easier to reason about. #### Documentation Angular 2 documentation provides an extensive introductory tutorial, and another tutorial that implements an application. It also has various guides for advanced concepts, a cheatsheet and a style guide. Unfortunately, at the moment, the API reference leaves much to be desired. Several APIs are either undocumented or provide no context for what the API might be used for. -Mithril documentation includes [introductory](index.md) [tutorials](simple-application.md), pages about advanced concepts, and an extensive API reference section, which includes input/output type information, examples for various common use cases and advice against misuse and anti-patterns. It also includes a cheatsheet for quick reference. +Mithril.js documentation includes [introductory](index.md) [tutorials](simple-application.md), pages about advanced concepts, and an extensive API reference section, which includes input/output type information, examples for various common use cases and advice against misuse and anti-patterns. It also includes a cheatsheet for quick reference. -Mithril documentation also demonstrates simple, close-to-the-metal solutions to common use cases in real-life applications where it's appropriate to inform a developer that web standards may be now on par with larger established libraries. +Mithril.js documentation also demonstrates simple, close-to-the-metal solutions to common use cases in real-life applications where it's appropriate to inform a developer that web standards may be now on par with larger established libraries. --- @@ -172,20 +176,20 @@ Mithril documentation also demonstrates simple, close-to-the-metal solutions to Vue is a view library similar to Angular. -Vue and Mithril have a lot of differences but they also share some similarities: +Vue and Mithril.js have a lot of differences but they also share some similarities: - They both use virtual DOM and lifecycle methods - Both organize views via components -Vue 2 uses a fork of Snabbdom as its virtual DOM system. In addition, Vue also provides tools for routing and state management as separate modules. Vue looks very similar to Angular and provides a similar directive system, HTML-based templates and logic flow directives. It differs from Angular in that it implements a monkeypatching reactive system that overwrites native methods in a component's data tree (whereas Angular 1 uses dirty checking and digest/apply cycles to achieve similar results). Similar to Angular 2, Vue compiles HTML templates into functions, but the compiled functions look more like Mithril or React views, rather than Angular's compiled rendering functions. +Vue 2 uses a fork of Snabbdom as its virtual DOM system. In addition, Vue also provides tools for routing and state management as separate modules. Vue looks very similar to Angular and provides a similar directive system, HTML-based templates and logic flow directives. It differs from Angular in that it implements a monkeypatching reactive system that overwrites native methods in a component's data tree (whereas Angular 1 uses dirty checking and digest/apply cycles to achieve similar results). Similar to Angular 2, Vue compiles HTML templates into functions, but the compiled functions look more like Mithril.js or React views, rather than Angular's compiled rendering functions. -Vue is significantly smaller than Angular when comparing apples to apples, but not as small as Mithril (Vue core is around 23kb gzipped, whereas the equivalent rendering module in Mithril is around 4kb gzipped). Both have similar performance characteristics, but benchmarks usually suggest Mithril is slightly faster. +Vue is significantly smaller than Angular when comparing apples to apples, but not as small as Mithril.js (Vue core is around 23kb gzipped, whereas the equivalent rendering module in Mithril.js is around 4kb gzipped). Both have similar performance characteristics, but benchmarks usually suggest Mithril.js is slightly faster. #### Performance Here's a comparison of library load times, i.e. the time it takes to parse and run the JavaScript code for each framework, by adding a `console.time()` call on the first line and a `console.timeEnd()` call on the last of a script that is composed solely of framework code. For your reading convenience, here are best-of-20 results with logging code manually added to bundled scripts, running from the filesystem, in Chrome on a modest 2010 PC desktop: -Vue | Mithril +Vue | Mithril.js ------- | ------- 21.8 ms | 4.5 ms @@ -193,9 +197,9 @@ Library load times matter in applications that don't stay open for long periods ##### Update performance -A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare a [Vue implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/vue/index.html) and a [Mithril implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Both implementations are naive (i.e. no optimizations). Sample results are shown below: +A useful tool to benchmark update performance is a tool developed by the Ember team called DbMonster. It updates a table as fast as it can and measures frames per second (FPS) and JavaScript times (min, max and mean). The FPS count can be difficult to evaluate since it also includes browser repaint times and `setTimeout` clamping delay, so the most meaningful number to look at is the mean render time. You can compare a [Vue implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/vue/index.html) and a [Mithril.js implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Both implementations are naive (i.e. no optimizations). Sample results are shown below: -Vue | Mithril +Vue | Mithril.js ------ | ------- 9.8 ms | 6.4 ms @@ -203,14 +207,14 @@ Vue | Mithril Vue is heavily inspired by Angular and has many things that Angular does (e.g. directives, filters, bi-directional bindings, `v-cloak`), but also has things inspired by React (e.g. components). As of Vue 2.0, it's also possible to write templates using hyperscript/JSX syntax (in addition to single-file components and the various webpack-based language transpilation plugins). Vue provides both bi-directional data binding and an optional Redux-like state management library, but unlike Angular, it provides no style guide. The many-ways-of-doing-one-thing approach can cause architectural fragmentation in long-lived projects. -Mithril has far less concepts and typically organizes applications in terms of components and a data layer. All component creation styles in Mithril output the same vnode structure using native JavaScript features only. The direct consequence of leaning on the language is less tooling and a simpler project setup. +Mithril.js has far less concepts and typically organizes applications in terms of components and a data layer. All component creation styles in Mithril.js output the same vnode structure using native JavaScript features only. The direct consequence of leaning on the language is less tooling and a simpler project setup. #### Documentation -Both Vue and Mithril have good documentation. Both include a good API reference with examples, tutorials for getting started, as well as pages covering various advanced concepts. +Both Vue and Mithril.js have good documentation. Both include a good API reference with examples, tutorials for getting started, as well as pages covering various advanced concepts. However, due to Vue's many-ways-to-do-one-thing approach, some things may not be adequately documented. For example, there's no documentation on hyperscript syntax or usage. -Mithril documentation typically errs on the side of being overly thorough if a topic involves things outside of the scope of Mithril. For example, when a topic involves a 3rd party library, Mithril documentation walks through the installation process for the 3rd party library. Mithril documentation also often demonstrates simple, close-to-the-metal solutions to common use cases in real-life applications where it's appropriate to inform a developer that web standards may be now on par with larger established libraries. +Mithril.js documentation typically errs on the side of being overly thorough if a topic involves things outside of the scope of Mithril. For example, when a topic involves a 3rd party library, Mithril.js documentation walks through the installation process for the 3rd party library. Mithril.js documentation also often demonstrates simple, close-to-the-metal solutions to common use cases in real-life applications where it's appropriate to inform a developer that web standards may be now on par with larger established libraries. -Mithril's tutorials also cover a lot more ground than Vue's: the [Vue tutorial](https://vuejs.org/v2/guide/#Getting-Started) finishes with a static list of foodstuff. [Mithril's 10 minute guide](index.md) covers the majority of its API and goes over key aspects of real-life applications, such as fetching data from a server and routing (and there's a [longer, more thorough tutorial](simple-application.md) if that's not enough). +Mithril.js' tutorials also cover a lot more ground than Vue's: the [Vue tutorial](https://vuejs.org/v2/guide/#Getting-Started) finishes with a static list of foodstuff. [Mithril.js' 10 minute guide](index.md) covers the majority of its API and goes over key aspects of real-life applications, such as fetching data from a server and routing (and there's a [longer, more thorough tutorial](simple-application.md) if that's not enough). diff --git a/docs/hyperscript.md b/docs/hyperscript.md index 29717041f..39704d2ee 100644 --- a/docs/hyperscript.md +++ b/docs/hyperscript.md @@ -1,3 +1,7 @@ + + # m(selector, attributes, children) - [Description](#description) @@ -22,7 +26,7 @@ ### Description -Represents an HTML element in a Mithril view +Represents an HTML element in a Mithril.js view ```javascript m("div.foo", {style: {color: "red"}}, "hello") @@ -55,7 +59,7 @@ Argument | Type | Required | Descripti ### How it works -Mithril provides a hyperscript function `m()`, which allows expressing any HTML structure using JavaScript syntax. It accepts a `selector` string (required), an `attrs` object (optional) and a `children` array (optional). +Mithril.js provides a hyperscript function `m()`, which allows expressing any HTML structure using JavaScript syntax. It accepts a `selector` string (required), an `attrs` object (optional) and a `children` array (optional). ```javascript m("div", {id: "box"}, "hello") @@ -126,7 +130,7 @@ m("a#exit.external[href='https://example.com']", "Leave") // Leave ``` -If you omit the tag name, Mithril assumes a `div` tag. +If you omit the tag name, Mithril.js assumes a `div` tag. ```javascript m(".box.box-bordered") //
@@ -167,9 +171,9 @@ If another attribute is present in both the first and the second argument, the s ### DOM attributes -Mithril uses both the JavaScript API and the DOM API (`setAttribute`) to resolve attributes. This means you can use both syntaxes to refer to attributes. +Mithril.js uses both the JavaScript API and the DOM API (`setAttribute`) to resolve attributes. This means you can use both syntaxes to refer to attributes. -For example, in the JavaScript API, the `readonly` attribute is called `element.readOnly` (notice the uppercase). In Mithril, all of the following are supported: +For example, in the JavaScript API, the `readonly` attribute is called `element.readOnly` (notice the uppercase). In Mithril.js, all of the following are supported: ```javascript m("input", {readonly: true}) // lowercase @@ -178,7 +182,7 @@ m("input[readonly]") m("input[readOnly]") ``` -This even includes custom elements. For example, you can use [A-Frame](https://aframe.io/docs/0.8.0/introduction/) within Mithril, no problem! +This even includes custom elements. For example, you can use [A-Frame](https://aframe.io/docs/0.8.0/introduction/) within Mithril.js, no problem! ```javascript m("a-scene", [ @@ -252,7 +256,7 @@ Do note that all the properties with magic semantics, like lifecycle attributes, ### Style attribute -Mithril supports both strings and objects as valid `style` values. In other words, all of the following are supported: +Mithril.js supports both strings and objects as valid `style` values. In other words, all of the following are supported: ```javascript m("div", {style: "background:red;"}) @@ -264,13 +268,13 @@ Using a string as a `style` would overwrite all inline styles in the element if You can use both hyphenated CSS property names (like `background-color`) and camel cased DOM `style` property names (like `backgroundColor`). You can also define [CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables), if your browser supports them. -Mithril does not attempt to add units to number values. It simply stringifies them. +Mithril.js does not attempt to add units to number values. It simply stringifies them. --- ### Events -Mithril supports event handler binding for all DOM events, including events whose specs do not define an `on${event}` property, such as `touchstart` +Mithril.js supports event handler binding for all DOM events, including events whose specs do not define an `on${event}` property, such as `touchstart` ```javascript function doSomething(e) { @@ -280,7 +284,7 @@ function doSomething(e) { m("div", {onclick: doSomething}) ``` -Mithril accepts functions and [EventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventListener) objects. So this will also work: +Mithril.js accepts functions and [EventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventListener) objects. So this will also work: ```javascript var clickListener = { @@ -292,7 +296,7 @@ var clickListener = { m("div", {onclick: clickListener}) ``` -By default, when an event attached with hyperscript fires, this will trigger Mithril's auto-redraw after your event callback returns (assuming you are using `m.mount` or `m.route` instead of `m.render` directly). You can disable auto-redraw specifically for a single event by setting `e.redraw = false` on it: +By default, when an event attached with hyperscript fires, this will trigger Mithril.js' auto-redraw after your event callback returns (assuming you are using `m.mount` or `m.route` instead of `m.render` directly). You can disable auto-redraw specifically for a single event by setting `e.redraw = false` on it: ```javascript m("div", { @@ -307,7 +311,7 @@ m("div", { ### Properties -Mithril supports DOM functionality that is accessible via properties such as ``'s `selectedIndex` and `value` properties. ```javascript m("select", {selectedIndex: 0}, [ @@ -345,7 +349,7 @@ To learn more about components, [see the components page](components.md). ### Lifecycle methods -Vnodes and components can have lifecycle methods (also known as *hooks*), which are called at various points during the lifetime of a DOM element. The lifecycle methods supported by Mithril are: `oninit`, `oncreate`, `onupdate`, `onbeforeremove`, `onremove`, and `onbeforeupdate`. +Vnodes and components can have lifecycle methods (also known as *hooks*), which are called at various points during the lifetime of a DOM element. The lifecycle methods supported by Mithril.js are: `oninit`, `oncreate`, `onupdate`, `onbeforeremove`, `onremove`, and `onbeforeupdate`. Lifecycle methods are defined in the same way as DOM event handlers, but receive the vnode as an argument, instead of an Event object: @@ -362,7 +366,7 @@ Hook | Description `oninit(vnode)` | Runs before a vnode is rendered into a real DOM element `oncreate(vnode)` | Runs after a vnode is appended to the DOM `onupdate(vnode)` | Runs every time a redraw occurs while the DOM element is attached to the document -`onbeforeremove(vnode)` | Runs before a DOM element is removed from the document. If a Promise is returned, Mithril only detaches the DOM element after the promise completes. This method is only triggered on the element that is detached from its parent DOM element, but not on its child elements. +`onbeforeremove(vnode)` | Runs before a DOM element is removed from the document. If a Promise is returned, Mithril.js only detaches the DOM element after the promise completes. This method is only triggered on the element that is detached from its parent DOM element, but not on its child elements. `onremove(vnode)` | Runs before a DOM element is removed from the document. If a `onbeforeremove` hook is defined, `onremove` is called after `done` is called. This method is triggered on the element that is detached from its parent element, and all of its children `onbeforeupdate(vnode, old)` | Runs before `onupdate` and if it returns `false`, it prevents a diff for the element and all of its children @@ -399,7 +403,7 @@ To learn more about keys, [see the keys page](keys.md). ### SVG and MathML -Mithril fully supports SVG. Xlink is also supported, but unlike in pre-v1.0 versions of Mithril, must have the namespace explicitly defined: +Mithril.js fully supports SVG. Xlink is also supported, but unlike in pre-v1.0 versions of Mithril.js, must have the namespace explicitly defined: ```javascript m("svg", [ @@ -460,7 +464,7 @@ You cannot use JavaScript statements such as `if` or `for` within JavaScript exp ### Converting HTML -In Mithril, well-formed HTML is valid JSX. Little effort other than copy-pasting is required to integrate an independently produced HTML file into a project using JSX. +In Mithril.js, well-formed HTML is valid JSX. Little effort other than copy-pasting is required to integrate an independently produced HTML file into a project using JSX. When using hyperscript, it's necessary to convert HTML to hyperscript syntax before the code can be run. To facilitate this, you can [use the HTML-to-Mithril-template converter](https://arthurclemens.github.io/mithril-template-converter/index.html). @@ -468,7 +472,7 @@ When using hyperscript, it's necessary to convert HTML to hyperscript syntax bef ### Avoid Anti-patterns -Although Mithril is flexible, some code patterns are discouraged: +Although Mithril.js is flexible, some code patterns are discouraged: #### Avoid dynamic selectors @@ -551,6 +555,6 @@ 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. +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.js 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). diff --git a/docs/index.md b/docs/index.md index a956bc1e3..3460a90d6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,10 @@ + + # Introduction -- [What is Mithril?](#what-is-mithril?) +- [What is Mithril.js?](#what-is-mithriljs?) - [Getting started](#getting-started) - [Hello world](#hello-world) - [DOM elements](#dom-elements) @@ -10,15 +14,15 @@ --- -### What is Mithril? +### What is Mithril.js? -Mithril is a modern client-side JavaScript framework for building Single Page Applications. +Mithril.js is a modern client-side JavaScript framework for building Single Page Applications. It's small (< 10kb gzip), fast and provides routing and XHR utilities out of the box.
Download size
- Mithril (9.5kb) + Mithril.js (9.5kb)
Vue + Vue-Router + Vuex + fetch (40kb)
@@ -29,7 +33,7 @@ It's small (< 10kb gzip), fast and provides routing and XHR utilities out of the
Performance
- Mithril (6.4ms) + Mithril.js (6.4ms)
Vue (9.8ms)
@@ -40,11 +44,11 @@ It's small (< 10kb gzip), fast and provides routing and XHR utilities out of the
-Mithril is used by companies like Vimeo and Nike, and open source platforms like Lichess. +Mithril.js is used by companies like Vimeo and Nike, and open source platforms like Lichess. -If you are an experienced developer and want to know how Mithril compares to other frameworks, see the [framework comparison](framework-comparison.md) page. +If you are an experienced developer and want to know how Mithril.js compares to other frameworks, see the [framework comparison](framework-comparison.md) page. -Mithril supports IE11, Firefox ESR, and the last two versions of Firefox, Edge, Safari, and Chrome. No polyfills required. +Mithril.js supports IE11, Firefox ESR, and the last two versions of Firefox, Edge, Safari, and Chrome. No polyfills required. *Looking for the v1 docs? [Click here](https://mithril.js.org/archive/v1.1.7/index.html).* @@ -52,7 +56,7 @@ Mithril supports IE11, Firefox ESR, and the last two versions of Firefox, Edge, ### Getting started -An easy way to try out Mithril is to include it from a CDN and follow this tutorial. It'll cover the majority of the API surface (including routing and XHR) but it'll only take 10 minutes. +An easy way to try out Mithril.js is to include it from a CDN and follow this tutorial. It'll cover the majority of the API surface (including routing and XHR) but it'll only take 10 minutes. Let's create an HTML file to follow along: @@ -67,7 +71,7 @@ Let's create an HTML file to follow along: ``` -To make things simpler you can try out mithril right here. This is a live playground with Mithril preloaded that - by the way - is also built in Mithril. +To make things simpler you can try out Mithril.js right here. This is a live playground with Mithril.js preloaded that - by the way - is also built in Mithril. ```js var root = document.body @@ -100,7 +104,7 @@ Now, let's change the text to something else. Add this line of code under the pr m.render(root, "My first app") ``` -As you can see, you use the same code to both create and update HTML. Mithril automatically figures out the most efficient way of updating the text, rather than blindly recreating it from scratch. +As you can see, you use the same code to both create and update HTML. Mithril.js automatically figures out the most efficient way of updating the text, rather than blindly recreating it from scratch. #### Live Example @@ -171,7 +175,7 @@ Note: If you prefer `` syntax, [it's possible to use it via a Babel plugin ### Components -A Mithril component is just an object with a `view` function. Here's the code above as a component: +A Mithril.js component is just an object with a `view` function. Here's the code above as a component: ```javascript var Hello = { @@ -199,7 +203,7 @@ As you would expect, doing so creates this markup: ``` -The `m.mount` function is similar to `m.render`, but instead of rendering some HTML only once, it activates Mithril's auto-redrawing system. To understand what that means, let's add some events: +The `m.mount` function is similar to `m.render`, but instead of rendering some HTML only once, it activates Mithril.js' auto-redrawing system. To understand what that means, let's add some events: ```javascript var count = 0 // added a variable @@ -219,9 +223,9 @@ m.mount(root, Hello) We defined an `onclick` event on the button, which increments a variable `count` (which was declared at the top). We are now also rendering the value of that variable in the button label. -You can now update the label of the button by clicking the button. Since we used `m.mount`, you don't need to manually call `m.render` to apply the changes in the `count` variable to the HTML; Mithril does it for you. +You can now update the label of the button by clicking the button. Since we used `m.mount`, you don't need to manually call `m.render` to apply the changes in the `count` variable to the HTML; Mithril.js does it for you. -If you're wondering about performance, it turns out Mithril is very fast at rendering updates, because it only touches the parts of the DOM it absolutely needs to. So in our example above, when you click the button, the text in it is the only part of the DOM Mithril actually updates. +If you're wondering about performance, it turns out Mithril.js is very fast at rendering updates, because it only touches the parts of the DOM it absolutely needs to. So in our example above, when you click the button, the text in it is the only part of the DOM Mithril.js actually updates. #### Live Example @@ -272,9 +276,9 @@ m.route(root, "/splash", { }) ``` -The `m.route` function still has the same auto-redrawing functionality that `m.mount` does, and it also enables URL awareness; in other words, it lets Mithril know what to do when it sees a `#!` in the URL. +The `m.route` function still has the same auto-redrawing functionality that `m.mount` does, and it also enables URL awareness; in other words, it lets Mithril.js know what to do when it sees a `#!` in the URL. -The `"/splash"` right after `root` means that's the default route, i.e. if the hashbang in the URL doesn't point to one of the defined routes (`/splash` and `/hello`, in our case), then Mithril redirects to the default route. So if you open the page in a browser and your URL is `https://localhost`, then you get redirected to `https://localhost/#!/splash`. +The `"/splash"` right after `root` means that's the default route, i.e. if the hashbang in the URL doesn't point to one of the defined routes (`/splash` and `/hello`, in our case), then Mithril.js redirects to the default route. So if you open the page in a browser and your URL is `https://localhost`, then you get redirected to `https://localhost/#!/splash`. Also, as you would expect, clicking on the link on the splash page takes you to the click counter screen we created earlier. Notice that now your URL will point to `https://localhost/#!/hello`. You can navigate back and forth to the splash page using the browser's back and next button. @@ -391,4 +395,4 @@ m.mount(root, Hello) We covered how to create and update HTML, how to create components, routes for a Single Page Application, and interacted with a server via XHR. -This should be enough to get you started writing the frontend for a real application. Now that you are comfortable with the basics of the Mithril API, [be sure to check out the simple application tutorial](simple-application.md), which walks you through building a realistic application. +This should be enough to get you started writing the frontend for a real application. Now that you are comfortable with the basics of the Mithril.js API, [be sure to check out the simple application tutorial](simple-application.md), which walks you through building a realistic application. diff --git a/docs/installation.md b/docs/installation.md index 344ffb147..53861917b 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,3 +1,7 @@ + + # Installation - [CDN](#cdn) @@ -6,7 +10,7 @@ ### CDN -If you're new to JavaScript or just want a very simple setup to get your feet wet, you can get Mithril from a [CDN](https://en.wikipedia.org/wiki/Content_delivery_network): +If you're new to JavaScript or just want a very simple setup to get your feet wet, you can get Mithril.js from a [CDN](https://en.wikipedia.org/wiki/Content_delivery_network): ```html @@ -28,7 +32,7 @@ $ npm install @types/mithril --save-dev For example usage, to file issues or to discuss TypeScript related topics visit: https://github.com/MithrilJS/mithril.d.ts -Type definitions for pre-release versions of Mithril (on the `next` branch) align with the `next` branch of the [types development repo](https://github.com/MithrilJS/mithril.d.ts/tree/next). You can install these types with: +Type definitions for pre-release versions of Mithril.js (on the `next` branch) align with the `next` branch of the [types development repo](https://github.com/MithrilJS/mithril.d.ts/tree/next). You can install these types with: ```bash $ npm install -D MithrilJS/mithril.d.ts#next @@ -80,7 +84,7 @@ $ npm start 7. open `index.html` in a browser -Optionally, you can include Mithril as a global variable using Webpack's provide plugin, to avoid including `import m from "mithril"` across a large number of files: +Optionally, you can include Mithril.js as a global variable using Webpack's provide plugin, to avoid including `import m from "mithril"` across a large number of files: ```js plugins: [ new webpack.ProvidePlugin({m: "mithril"}), @@ -91,18 +95,18 @@ Then, you could remove the import line from step 4 (don't forget to restart Webp #### Step by step -For production-level projects, the recommended way of installing Mithril is to use npm. +For production-level projects, the recommended way of installing Mithril.js is to use npm. npm is the default package manager that is bundled w/ Node.js. It is widely used as the package manager for both client-side and server-side libraries in the JavaScript ecosystem. Download and install [Node](https://nodejs.org); npm is bundled with that and installed alongside it. -To use Mithril via npm, go to your project folder, and run `npm init --yes` from the command line. This will create a file called `package.json`. +To use Mithril.js via npm, go to your project folder, and run `npm init --yes` from the command line. This will create a file called `package.json`. ```bash npm init --yes # creates a file called package.json ``` -Then, to install Mithril, run: +Then, to install Mithril.js, run: ```bash npm install mithril --save @@ -148,7 +152,7 @@ The `-d` flag tells webpack to use development mode, which produces source maps The `--watch` flag tells webpack to watch the file system and automatically recreate `app.js` if file changes are detected. -Now you can run the script via `npm start` in your command line window. This looks up the `webpack` command in the npm path, reads `index.js` and creates a file called `app.js` which includes both Mithril and the `hello world` code above. If you want to run the `webpack` command directly from the command line, you need to either add `node_modules/.bin` to your PATH, or install webpack globally via `npm install webpack -g`. It's, however, recommended that you always install webpack locally and use npm scripts, to ensure builds are reproducible in different computers. +Now you can run the script via `npm start` in your command line window. This looks up the `webpack` command in the npm path, reads `index.js` and creates a file called `app.js` which includes both Mithril.js and the `hello world` code above. If you want to run the `webpack` command directly from the command line, you need to either add `node_modules/.bin` to your PATH, or install webpack globally via `npm install webpack -g`. It's, however, recommended that you always install webpack locally and use npm scripts, to ensure builds are reproducible in different computers. ``` npm start @@ -189,7 +193,7 @@ var MyComponent = require("./mycomponent") m.mount(document.body, MyComponent) ``` -Note that in this example, we're using `m.mount`, which wires up the component to Mithril's autoredraw system. In most applications, you will want to use `m.mount` (or `m.route` if your application has multiple screens) instead of `m.render` to take advantage of the autoredraw system, rather than re-rendering manually every time a change occurs. +Note that in this example, we're using `m.mount`, which wires up the component to Mithril.js' autoredraw system. In most applications, you will want to use `m.mount` (or `m.route` if your application has multiple screens) instead of `m.render` to take advantage of the autoredraw system, rather than re-rendering manually every time a change occurs. #### Production build @@ -220,7 +224,7 @@ You can use hooks in your production environment to run the production build scr --- -### Alternate ways to use Mithril +### Alternate ways to use Mithril.js #### Live reload development environment @@ -263,6 +267,6 @@ If you don't have the ability to run a bundler script due to company security po ```javascript // index.js -// if a CommonJS environment is not detected, Mithril will be created in the global scope +// if a CommonJS environment is not detected, Mithril.js will be created in the global scope m.render(document.body, "hello world") ``` diff --git a/docs/integrating-libs.md b/docs/integrating-libs.md index 904f11276..a4d7fd63b 100644 --- a/docs/integrating-libs.md +++ b/docs/integrating-libs.md @@ -1,3 +1,7 @@ + + # 3rd Party Integration Integration with third party libraries or vanilla JavaScript code can be achieved via [lifecycle methods](lifecycle-methods.md). @@ -109,10 +113,10 @@ function Demo() { }), m('button', { onclick: prev - }, 'Mithril Button -'), + }, 'Mithril.js Button -'), m('button', { onclick: next - }, 'Mithril Button +') + }, 'Mithril.js Button +') ] } } diff --git a/docs/jsonp.md b/docs/jsonp.md index a05e51237..b46c48ddd 100644 --- a/docs/jsonp.md +++ b/docs/jsonp.md @@ -1,3 +1,7 @@ + + # jsonp(options) - [Description](#description) diff --git a/docs/jsx.md b/docs/jsx.md index 5f452cb8d..940e51f17 100644 --- a/docs/jsx.md +++ b/docs/jsx.md @@ -1,3 +1,7 @@ + + # JSX - [Description](#description) @@ -381,6 +385,6 @@ m("main", [ ### Converting HTML -In Mithril, well-formed HTML is generally valid JSX. Little more than just pasting raw HTML is required for things to just work. About the only things you'd normally have to do are change unquoted property values like `attr=value` to `attr="value"` and change void elements like `` to ``, this being due to JSX being based on XML and not HTML. +In Mithril.js, well-formed HTML is generally valid JSX. Little more than just pasting raw HTML is required for things to just work. About the only things you'd normally have to do are change unquoted property values like `attr=value` to `attr="value"` and change void elements like `` to ``, this being due to JSX being based on XML and not HTML. When using hyperscript, you often need to translate HTML to hyperscript syntax to use it. To help speed up this process along, you can use a [community-created HTML-to-Mithril-template converter](https://arthurclemens.github.io/mithril-template-converter/index.html) to do much of it for you. diff --git a/docs/keys.md b/docs/keys.md index f6f82d14f..6ac6b7e89 100644 --- a/docs/keys.md +++ b/docs/keys.md @@ -1,3 +1,7 @@ + + # Keys - [What are keys?](#what-are-keys?) @@ -37,7 +41,7 @@ What this translates to is stuff like `[m(".foo", {key: 1}), null]` and `["foo", ### Linking model data in lists of views -When you're rendering lists, especially editable list, you're often dealing with things like editable TODOs and such. These have state and identities, and you have to give Mithril the information it needs to track them. +When you're rendering lists, especially editable list, you're often dealing with things like editable TODOs and such. These have state and identities, and you have to give Mithril.js the information it needs to track them. Suppose we have a simple social media post listing, where you can comment on posts and where you can hide posts for reasons like reporting them. @@ -166,16 +170,16 @@ m(".comment-list", comments.map(function(comment) { })) ``` -Each of these refers to a subtree with associated state Mithril has no idea about. (Mithril only knows about vnodes, nothing else.) When you leave those unkeyed, things can and will get weird and unexpected. In this case, try clicking on the "N comments" to show the comments, typing into the comment compose box at the bottom of it, then clicking "I don't like this" on a post above it. [Here's a live demo for you to try it out on, complete with a mock model. (Note: if you're on Edge or IE, you may run into issues due to the link's hash length.)](https://flems.io/#0=N4Igxg9gdgzhA2BTEAucD4EMAONEBMQAaEGMAJw1QG0AGIgZgCYB2AXRIDMBLJGG0FEwBbZGgB0ACwAuw+MXRRpiJahAgAvkUEixIYRHyJ44gFb8SkJSulqAOiACueAAQxp5bmGkO7UPwBumOQuALKGxi4AvC4AFJyOUN7c0LEAlC7AfnbSQSFgmGCSiNEuAPIARqaI3uIUiJjKsVCO8PBp2T7+0glJ0ilQLgVFBLEA1ogAnkQu3FDc0hlZ3TnkiNKO5IPDxdQTk2wuAIQxLW0uAPxDhbv7hyhxO4h7U4cxcwvpHd0a2d29yWgLmEmAmAFU8OR0plOjk8i5nIhyAA5XSlTigpHiObKLbrcSIlG6dKwjzrTbbG6jByElAOFwAagRkNRohmAP6qSWpJyAHpeS4AJJGTBtaZCgDk+Bcpmc0mZJXgEDWwmw3AAHpF8Nw1t54OKKo55QsXPgIIgYFAJfKYI5sNhlV0cs7pPyXAAJAAqnoACgBlcQuP0QWawZSYfAzQUuSQRlzSEMAd08ymBkxcEETgwAjo4vGMXMj1vBuJx05CAl5EDzXQKEy5E8qC8EIIlpSbMFB20l4I4jC5sJRsEiXABhMoAJT9MYaRnIMDpKx8tZj0mkuBQ-IA5gtJI4KnUIMJedwYNxMJJRBHMzBeUqVWrNfAALSQefPwcQdWTGvw7jCLcwXIeBSgcGR1wXfljEQLdO2kZ9k24LcZGfTAABZMAAVkQNDxCgYtS0mQ9jzwgiy15DkBlvNZcwtHwQD+F1VnJLZgXEGjHDo2J-0A4CZmWJjlwqQoxi3Sg2weDxOKIGtmJgB1YEQT1JmHB4HAqJUKgcGSlxyDQ0nEaRiigeJEkBEyNIgCpuV05c1g2ViBOXQTuHwB5HDzfBxACND0h0wSXUJVlEHclldH85zIsJH0vAckKXDBCcABk6jWRpEEqapvESpLYks6yIsE35bP00lSp+RiejMzlBhBcFIRgWJIESRYYSXeFCRgUpqDYUlOGVOI-1KWgAG5ZhcAAeIZWyUMbuAZBkMk68RsGcSRYjqxAISRL5SXsikXB9ShhFPRBxFFeBYk6749Mq+EwE2NYlG28gZgddwYEqyigU4dYihe6EnOYhzKRGfBYhpSFtJcb6TJswT9tYzaAYMoyVFMvoBiuyF4cinIHvIJ7pBe0pCRrcqXQp6Ris6WGYb+yQfQgD7Ab2ljQeKcGHHe6RPuIGHqqx3GXUR2rMRexqMQmch2M7M1hDwxxhAqHbgBOqAHgARhmEF1QeJhaH0m68ekQzjIx8zYhLdxhZNnmupia3TZBbALZq7GkVtgKyRBtrvZctyEU87zfLSQqTcCyFQqRcP-bXRB1WkB4paxe9EAV5RE782STcgYRRCUUcZqTmHMRl8g5aPRXldV9WHnoYFMD1lwGENsOc8pjvqeNoqe+7zoae6e6jwL3nSkympTfqdLmladovsFn6GaL-ObEannbdF64wYhkAebpEBGQHZnTdc9lF7hv3Iq35GGtiHniNHouWtR83Yat09WqByK89HmBqAfq5N4LgnbiBdm7LGhIvbX3ZlfOOrl3LBx8tnWyglaQKlel3HImcS4pxlmnDOCdpAoLjjTE2VNKZ9ypoPHIdMVQQACIgAAQpMYUH93AzAWOnW2w8WrDT6gNWIQ0YijXGlNUBSAoBbiMnNBa0DSxxCdtQbgbBsTShOO8ZQCtXIZCUc1JQC1gFKJUWVUkEiVDSMkKUfRTo7KwPwomQ6x1ToQNSGsOA8BGFLDcOsT0-5ECtmIe4hAjCZiYVoLQDI1DKpb2-r9aQ-0o70wSZIF64d4lFCZh9ZODMsm83ScvEea8ckpJXn-WOS4tzrBesnC+rNbJbwJkTF6ZUKkuiqdIPJC4BaYy5HAuxvt7bHFOHPS4x8PriBgCWMAiBoQPDOPAVplV8ZpWUHk2pvSTI4OgVvDJqTITpDNujd+0Ccj2xWmtWI38A6INciHEhEcNhJKaTYNJXccGSSIbHH+RTC7F3rt8vSfcgVLP8EuSQrlEDrJ6ZbDe-TgYHXoYwlhbD7ZvRPlQ-ylT1hlOKTC92cLrk+wOr-NeACT5qMOCcFwCyxmkqUP-QB+BVFTKrHMmlc9QWkgjPgXFSgNmwpPjMbZ8LiWsSOkeU6502ixGoDnPZKNAU9EKavBl98MU1jYK-Y5dTTkJN+bzcl7hKUXJgOtIlLoEFBzucg9uqDlzoJec9SESrsFEM+YnJVFDlzeq0MsoykK+W4LqYSmsW8kXMNYeDelhqmUcHGYsLl1M-D6XSAoPASBzL8DQJrTCKBMIAE5NDaBAEIUQagzAWEUMoVQaBIYlHcJ4bwvgwV0xRlfWJpJKyIETAK92AQoARGgR1SEpQB0RHOmuecBIoYNNgcIXeM6kTaRzguhwS7yAflipsas-M10gB4tDYAMByBgGjjLaK261hGyVfujdz4y27pmA4AAAtDQkG7gp2sElQgeKa-h0xXu9RAAB1OYZpEz1LhMENw+4TprjmFudEoo8CdHhDgzh8x+iindIgJCMhOidvatwHtfasbjqMMOmDPMx2DqMJOjwMAVonzDfOxdedgMrvtfunBwQGhcbjhayK2oYCYA0gQB4toKjwf6FImYQRezxQw1ghMSRVnxXfhRxAeqXJYfPPAXD+H5QxAZImcDmZxAdKA0aAgfppCTCQEIujZ15YGWKEZyZ0zZkN2fEwYFjytPiHlpM+zSApB4eQsZlwDhMBGggC2uOLpAvBfcA5s67nIulFCI0SQYCm7cT0zhiLMh5POaC1XMglA2iGcixkJk3N1QJceXpV1y5oBzFWsGzZsREABB05FHBpReuGWCB07yopOIqZyMN6Qo38SpbCxlmQoEQCxYTE1xLygAgjfIGNhb6XitRey0ZPL6oCsLH0zVkrLgZtzdNpVjAV3Wr1b3o1hi9rKatbOVgGZkgEBzgeIFxojGVo-cQH9+Ac5Wt+vtd+k2d6OPM0QM+KT8Hob7sNGuaAAnHnAGgGAaZYwyN9KEy6VHCxZNIZiFJasH3lxA6nUx8nxDtlHJMic0V3tmeU+Q-AVDdOXQKzWPgCukH-O9xU4NmIvh3ukJh4lkAeSNuRTSP539FUoCD0AwaoDSOoNisGN-btvb8Xkec1RkINGYgM5BzzVjvsF267wGBrsmZ+I52wGDiHAPosgBAymEokxWz5ANeIMPOPBLM+J1soh-Wt7hCMCYHlQb1UcPjLHnO8vIq+r-ZrgDF8xwGv10RpLJGTeafN6K4eqqos2+nTG+3B0EcGoj5FBdaTMiOtD4SfSt72MGufBUQwP5+YxsMrHvvMXH42GfBCowrfBJ457PmaP+u44J+MFIQNReY3apMkLggovxc+qz-7EA0YzRWnlCWCY8YIV8xzmr0xGutcF7yU72ZuMS-07L6vrTFvpoa9aMJ1gd68W850HdYgP8XcIN3d7VPdChwd-skQ1I-cA8XAg9NgE0w9xAF9lwo8wgIgTBp41khVyZ1dbo89W038T4nMh0r54QzVMwg0HZS4+dadoMQ8a8YAAAxBmAgXnfnQjWBI3X-U3VIf-KvajE+YA+jUApjO3WyavP+UoJgxMFgsZDfEwKzA1deDFFweZTlCApvRdO3YgVdWIDvYAdBB+HvOHQSO9HmQfYfaGB+bZSfEAZjdwZ8UQWbPAwXXeTALw+CGNV8YufwyKJfAnFfcQy+UnSKBRWII4NQjQgAMlSOOBjV4P4PwH60EiyL4JSQEOp3IEmwF0ii0PED2RYNT0WDZw2nYkP0wETGPyKim15j+3UN0NKGSM6JYJUw0FP0eQfhjWfiUGfUPgb3MPKJGJ12LmiCiBiE1jGRlwMN9z5la3sPh0COCNn0hQiJdCiMJ1XzyOXEqLnyhVoI3nqIPxF2aNaJPy+3ogv2gGtBAW4FvwDQfzp2Py2LJz6O6KuEcJPjCO4IOP1W4IWI5TaC7iBP7xr2fCdkRIgAjEQ2hgcCShRO1CkRwIcAeIeDlXKObwRKdmhiyLy1dnfl305xNhvigINX4hjQeBjW9W9hvRU0dzmNVCR34n3gTV7y7l6lhzWIWRrAoOTSoL8Dpj4NGFxkqL2S6UOTRn30aLuJaOEN9lENI1iLX3p2kI+iyyIMs3WEVP8zpPXV+gIAiP3UkE1nRJABlPwDxKVSGUWOhJAjhPXScKRKVFRKkXtMxL9K3FxJAGPweGBO8ON3BIXXfyPGAz+Lb1MJBNJP5nOXAXflDXKLpLyV5JPgeB5lZOz1V0fzFOf0oMHgVgMBaliDNAelHnECH3wHFEdLSHTWgizTUE1mYBQCYAADZi0dBy0JAwAYAq0rAa1bA0BGz0xlgXAYZoB4IMQTp9QHgABBTwUUGYUTWAFHJEUsEaf9MFDdNqOckTBAyYZOJAdUA8wYFwUUJCKAZ8LhYQbpGZawcgG8weYIlwY82cxuXbOYZwrHYQeucQbCYQT8v4e9NUbwHdE8-8ncR8zwSLUC8CyCo8wkLdWCtYWYACeCpbEuBgdOG8ucofcgOcZ8CubUZwB4IiiCw8vwYImYYiYDeCsin3TWbAdUNwBAVyFwDSESEi-i5UCiqi7gGilwTWYivwOc7AHlRDLWaS28kEACx8hMbAUClgNChiqAFipHdPROPjTANikSlAmlaARAIS9xbgAAL3ikHXwiEoYSRE4CVBNznyMCgCErM3wCMi1giQAFIhKCLaKlLTzTxzyHhLIwAxghL+olAUdbL4opL6LJTdLEc8AUc4MTQ-ycFUISwtwNYXBkKZB0LGKMqShMdVN4L4r4Izw7LFLUrSLTLN0xKJKmAwrhLyKzKHLLKZL+KRIxIZpA4ABiWgTAFgISyAe8B4EazgeaoSuS-AbErcVC9OSSzqs8rAC80MEsfCQfJUGKsq3Spw3w4yv8razAHa1yhOIS2UdwQiMI6wflNwOSmZQfdYRMRAFQISlSxC58dSzS7S282qxKhqlwWgcQAADiUq-NOvWGMoAD4XAAAqI+NGv8v6wCpATgEuFK463Y0ImxZipwi45i0Ii4ky7q8geZCyxa+SqReuOKhcsG5Kzq0G-bB4BYe8sAKazYOAGm4+HEJEAmpwom-5P7RhTBE6kEi4lASWmOcqgfOWhWkIP86a5UWaqG7W0WkErI+CrGtSiADSiGsC2Gv4F9BCRACoMYBYZ8fYTgCuUQLqTsf8dKQMla+C2gAKzIaaZ6kuDErEtEkAMaYqOcpgTCH24AP2ic1Aj2xDXAkOlwMOlwcJKOmOmwOOoOnExO0O-qrS9O8czO33eOnO3O5Ow8l9B2p2i0O8+YEEZQUupDP87232oul6wOoMhwPO28iOwuhc4uzula8ulOtOtugejukAJu8PJOlOgu8e-2rOoMkMnuuczWQKhe2Oku7OrcbuiutK8QX0z23KohJ8rsYu58AtK+gtLioSva5HEKiGgmo+hSlATAXGkcE+xOM+zykuUafq++2fQ7LmwreAISy6na6K2K-q9ugOqenele-qq2m2u212hugYfWLqe+mDSGzCF2+u92ne0MHgLDPq28tBxoDBlwJgLBuYBoEIXB-Bt2xuohuYEhrhAm0Ip2Gqlm+q+KSGmG1Kr8rhz+H8zCmCuKXC5u-qx+jqoRqCmNeCpalaxqoS9isyzi7ijxPigSo6-q9R1q1EiSyGrSzqvK+8wqh4Eq6QThgfac+C8xgqoqnGmx-qxxh8yTWbcgVxg+imyFA20bQCwG020x+Ro8sW7ovS1wP8gxrWLiniksaUXR6B28gxyiox7pSG3szq5RhSkJ4Gucw2gG42pmnSwmyJiqgyrxhofC4Bludm3hpK0CwR3W7w-WqJyqo0aqv8jmpp02lp-q3Jxm028CjaoRtszNGqbNEALspgFATWTQDgEAPasYbNagQcvQeDSQTweQEgTYeQNAcCDcfkRIbAUSYiXkLZnZl9JgcQSGtCS53cHZsBOYStBQezYcNQSrbgbAWwLQDZtQPBF9XCTWO5hQfZtQI5yCXkRMGy8i7EKAXkdwOWUUCyiiMuYF8QUF2gd5lSPQb535gc0tXQNQDyVyF9BgcQSlhgcF4CSFtcY5mFuFryOYJF2bLsVF-CXkMl-AClqlql3Fz5tAAlv5tgDQIAA) +Each of these refers to a subtree with associated state Mithril.js has no idea about. (Mithril.js only knows about vnodes, nothing else.) When you leave those unkeyed, things can and will get weird and unexpected. In this case, try clicking on the "N comments" to show the comments, typing into the comment compose box at the bottom of it, then clicking "I don't like this" on a post above it. [Here's a live demo for you to try it out on, complete with a mock model. (Note: if you're on Edge or IE, you may run into issues due to the link's hash length.)](https://flems.io/#0=N4Igxg9gdgzhA2BTEAucD4EMAONEBMQAaEGMAJw1QG0AGIgZgCYB2AXRIDMBLJGG0FEwBbZGgB0ACwAuw+MXRRpiJahAgAvkUEixIYRHyJ44gFb8SkJSulqAOiACueAAQxp5bmGkO7UPwBumOQuALKGxi4AvC4AFJyOUN7c0LEAlC7AfnbSQSFgmGCSiNEuAPIARqaI3uIUiJjKsVCO8PBp2T7+0glJ0ilQLgVFBLEA1ogAnkQu3FDc0hlZ3TnkiNKO5IPDxdQTk2wuAIQxLW0uAPxDhbv7hyhxO4h7U4cxcwvpHd0a2d29yWgLmEmAmAFU8OR0plOjk8i5nIhyAA5XSlTigpHiObKLbrcSIlG6dKwjzrTbbG6jByElAOFwAagRkNRohmAP6qSWpJyAHpeS4AJJGTBtaZCgDk+Bcpmc0mZJXgEDWwmw3AAHpF8Nw1t54OKKo55QsXPgIIgYFAJfKYI5sNhlV0cs7pPyXAAJAAqnoACgBlcQuP0QWawZSYfAzQUuSQRlzSEMAd08ymBkxcEETgwAjo4vGMXMj1vBuJx05CAl5EDzXQKEy5E8qC8EIIlpSbMFB20l4I4jC5sJRsEiXABhMoAJT9MYaRnIMDpKx8tZj0mkuBQ-IA5gtJI4KnUIMJedwYNxMJJRBHMzBeUqVWrNfAALSQefPwcQdWTGvw7jCLcwXIeBSgcGR1wXfljEQLdO2kZ9k24LcZGfTAABZMAAVkQNDxCgYtS0mQ9jzwgiy15DkBlvNZcwtHwQD+F1VnJLZgXEGjHDo2J-0A4CZmWJjlwqQoxi3Sg2weDxOKIGtmJgB1YEQT1JmHB4HAqJUKgcGSlxyDQ0nEaRiigeJEkBEyNIgCpuV05c1g2ViBOXQTuHwB5HDzfBxACND0h0wSXUJVlEHclldH85zIsJH0vAckKXDBCcABk6jWRpEEqapvESpLYks6yIsE35bP00lSp+RiejMzlBhBcFIRgWJIESRYYSXeFCRgUpqDYUlOGVOI-1KWgAG5ZhcAAeIZWyUMbuAZBkMk68RsGcSRYjqxAISRL5SXsikXB9ShhFPRBxFFeBYk6749Mq+EwE2NYlG28gZgddwYEqyigU4dYihe6EnOYhzKRGfBYhpSFtJcb6TJswT9tYzaAYMoyVFMvoBiuyF4cinIHvIJ7pBe0pCRrcqXQp6Ris6WGYb+yQfQgD7Ab2ljQeKcGHHe6RPuIGHqqx3GXUR2rMRexqMQmch2M7M1hDwxxhAqHbgBOqAHgARhmEF1QeJhaH0m68ekQzjIx8zYhLdxhZNnmupia3TZBbALZq7GkVtgKyRBtrvZctyEU87zfLSQqTcCyFQqRcP-bXRB1WkB4paxe9EAV5RE782STcgYRRCUUcZqTmHMRl8g5aPRXldV9WHnoYFMD1lwGENsOc8pjvqeNoqe+7zoae6e6jwL3nSkympTfqdLmladovsFn6GaL-ObEannbdF64wYhkAebpEBGQHZnTdc9lF7hv3Iq35GGtiHniNHouWtR83Yat09WqByK89HmBqAfq5N4LgnbiBdm7LGhIvbX3ZlfOOrl3LBx8tnWyglaQKlel3HImcS4pxlmnDOCdpAoLjjTE2VNKZ9ypoPHIdMVQQACIgAAQpMYUH93AzAWOnW2w8WrDT6gNWIQ0YijXGlNUBSAoBbiMnNBa0DSxxCdtQbgbBsTShOO8ZQCtXIZCUc1JQC1gFKJUWVUkEiVDSMkKUfRTo7KwPwomQ6x1ToQNSGsOA8BGFLDcOsT0-5ECtmIe4hAjCZiYVoLQDI1DKpb2-r9aQ-0o70wSZIF64d4lFCZh9ZODMsm83ScvEea8ckpJXn-WOS4tzrBesnC+rNbJbwJkTF6ZUKkuiqdIPJC4BaYy5HAuxvt7bHFOHPS4x8PriBgCWMAiBoQPDOPAVplV8ZpWUHk2pvSTI4OgVvDJqTITpDNujd+0Ccj2xWmtWI38A6INciHEhEcNhJKaTYNJXccGSSIbHH+RTC7F3rt8vSfcgVLP8EuSQrlEDrJ6ZbDe-TgYHXoYwlhbD7ZvRPlQ-ylT1hlOKTC92cLrk+wOr-NeACT5qMOCcFwCyxmkqUP-QB+BVFTKrHMmlc9QWkgjPgXFSgNmwpPjMbZ8LiWsSOkeU6502ixGoDnPZKNAU9EKavBl98MU1jYK-Y5dTTkJN+bzcl7hKUXJgOtIlLoEFBzucg9uqDlzoJec9SESrsFEM+YnJVFDlzeq0MsoykK+W4LqYSmsW8kXMNYeDelhqmUcHGYsLl1M-D6XSAoPASBzL8DQJrTCKBMIAE5NDaBAEIUQagzAWEUMoVQaBIYlHcJ4bwvgwV0xRlfWJpJKyIETAK92AQoARGgR1SEpQB0RHOmuecBIoYNNgcIXeM6kTaRzguhwS7yAflipsas-M10gB4tDYAMByBgGjjLaK261hGyVfujdz4y27pmA4AAAtDQkG7gp2sElQgeKa-h0xXu9RAAB1OYZpEz1LhMENw+4TprjmFudEoo8CdHhDgzh8x+iindIgJCMhOidvatwHtfasbjqMMOmDPMx2DqMJOjwMAVonzDfOxdedgMrvtfunBwQGhcbjhayK2oYCYA0gQB4toKjwf6FImYQRezxQw1ghMSRVnxXfhRxAeqXJYfPPAXD+H5QxAZImcDmZxAdKA0aAgfppCTCQEIujZ15YGWKEZyZ0zZkN2fEwYFjytPiHlpM+zSApB4eQsZlwDhMBGggC2uOLpAvBfcA5s67nIulFCI0SQYCm7cT0zhiLMh5POaC1XMglA2iGcixkJk3N1QJceXpV1y5oBzFWsGzZsREABB05FHBpReuGWCB07yopOIqZyMN6Qo38SpbCxlmQoEQCxYTE1xLygAgjfIGNhb6XitRey0ZPL6oCsLH0zVkrLgZtzdNpVjAV3Wr1b3o1hi9rKatbOVgGZkgEBzgeIFxojGVo-cQH9+Ac5Wt+vtd+k2d6OPM0QM+KT8Hob7sNGuaAAnHnAGgGAaZYwyN9KEy6VHCxZNIZiFJasH3lxA6nUx8nxDtlHJMic0V3tmeU+Q-AVDdOXQKzWPgCukH-O9xU4NmIvh3ukJh4lkAeSNuRTSP539FUoCD0AwaoDSOoNisGN-btvb8Xkec1RkINGYgM5BzzVjvsF267wGBrsmZ+I52wGDiHAPosgBAymEokxWz5ANeIMPOPBLM+J1soh-Wt7hCMCYHlQb1UcPjLHnO8vIq+r-ZrgDF8xwGv10RpLJGTeafN6K4eqqos2+nTG+3B0EcGoj5FBdaTMiOtD4SfSt72MGufBUQwP5+YxsMrHvvMXH42GfBCowrfBJ457PmaP+u44J+MFIQNReY3apMkLggovxc+qz-7EA0YzRWnlCWCY8YIV8xzmr0xGutcF7yU72ZuMS-07L6vrTFvpoa9aMJ1gd68W850HdYgP8XcIN3d7VPdChwd-skQ1I-cA8XAg9NgE0w9xAF9lwo8wgIgTBp41khVyZ1dbo89W038T4nMh0r54QzVMwg0HZS4+dadoMQ8a8YAAAxBmAgXnfnQjWBI3X-U3VIf-KvajE+YA+jUApjO3WyavP+UoJgxMFgsZDfEwKzA1deDFFweZTlCApvRdO3YgVdWIDvYAdBB+HvOHQSO9HmQfYfaGB+bZSfEAZjdwZ8UQWbPAwXXeTALw+CGNV8YufwyKJfAnFfcQy+UnSKBRWII4NQjQgAMlSOOBjV4P4PwH60EiyL4JSQEOp3IEmwF0ii0PED2RYNT0WDZw2nYkP0wETGPyKim15j+3UN0NKGSM6JYJUw0FP0eQfhjWfiUGfUPgb3MPKJGJ12LmiCiBiE1jGRlwMN9z5la3sPh0COCNn0hQiJdCiMJ1XzyOXEqLnyhVoI3nqIPxF2aNaJPy+3ogv2gGtBAW4FvwDQfzp2Py2LJz6O6KuEcJPjCO4IOP1W4IWI5TaC7iBP7xr2fCdkRIgAjEQ2hgcCShRO1CkRwIcAeIeDlXKObwRKdmhiyLy1dnfl305xNhvigINX4hjQeBjW9W9hvRU0dzmNVCR34n3gTV7y7l6lhzWIWRrAoOTSoL8Dpj4NGFxkqL2S6UOTRn30aLuJaOEN9lENI1iLX3p2kI+iyyIMs3WEVP8zpPXV+gIAiP3UkE1nRJABlPwDxKVSGUWOhJAjhPXScKRKVFRKkXtMxL9K3FxJAGPweGBO8ON3BIXXfyPGAz+Lb1MJBNJP5nOXAXflDXKLpLyV5JPgeB5lZOz1V0fzFOf0oMHgVgMBaliDNAelHnECH3wHFEdLSHTWgizTUE1mYBQCYAADZi0dBy0JAwAYAq0rAa1bA0BGz0xlgXAYZoB4IMQTp9QHgABBTwUUGYUTWAFHJEUsEaf9MFDdNqOckTBAyYZOJAdUA8wYFwUUJCKAZ8LhYQbpGZawcgG8weYIlwY82cxuXbOYZwrHYQeucQbCYQT8v4e9NUbwHdE8-8ncR8zwSLUC8CyCo8wkLdWCtYWYACeCpbEuBgdOG8ucofcgOcZ8CubUZwB4IiiCw8vwYImYYiYDeCsin3TWbAdUNwBAVyFwDSESEi-i5UCiqi7gGilwTWYivwOc7AHlRDLWaS28kEACx8hMbAUClgNChiqAFipHdPROPjTANikSlAmlaARAIS9xbgAAL3ikHXwiEoYSRE4CVBNznyMCgCErM3wCMi1giQAFIhKCLaKlLTzTxzyHhLIwAxghL+olAUdbL4opL6LJTdLEc8AUc4MTQ-ycFUISwtwNYXBkKZB0LGKMqShMdVN4L4r4Izw7LFLUrSLTLN0xKJKmAwrhLyKzKHLLKZL+KRIxIZpA4ABiWgTAFgISyAe8B4EazgeaoSuS-AbErcVC9OSSzqs8rAC80MEsfCQfJUGKsq3Spw3w4yv8razAHa1yhOIS2UdwQiMI6wflNwOSmZQfdYRMRAFQISlSxC58dSzS7S282qxKhqlwWgcQAADiUq-NOvWGMoAD4XAAAqI+NGv8v6wCpATgEuFK463Y0ImxZipwi45i0Ii4ky7q8geZCyxa+SqReuOKhcsG5Kzq0G-bB4BYe8sAKazYOAGm4+HEJEAmpwom-5P7RhTBE6kEi4lASWmOcqgfOWhWkIP86a5UWaqG7W0WkErI+CrGtSiADSiGsC2Gv4F9BCRACoMYBYZ8fYTgCuUQLqTsf8dKQMla+C2gAKzIaaZ6kuDErEtEkAMaYqOcpgTCH24AP2ic1Aj2xDXAkOlwMOlwcJKOmOmwOOoOnExO0O-qrS9O8czO33eOnO3O5Ow8l9B2p2i0O8+YEEZQUupDP87232oul6wOoMhwPO28iOwuhc4uzula8ulOtOtugejukAJu8PJOlOgu8e-2rOoMkMnuuczWQKhe2Oku7OrcbuiutK8QX0z23KohJ8rsYu58AtK+gtLioSva5HEKiGgmo+hSlATAXGkcE+xOM+zykuUafq++2fQ7LmwreAISy6na6K2K-q9ugOqenele-qq2m2u212hugYfWLqe+mDSGzCF2+u92ne0MHgLDPq28tBxoDBlwJgLBuYBoEIXB-Bt2xuohuYEhrhAm0Ip2Gqlm+q+KSGmG1Kr8rhz+H8zCmCuKXC5u-qx+jqoRqCmNeCpalaxqoS9isyzi7ijxPigSo6-q9R1q1EiSyGrSzqvK+8wqh4Eq6QThgfac+C8xgqoqnGmx-qxxh8yTWbcgVxg+imyFA20bQCwG020x+Ro8sW7ovS1wP8gxrWLiniksaUXR6B28gxyiox7pSG3szq5RhSkJ4Gucw2gG42pmnSwmyJiqgyrxhofC4Bludm3hpK0CwR3W7w-WqJyqo0aqv8jmpp02lp-q3Jxm028CjaoRtszNGqbNEALspgFATWTQDgEAPasYbNagQcvQeDSQTweQEgTYeQNAcCDcfkRIbAUSYiXkLZnZl9JgcQSGtCS53cHZsBOYStBQezYcNQSrbgbAWwLQDZtQPBF9XCTWO5hQfZtQI5yCXkRMGy8i7EKAXkdwOWUUCyiiMuYF8QUF2gd5lSPQb535gc0tXQNQDyVyF9BgcQSlhgcF4CSFtcY5mFuFryOYJF2bLsVF-CXkMl-AClqlql3Fz5tAAlv5tgDQIAA) -Instead of doing what you would expect, it instead gets really confused and does the wrong thing: it closes the comment list you had open and the post after the one you had the comments open on now just persistently shows "Loading..." even though it thinks it's already loaded the comments. This is because the comments are lazily loaded and they just assume the same comment is passed each time (which sounds relatively sane here), but in this case, it's not. This is because of how Mithril patches unkeyed fragments: it patches them one by one iteratively in a very simple fashion. So in this case, the diff might look like this: +Instead of doing what you would expect, it instead gets really confused and does the wrong thing: it closes the comment list you had open and the post after the one you had the comments open on now just persistently shows "Loading..." even though it thinks it's already loaded the comments. This is because the comments are lazily loaded and they just assume the same comment is passed each time (which sounds relatively sane here), but in this case, it's not. This is because of how Mithril.js patches unkeyed fragments: it patches them one by one iteratively in a very simple fashion. So in this case, the diff might look like this: - Before: `A, B, C, D, E` - Patched: `A, B, C -> D, D -> E, E -> (removed)` And since the component remains the same (it's always `Comment`), only the attributes change and it's not replaced. -To fix this bug, you simply add a key, so Mithril knows to potentially move state around if necessary to fix the issue. [Here's a live, working example of everything fixed.](https://flems.io/#0=N4Igxg9gdgzhA2BTEAucD4EMAONEBMQAaEGMAJw1QG0AGIgZgCYB2AXRIDMBLJGG0FEwBbZGgB0ACwAuw+MXRRpiJahAgAvkUEixIYRHyJ44gFb8SkJSulqAOiACueAAQxp5bmGkO7UPwBumOQuALKGxi4AvC4AFJyOUN7c0LEAlC7AfnbSQSFgmGCSiNEuAPIARqaI3uIUiJjKsVCO8PBp2T7+0glJ0ilQLgVFBLEA1ogAnkQu3FDc0hlZ3TnkiNKO5IPDxdQTk2wuAIQxLW0uAPxDhbv7hyhxO4h7U4cxcwvpHd0a2d29yWgLmEmAmAFU8OR0plOjk8i5nIhyAA5XSlTigpHiObKLbrcSIlG6dKwjzrTbbG6jByElAOFwAagRkNRohmAP6qSWpJyAHpeS4AJJGTBtaZCgDk+Bcpmc0mZJXgEDWwmw3AAHpF8Nw1t54OKKo55QsXPgIIgYFAJfKYI5sNhlV0cs7pPyXAAJAAqnoACgBlcQuP0QWawZSYfAzQUuSQRlzSEMAd08ymBkxcEETgwAjo4vGMXMj1vBuJx05CAl5EDzXQKEy5E8qC8EIIlpSbMFB20l4I4jC5sJRsEiXABhMoAJT9MYaRnIMDpKx8tZj0mkuBQ-IA5gtJI4KnUIMJedwYNxMJJRBHMzBeUqVWrNfAALSQefPwcQdWTGvw7jCLcwXIeBSgcGR1wXfljEQLdO2kZ9k24LcZGfTAABZMAAVkQNDxCgYtS0mQ9jzwgiy15DkBlvNZcwtHwQD+F1VnJLZgXEGjHDo2J-0A4CZmWJjlwqQoxi3Sg2weDxOKIGtmJgB1YEQT1JmHB4HAqJUKgcGSlxyDQ0nEaRiigeJEkBEyNIgCpuV05c1g2ViBOXQTuHwB5HDzfBxACND0h0wSXUJVlEHclldH85zIsJH0vAckKXDBCcABk6jWRpEEqapvESpLYks6yIsE35bP00lSp+RiejMzlBhBcFIRgWJIESRYYSXeFCRgUpqDYUlOGVOI-1KWgAG5ZhcAAeIZWyUMbuAZBkMk68RsGcSRYjqxAISRL5SXsikXB9ShhFPRBxFFeBYk6749Mq+EwE2NYlG28gZgddwYEqyigU4dYihe6EnOYhzKRGfBYhpSFtJcb6TJswT9tYzaAYMoyVFMvoBiuyF4cinIHvIJ7pBe0pCRrcqXQp6Ris6WGYb+yQfQgD7Ab2ljQeKcGHHe6RPuIGHqqx3GXUR2rMRexqMQmch2M7M1hDwxxhAqHbgBOqAHgARhmEF1QeJhaH0m68ekQzjIx8zYhLdxhZNnmupia3TZBbALZq7GkVtgKyRBtrvZctyEU87zfLSQqTcCyFQqRcP-bXRB1WkB4paxe9EAV5RE782STcgYRRCUUcZqTmHMRl8g5aPRXldV9WHnoYFMD1lwGENsOc8pjvqeNoqe+7zoae6e6jwL3nSkympTfqdLmladovsFn6GaL-ObEannbdF64wYhkAebpEBGQHZnTdc9lF7hv3Iq35GGtiHniNHouWtR83Yat09WqByK89HmBqAfq5N4LgnbiBdm7LGhIvbX3ZlfOOrl3LBx8tnWyglaQKlel3HImcS4pxlmnDOCdpAoLjjTE2VNKZ9ypoPHIdMVQQACIgAAQpMYUH93AzAWOnW2w8WrDT6gNWIQ0YijXGlNUBSAoBbiMnNBa0DSxxCdtQbgbBsTShOO8ZQCtXIZCUc1JQC1gFKJUWVUkEiVDSMkKUfRTo7KwPwomQ6x1ToQNSGsOA8BGFLDcOsT0-5ECtmIe4hAjCZiYVoLQDI1DKpb2-r9aQ-0o70wSZIF64d4lFCZh9ZODMsm83ScvEea8ckpJXn-WOS4tzrBesnC+rNbJbwJkTF6ZUKkuiqdIPJC4BaYy5HAuxvt7bHFOHPS4x8PriBgCWMAiBoQPDOPAVplV8ZpWUHk2pvSTI4OgVvDJqTITpDNujd+0Ccj2xWmtWI38A6INciHEhEcNhJKaTYNJXccGSSIbHH+RTC7F3rt8vSfcgVLP8EuSQrlEDrJ6ZbDe-TgYHXoYwlhbD7ZvRPlQ-ylT1hlOKTC92cLrk+wOr-NeACT5qMOCcFwCyxmkqUP-QB+BVFTKrHMmlc9QWkgjPgXFSgNmwpPjMbZ8LiWsSOkeU6502ixGoDnPZKNAU9EKavBl98MU1jYK-Y5dTTkJN+bzcl7hKUXJgOtIlLoEFBzucg9uqDlzoJec9SESrsFEM+YnJVFDlzeq0MsoykK+W4LqYSmsW8kXMNYeDelhqmUcHGYsLl1M-D6XSAoPASBzL8DQJrTCKBMIAE5NDaBAEIUQagzAWEUMoVQaBIYlHcJ4bwvgwV0xRlfWJpJKyIETAK92AQoARGgR1SEpQB0RHOmuecBIoYNNgcIXeM6kTaRzguhwS7yAflipsas-M10gB4tDYAMByBgGjjLaK261hGyVfujdz4y27pmA4AAAtDQkG7gp2sElQgeKa-h0xXu9RAAB1OYZpEz1LhMENw+4TprjmFudEoo8CdHhDgzh8x+iindIgJCMhOidvatwHtfasbjqMMOmDPMx2DqMJOjwMAVonzDfOxdedgMrvtfunBwQGhcbjhayK2oYCYA0gQB4toKjwf6FImYQRezxQw1ghMSRVnxXfhRxAeqXJYfPPAXD+H5QxAZImcDmZxAdKA0aAgfppCTCQEIujZ15YGWKEZyZ0zZkN2fEwYFjytPiHlpM+zSApB4eQsZlwDhMBGggC2uOLpAvBfcA5s67nIulFCI0SQYCm7cT0zhiLMh5POaC1XMglA2iGcixkJk3N1QJceXpV1y5oBzFWsGzZsREABB05FHBpReuGWCB07yopOIqZyMN6Qo38SpbCxlmQoEQCxYTE1xLygAgjfIGNhb6XitRey0ZPL6oCsLH0zVkrLgZtzdNpVjAV3Wr1b3o1hi9rKatbOVgGZkgEBzgeIFxojGVo-cQH9+Ac5Wt+vtd+k2d6OPM0QM+KT8Hob7sNGuaAAnHnAGgGAaZYwyN9KEy6VHCxZNIZiFJasH3lxA6nUx8nxDtlHJMic0V3tmeU+Q-AVDdOXQKzWPgCukH-O9xU4NmIvh3ukJh4lkAeSNuRTSP539FUoCD0AwaoDSOoNisGN-btvb8Xkec1RkINGYgM5BzzVjvsF267wGBrsmZ+I52wGDiHAPosgBAymEokxWz5ANeIMPOPBLM+J1soh-Wt7hCMCYHlQb1UcPjLHnO8vIq+r-ZrgDF8xwGv10RpLJGTeafN6K4eqqos2+nTG+3B0EcGoj5FBdaTMiOtD4SfSt72MGufBUQwP5+YxsMrHvvMXH42GfBCowrfBJ457PmaP+u44J+MFIQNReY3apMkLggovxc+qz-7EA0YzRWnlCWCY8YIV8xzmr0xGutcF7yU72ZuMS-07L6vrTFvpoa9aMJ1gd68W850HdYgP8XcIN3d7VPdChwd-skQ1I-cA8XAg9NgE0w9xAF9lwo8wgIgTBp41khVyZ1dbo89W038T4nMh0r54QzVMwg0HZS4+dadoMQ8a8YAAAxBmAgXnfnQjWBI3X-U3VIf-KvajE+YA+jUApjO3WyavP+UoJgxMFgsZDfEwKzA1deDFFweZTlCApvRdO3YgVdWIDvYAdBB+HvOHQSO9HmQfYfaGB+bZSfEAZjdwZ8UQWbPAwXXeTALw+CGNV8YufwyKJfAnFfcQy+UnSKBRWII4NQjQgAMlSOOBjV4P4PwH60EiyL4JSQEOp3IEmwF0ii0PED2RYNT0WDZw2nYkP0wETGPyKim15j+3UN0NKGSM6JYJUw0FP0eQfhjWfiUGfUPgb3MPKJGJ12LmiCiBiE1jGRlwMN9z5la3sPh0COCNn0hQiJdCiMJ1XzyOXEqLnyhVoI3nqIPxF2aNaJPy+3ogv2gGtBAW4FvwDQfzp2Py2LJz6O6KuEcJPjCO4IOP1W4IWI5TaC7iBP7xr2fCdkRIgAjEQ2hgcCShRO1CkRwIcAeIeDlXKObwRKdmhiyLy1dnfl305xNhvigINX4n2AeDHzPkANHmZINW9W9hvRU0dzmNVCR34n3gTV7y7l6lhzWIWRrAoOTSoL8Dpj4NGFxkqL2S6UOTRn30aLuJaOEN9lENI1iLX3p2kI+iyyIMs3WDVP8zpPXV+gIAiP3UkE1nRJAEVPwDxKVSGUWOhJAjhPXScKRKVFRKkRdMxODK3FxJAGPweGBO8ON3BIXXfyPGAz+Lb1MJBNJP5nOXAXflDXKLpLyUZKmAeCZXRXcBLJPi5Oz1V0f2lOf0oMHgVgMBaliDNAelHnECH3wHFDdLSHTWgizTUE1gADZNYUAmAAAOYtHQctCQMAGAKtKwGtWwNALs9MZYFwGGaAeCDEE6fUB4AAQU8FFBmFE1gBRyRFLBGn-TBQ3Tak3JEwQMmGTiQHVGvMGBcFFCQigGfC4WEG6RmWsHIHfMHmCJcDvI3Mbl2zmGcKx2EHrnEGwmEBAr+HvTVG8B3XvKgp3B-M8EiwQqQpQtvMJC3QwrWFmAAiwqWxLgYHTnfM3KH3IDnGfArm1GcAeFouQpvL8GCJmGImAywsYp901mwHVDcAQFchcA0hEnoqkuVGYtYu4HYpcE1jor8E3OwB5UQy1jUo-JBGgp-ITGwAQpYEIu4qgH4qR3T0Tj40wEEvkpQJpWgEQFkvcW4AAC94pB18JZKGEkROAlQTc58jAoBZKzN8AjItYIkABSWS6iji3Sh808J8h4SyMAMYWS-qJQFHDy+KVSriuUiyxHPAFHODE0SCnBVCEsLcDWFwPCmQIini4qkoTHVTLCrK+CM8TynSgqhihyzdRS5SpgRKuSpixy7yly9SqSkSMSGaQOAAYloEwBYFksgHvAeHms4C2tks0vwGxK3AIvThUpGsfKwGfNDBLHwkHyVHSsaosqcN8LssgtOswHOoCoTlktlHcEIjCOsH5TcE0pmUH3WETEQBUFkv0pwufCMpMrMo-I6pyu6pcFoHEAnN0tAoevWDsoAD4XAAAqI+AmyCyGmCpATgEufKu63Y0ImxPipwi4vi0Ii4+ysa8geZZynarSqReuTK7cxGvKkahG-bB4BYL8sAVazYOANm4+HEJEKmpwmm-5P7RhTBe6kEi4lAZWmOJqgfDWrWkISCta5UDaic02+WkErIrCkmwyiAYy5GxC9Gv4F9BCRACoMYBYZ8fYTgCuUQLqTsf8dKMM-arC2gaKzIaaP6kuDErEtEkAMaYqTcpgTCMO4ACO5c1AoOxDXAuOlwBOlwcJFOtOmwDOmOnE7O+Oqa0ywupc4u33TOsu8u3Om8l9L2n2i0T8+YEEZQeupDSC0O8Omu-66O8MhwCuj8pO6u7c2u4e-axuvOgugeqeoekAHu8PHOvOquxeyOku8MyMsezczWGKre9Ouu0urcUepuwq8QIM4OiqohX8rsWu58AtF+gtUS2Sy65HeK5Gqmm+7SlATAcmkcO+xOB+kKkuUaKaz+2fQ7EWwreAWSl686tKjKqaweqOles+veqal2t2j2-2rugYfWLqT+mDFGzCP2zuwOs+0MHgLDSaj8ghxoIhlwJgEhuYBoEIchyhgO7umhuYOhrhKm0Ip2dqvmrq+KFGtGgq0CkRz+cCki9CuKCi3uqa7+4amR1CmNLC3a-anq2SoSxykSsSjxSS6S26qawxga1E5SlG0ykayqr8mqh4eq6QYRgfNcrCxx6q2qsmtxqa7x78yTWbcgfxq+pmyFK20bGCmG+2+xzR28hW7oyy1wSCqxrWUS8SksaUcx1Bj8qxlimx7pFG4cka3R7SuJuGzc626G22nm8y6m5J5q6ykJhoKi2BluQW8R3KhC6R827wy2lJlqo0NqyCoWnp+2vpqa8p7m+2pC46mR-szNGqbNEATWZgFATWTQDgEAS6sYbNagGcvQeDSQTweQEgTYeQNAcCDcfkRIbAUSYiXkE5s5l9JgcQFGtCZ53cM5sBOYStBQezYcNQSrbgbAWwLQI5tQPBF9XCTWD5hQS5tQG5yCXkRMdypi7EKAXkdwOWUUZyiiMuWF8QeF2gQFlSPQUF8F6c0tXQNQDyVyF9BgcQZlhgRF4CZFtcW5tFjFryOYHF2bLsfF-CXkBl-AJlllll8l4FtAKliFtgDQIAA) +To fix this bug, you simply add a key, so Mithril.js knows to potentially move state around if necessary to fix the issue. [Here's a live, working example of everything fixed.](https://flems.io/#0=N4Igxg9gdgzhA2BTEAucD4EMAONEBMQAaEGMAJw1QG0AGIgZgCYB2AXRIDMBLJGG0FEwBbZGgB0ACwAuw+MXRRpiJahAgAvkUEixIYRHyJ44gFb8SkJSulqAOiACueAAQxp5bmGkO7UPwBumOQuALKGxi4AvC4AFJyOUN7c0LEAlC7AfnbSQSFgmGCSiNEuAPIARqaI3uIUiJjKsVCO8PBp2T7+0glJ0ilQLgVFBLEA1ogAnkQu3FDc0hlZ3TnkiNKO5IPDxdQTk2wuAIQxLW0uAPxDhbv7hyhxO4h7U4cxcwvpHd0a2d29yWgLmEmAmAFU8OR0plOjk8i5nIhyAA5XSlTigpHiObKLbrcSIlG6dKwjzrTbbG6jByElAOFwAagRkNRohmAP6qSWpJyAHpeS4AJJGTBtaZCgDk+Bcpmc0mZJXgEDWwmw3AAHpF8Nw1t54OKKo55QsXPgIIgYFAJfKYI5sNhlV0cs7pPyXAAJAAqnoACgBlcQuP0QWawZSYfAzQUuSQRlzSEMAd08ymBkxcEETgwAjo4vGMXMj1vBuJx05CAl5EDzXQKEy5E8qC8EIIlpSbMFB20l4I4jC5sJRsEiXABhMoAJT9MYaRnIMDpKx8tZj0mkuBQ-IA5gtJI4KnUIMJedwYNxMJJRBHMzBeUqVWrNfAALSQefPwcQdWTGvw7jCLcwXIeBSgcGR1wXfljEQLdO2kZ9k24LcZGfTAABZMAAVkQNDxCgYtS0mQ9jzwgiy15DkBlvNZcwtHwQD+F1VnJLZgXEGjHDo2J-0A4CZmWJjlwqQoxi3Sg2weDxOKIGtmJgB1YEQT1JmHB4HAqJUKgcGSlxyDQ0nEaRiigeJEkBEyNIgCpuV05c1g2ViBOXQTuHwB5HDzfBxACND0h0wSXUJVlEHclldH85zIsJH0vAckKXDBCcABk6jWRpEEqapvESpLYks6yIsE35bP00lSp+RiejMzlBhBcFIRgWJIESRYYSXeFCRgUpqDYUlOGVOI-1KWgAG5ZhcAAeIZWyUMbuAZBkMk68RsGcSRYjqxAISRL5SXsikXB9ShhFPRBxFFeBYk6749Mq+EwE2NYlG28gZgddwYEqyigU4dYihe6EnOYhzKRGfBYhpSFtJcb6TJswT9tYzaAYMoyVFMvoBiuyF4cinIHvIJ7pBe0pCRrcqXQp6Ris6WGYb+yQfQgD7Ab2ljQeKcGHHe6RPuIGHqqx3GXUR2rMRexqMQmch2M7M1hDwxxhAqHbgBOqAHgARhmEF1QeJhaH0m68ekQzjIx8zYhLdxhZNnmupia3TZBbALZq7GkVtgKyRBtrvZctyEU87zfLSQqTcCyFQqRcP-bXRB1WkB4paxe9EAV5RE782STcgYRRCUUcZqTmHMRl8g5aPRXldV9WHnoYFMD1lwGENsOc8pjvqeNoqe+7zoae6e6jwL3nSkympTfqdLmladovsFn6GaL-ObEannbdF64wYhkAebpEBGQHZnTdc9lF7hv3Iq35GGtiHniNHouWtR83Yat09WqByK89HmBqAfq5N4LgnbiBdm7LGhIvbX3ZlfOOrl3LBx8tnWyglaQKlel3HImcS4pxlmnDOCdpAoLjjTE2VNKZ9ypoPHIdMVQQACIgAAQpMYUH93AzAWOnW2w8WrDT6gNWIQ0YijXGlNUBSAoBbiMnNBa0DSxxCdtQbgbBsTShOO8ZQCtXIZCUc1JQC1gFKJUWVUkEiVDSMkKUfRTo7KwPwomQ6x1ToQNSGsOA8BGFLDcOsT0-5ECtmIe4hAjCZiYVoLQDI1DKpb2-r9aQ-0o70wSZIF64d4lFCZh9ZODMsm83ScvEea8ckpJXn-WOS4tzrBesnC+rNbJbwJkTF6ZUKkuiqdIPJC4BaYy5HAuxvt7bHFOHPS4x8PriBgCWMAiBoQPDOPAVplV8ZpWUHk2pvSTI4OgVvDJqTITpDNujd+0Ccj2xWmtWI38A6INciHEhEcNhJKaTYNJXccGSSIbHH+RTC7F3rt8vSfcgVLP8EuSQrlEDrJ6ZbDe-TgYHXoYwlhbD7ZvRPlQ-ylT1hlOKTC92cLrk+wOr-NeACT5qMOCcFwCyxmkqUP-QB+BVFTKrHMmlc9QWkgjPgXFSgNmwpPjMbZ8LiWsSOkeU6502ixGoDnPZKNAU9EKavBl98MU1jYK-Y5dTTkJN+bzcl7hKUXJgOtIlLoEFBzucg9uqDlzoJec9SESrsFEM+YnJVFDlzeq0MsoykK+W4LqYSmsW8kXMNYeDelhqmUcHGYsLl1M-D6XSAoPASBzL8DQJrTCKBMIAE5NDaBAEIUQagzAWEUMoVQaBIYlHcJ4bwvgwV0xRlfWJpJKyIETAK92AQoARGgR1SEpQB0RHOmuecBIoYNNgcIXeM6kTaRzguhwS7yAflipsas-M10gB4tDYAMByBgGjjLaK261hGyVfujdz4y27pmA4AAAtDQkG7gp2sElQgeKa-h0xXu9RAAB1OYZpEz1LhMENw+4TprjmFudEoo8CdHhDgzh8x+iindIgJCMhOidvatwHtfasbjqMMOmDPMx2DqMJOjwMAVonzDfOxdedgMrvtfunBwQGhcbjhayK2oYCYA0gQB4toKjwf6FImYQRezxQw1ghMSRVnxXfhRxAeqXJYfPPAXD+H5QxAZImcDmZxAdKA0aAgfppCTCQEIujZ15YGWKEZyZ0zZkN2fEwYFjytPiHlpM+zSApB4eQsZlwDhMBGggC2uOLpAvBfcA5s67nIulFCI0SQYCm7cT0zhiLMh5POaC1XMglA2iGcixkJk3N1QJceXpV1y5oBzFWsGzZsREABB05FHBpReuGWCB07yopOIqZyMN6Qo38SpbCxlmQoEQCxYTE1xLygAgjfIGNhb6XitRey0ZPL6oCsLH0zVkrLgZtzdNpVjAV3Wr1b3o1hi9rKatbOVgGZkgEBzgeIFxojGVo-cQH9+Ac5Wt+vtd+k2d6OPM0QM+KT8Hob7sNGuaAAnHnAGgGAaZYwyN9KEy6VHCxZNIZiFJasH3lxA6nUx8nxDtlHJMic0V3tmeU+Q-AVDdOXQKzWPgCukH-O9xU4NmIvh3ukJh4lkAeSNuRTSP539FUoCD0AwaoDSOoNisGN-btvb8Xkec1RkINGYgM5BzzVjvsF267wGBrsmZ+I52wGDiHAPosgBAymEokxWz5ANeIMPOPBLM+J1soh-Wt7hCMCYHlQb1UcPjLHnO8vIq+r-ZrgDF8xwGv10RpLJGTeafN6K4eqqos2+nTG+3B0EcGoj5FBdaTMiOtD4SfSt72MGufBUQwP5+YxsMrHvvMXH42GfBCowrfBJ457PmaP+u44J+MFIQNReY3apMkLggovxc+qz-7EA0YzRWnlCWCY8YIV8xzmr0xGutcF7yU72ZuMS-07L6vrTFvpoa9aMJ1gd68W850HdYgP8XcIN3d7VPdChwd-skQ1I-cA8XAg9NgE0w9xAF9lwo8wgIgTBp41khVyZ1dbo89W038T4nMh0r54QzVMwg0HZS4+dadoMQ8a8YAAAxBmAgXnfnQjWBI3X-U3VIf-KvajE+YA+jUApjO3WyavP+UoJgxMFgsZDfEwKzA1deDFFweZTlCApvRdO3YgVdWIDvYAdBB+HvOHQSO9HmQfYfaGB+bZSfEAZjdwZ8UQWbPAwXXeTALw+CGNV8YufwyKJfAnFfcQy+UnSKBRWII4NQjQgAMlSOOBjV4P4PwH60EiyL4JSQEOp3IEmwF0ii0PED2RYNT0WDZw2nYkP0wETGPyKim15j+3UN0NKGSM6JYJUw0FP0eQfhjWfiUGfUPgb3MPKJGJ12LmiCiBiE1jGRlwMN9z5la3sPh0COCNn0hQiJdCiMJ1XzyOXEqLnyhVoI3nqIPxF2aNaJPy+3ogv2gGtBAW4FvwDQfzp2Py2LJz6O6KuEcJPjCO4IOP1W4IWI5TaC7iBP7xr2fCdkRIgAjEQ2hgcCShRO1CkRwIcAeIeDlXKObwRKdmhiyLy1dnfl305xNhvigINX4n2AeDHzPkANHmZINW9W9hvRU0dzmNVCR34n3gTV7y7l6lhzWIWRrAoOTSoL8Dpj4NGFxkqL2S6UOTRn30aLuJaOEN9lENI1iLX3p2kI+iyyIMs3WDVP8zpPXV+gIAiP3UkE1nRJAEVPwDxKVSGUWOhJAjhPXScKRKVFRKkRdMxODK3FxJAGPweGBO8ON3BIXXfyPGAz+Lb1MJBNJP5nOXAXflDXKLpLyUZKmAeCZXRXcBLJPi5Oz1V0f2lOf0oMHgVgMBaliDNAelHnECH3wHFDdLSHTWgizTUE1gADZNYUAmAAAOYtHQctCQMAGAKtKwGtWwNALs9MZYFwGGaAeCDEE6fUB4AAQU8FFBmFE1gBRyRFLBGn-TBQ3Tak3JEwQMmGTiQHVGvMGBcFFCQigGfC4WEG6RmWsHIHfMHmCJcDvI3Mbl2zmGcKx2EHrnEGwmEBAr+HvTVG8B3XvKgp3B-M8EiwQqQpQtvMJC3QwrWFmAAiwqWxLgYHTnfM3KH3IDnGfArm1GcAeFouQpvL8GCJmGImAywsYp901mwHVDcAQFchcA0hEnoqkuVGYtYu4HYpcE1jor8E3OwB5UQy1jUo-JBGgp-ITGwAQpYEIu4qgH4qR3T0Tj40wEEvkpQJpWgEQFkvcW4AAC94pB18JZKGEkROAlQTc58jAoBZKzN8AjItYIkABSWS6iji3Sh808J8h4SyMAMYWS-qJQFHDy+KVSriuUiyxHPAFHODE0SCnBVCEsLcDWFwPCmQIini4qkoTHVTLCrK+CM8TynSgqhihyzdRS5SpgRKuSpixy7yly9SqSkSMSGaQOAAYloEwBYFksgHvAeHms4C2tks0vwGxK3AIvThUpGsfKwGfNDBLHwkHyVHSsaosqcN8LssgtOswHOoCoTlktlHcEIjCOsH5TcE0pmUH3WETEQBUFkv0pwufCMpMrMo-I6pyu6pcFoHEAnN0tAoevWDsoAD4XAAAqI+AmyCyGmCpATgEufKu63Y0ImxPipwi4vi0Ii4+ysa8geZZynarSqReuTK7cxGvKkahG-bB4BYL8sAVazYOANm4+HEJEKmpwmm-5P7RhTBe6kEi4lAZWmOJqgfDWrWkISCta5UDaic02+WkErIrCkmwyiAYy5GxC9Gv4F9BCRACoMYBYZ8fYTgCuUQLqTsf8dKMM-arC2gaKzIaaP6kuDErEtEkAMaYqTcpgTCMO4ACO5c1AoOxDXAuOlwBOlwcJFOtOmwDOmOnE7O+Oqa0ywupc4u33TOsu8u3Om8l9L2n2i0T8+YEEZQeupDSC0O8Omu-66O8MhwCuj8pO6u7c2u4e-axuvOgugeqeoekAHu8PHOvOquxeyOku8MyMsezczWGKre9Ouu0urcUepuwq8QIM4OiqohX8rsWu58AtF+gtUS2Sy65HeK5Gqmm+7SlATAcmkcO+xOB+kKkuUaKaz+2fQ7EWwreAWSl686tKjKqaweqOles+veqal2t2j2-2rugYfWLqT+mDFGzCP2zuwOs+0MHgLDSaj8ghxoIhlwJgEhuYBoEIchyhgO7umhuYOhrhKm0Ip2dqvmrq+KFGtGgq0CkRz+cCki9CuKCi3uqa7+4amR1CmNLC3a-anq2SoSxykSsSjxSS6S26qawxga1E5SlG0ykayqr8mqh4eq6QYRgfNcrCxx6q2qsmtxqa7x78yTWbcgfxq+pmyFK20bGCmG+2+xzR28hW7oyy1wSCqxrWUS8SksaUcx1Bj8qxlimx7pFG4cka3R7SuJuGzc626G22nm8y6m5J5q6ykJhoKi2BluQW8R3KhC6R827wy2lJlqo0NqyCoWnp+2vpqa8p7m+2pC46mR-szNGqbNEATWZgFATWTQDgEAS6sYbNagGcvQeDSQTweQEgTYeQNAcCDcfkRIbAUSYiXkE5s5l9JgcQFGtCZ53cM5sBOYStBQezYcNQSrbgbAWwLQI5tQPBF9XCTWD5hQS5tQG5yCXkRMdypi7EKAXkdwOWUUZyiiMuWF8QeF2gQFlSPQUF8F6c0tXQNQDyVyF9BgcQZlhgRF4CZFtcW5tFjFryOYHF2bLsfF-CXkBl-AJlllll8l4FtAKliFtgDQIAA) ```javascript // In the `Feed` component @@ -471,7 +475,7 @@ return [m(Person, #### Keying elements unnecessarily -It's a common misconception that keys are themselves identities. Mithril enforces for all fragments that their children must either all have keys or all lack keys, and will throw an error if you forget this. Suppose you have this layout: +It's a common misconception that keys are themselves identities. Mithril.js enforces for all fragments that their children must either all have keys or all lack keys, and will throw an error if you forget this. Suppose you have this layout: ```javascript m(".page", @@ -481,7 +485,7 @@ m(".page", ) ``` -This obviously will throw, as `.header` has a key and `.body` and `.footer` both lack keys. But here's the thing: you don't need keys for this. If you find yourself using keys for things like this, the solution isn't to add keys, but to remove them. Only add them if you really, *really* need them. Yes, the underlying DOM nodes have identities, but Mithril doesn't need to track those identities to correctly patch them. It practically never does. Only with lists where each entry has some sort of associated state Mithril doesn't itself track, whether it be in a model, in a component, or in the DOM itself, do you need keys. +This obviously will throw, as `.header` has a key and `.body` and `.footer` both lack keys. But here's the thing: you don't need keys for this. If you find yourself using keys for things like this, the solution isn't to add keys, but to remove them. Only add them if you really, *really* need them. Yes, the underlying DOM nodes have identities, but Mithril.js doesn't need to track those identities to correctly patch them. It practically never does. Only with lists where each entry has some sort of associated state Mithril.js doesn't itself track, whether it be in a model, in a component, or in the DOM itself, do you need keys. One last thing: avoid static keys. They're always unnecessary. If you're not computing your `key` attribute, you're probably doing something wrong. @@ -523,7 +527,7 @@ things.map(function(thing) { }) ``` -Instead, filter the list before returning it, and Mithril will do the right thing. Most of the time, [`Array.prototype.filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) is precisely what you need and you should definitely try it out. +Instead, filter the list before returning it, and Mithril.js will do the right thing. Most of the time, [`Array.prototype.filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) is precisely what you need and you should definitely try it out. ```javascript // PREFER @@ -546,4 +550,4 @@ var things = [ ] ``` -Mithril uses an empty object to map keys to indices to know how to properly patch keyed fragments. When you have a duplicate key, it's no longer clear where that element moved to, and so Mithril will break in that circumstance and do unexpected things on update, especially if the list changed. Distinct keys are required for Mithril to properly connect old to new nodes, so you must choose something locally unique to use as a key. +Mithril.js uses an empty object to map keys to indices to know how to properly patch keyed fragments. When you have a duplicate key, it's no longer clear where that element moved to, and so Mithril.js will break in that circumstance and do unexpected things on update, especially if the list changed. Distinct keys are required for Mithril.js to properly connect old to new nodes, so you must choose something locally unique to use as a key. diff --git a/docs/layout.html b/docs/layout.html index 135bc846e..4a95e1010 100644 --- a/docs/layout.html +++ b/docs/layout.html @@ -1,5 +1,5 @@ - + Mithril.js @@ -8,6 +8,7 @@ +
diff --git a/docs/learning-mithril.md b/docs/learning-mithril.md index bbf0b561e..ae6281519 100644 --- a/docs/learning-mithril.md +++ b/docs/learning-mithril.md @@ -1,5 +1,9 @@ + + # Learning Resources -Links to Mithril learning content: +Links to Mithril.js learning content: - [Mithril 0-60](https://vimeo.com/showcase/5584199) diff --git a/docs/lifecycle-methods.md b/docs/lifecycle-methods.md index 381b1fd2c..e95c0e90e 100644 --- a/docs/lifecycle-methods.md +++ b/docs/lifecycle-methods.md @@ -1,3 +1,7 @@ + + # Lifecycle methods - [Usage](#usage) @@ -137,7 +141,7 @@ m(RedrawReporter, {data: "Hello"}) ### onbeforeremove -The `onbeforeremove(vnode)` hook is called before a DOM element is detached from the document. If a Promise is returned, Mithril only detaches the DOM element after the promise completes. +The `onbeforeremove(vnode)` hook is called before a DOM element is detached from the document. If a Promise is returned, Mithril.js only detaches the DOM element after the promise completes. This hook is only called on the DOM element that loses its `parentNode`, but it does not get called in its child elements. @@ -188,7 +192,7 @@ function Timer() { ### onbeforeupdate -The `onbeforeupdate(vnode, old)` hook is called before a vnode is diffed in a update. If this function is defined and returns false, Mithril prevents a diff from happening to the vnode, and consequently to the vnode's children. +The `onbeforeupdate(vnode, old)` hook is called before a vnode is diffed in a update. If this function is defined and returns false, Mithril.js prevents a diff from happening to the vnode, and consequently to the vnode's children. This hook by itself does not prevent a virtual DOM subtree from being generated unless the subtree is encapsulated within a component. @@ -200,7 +204,7 @@ This hook is useful to reduce lag in updates in cases where there is a overly la ### Avoid anti-patterns -Although Mithril is flexible, some code patterns are discouraged: +Although Mithril.js is flexible, some code patterns are discouraged: #### Avoid premature optimizations diff --git a/docs/migration-v02x.md b/docs/migration-v02x.md index 552b3f388..9ab4c3d41 100644 --- a/docs/migration-v02x.md +++ b/docs/migration-v02x.md @@ -122,7 +122,7 @@ It served little use in general, and you can always add it back yourself. You sh ## `config` function -In v0.2.x mithril provided a single lifecycle method, `config`. v2.x provide much more fine-grained control over the lifecycle of a vnode. +In v0.2.x Mithril.js provided a single lifecycle method, `config`. v2.x provide much more fine-grained control over the lifecycle of a vnode. ### v0.2.x @@ -163,11 +163,11 @@ If available the DOM-Element of the vnode can be accessed at `vnode.dom`. ## Changes in redraw behaviour -Mithril's rendering engine still operates on the basis of semi-automated global redraws, but some APIs and behaviours differ: +Mithril.js' rendering engine still operates on the basis of semi-automated global redraws, but some APIs and behaviours differ: ### No more redraw locks -In v0.2.x, Mithril allowed 'redraw locks' which temporarily prevented blocked draw logic: by default, `m.request` would lock the draw loop on execution and unlock when all pending requests had resolved - the same behaviour could be invoked manually using `m.startComputation()` and `m.endComputation()`. The latter APIs and the associated behaviour has been removed in v2.x without replacement. Redraw locking can lead to buggy UIs: the concerns of one part of the application should not be allowed to prevent other parts of the view from updating to reflect change. +In v0.2.x, Mithril.js allowed 'redraw locks' which temporarily prevented blocked draw logic: by default, `m.request` would lock the draw loop on execution and unlock when all pending requests had resolved - the same behaviour could be invoked manually using `m.startComputation()` and `m.endComputation()`. The latter APIs and the associated behaviour has been removed in v2.x without replacement. Redraw locking can lead to buggy UIs: the concerns of one part of the application should not be allowed to prevent other parts of the view from updating to reflect change. ### Cancelling redraw from event handlers @@ -195,7 +195,7 @@ m("div", { ### Synchronous redraw changed -In v0.2.x it was possible to force mithril to redraw immediately by passing a truthy value to `m.redraw()`. In v2.x, this functionality was split into two different methods for clarity. +In v0.2.x it was possible to force Mithril.js to redraw immediately by passing a truthy value to `m.redraw()`. In v2.x, this functionality was split into two different methods for clarity. #### v0.2.x @@ -821,7 +821,7 @@ Additionally, if the `extract` option is passed to `m.request` the return value ## `m.request` headers -In v0.2.x, Mithril didn't set any headers on requests by default. Now, it sets up to 2 headers: +In v0.2.x, Mithril.js didn't set any headers on requests by default. Now, it sets up to 2 headers: - `Content-Type: application/json; charset=utf-8` for requests with JSON bodies that are `!= null` - `Accept: application/json, text/*` for requests expecting JSON responses diff --git a/docs/migration-v1x.md b/docs/migration-v1x.md index 599ffc94b..0ba10e24b 100644 --- a/docs/migration-v1x.md +++ b/docs/migration-v1x.md @@ -318,12 +318,12 @@ In v1.x, component vnode children were normalized like other vnodes. In v2.x, th ## `m.request` headers -In v1.x, Mithril set these two headers on all non-`GET` requests, but only when `useBody` was set to `true` (the default) and the other conditions listed hold: +In v1.x, Mithril.js set these two headers on all non-`GET` requests, but only when `useBody` was set to `true` (the default) and the other conditions listed hold: - `Content-Type: application/json; charset=utf-8` for requests with JSON bodies - `Accept: application/json, text/*` for requests expecting JSON responses -In v2.x, Mithril sets the first for all requests with JSON bodies that are `!= null` and omits it by default otherwise, and this is done independent of which method is chosen, including on `GET` requests. +In v2.x, Mithril.js sets the first for all requests with JSON bodies that are `!= null` and omits it by default otherwise, and this is done independent of which method is chosen, including on `GET` requests. The first of the two headers, `Content-Type`, will trigger a CORS prefetch as it [is not a CORS-safelisted request header](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) due to the specified content type, and that could introduce new errors depending on how CORS is configured on your server. If you run into issues with this, you may need to override that header in question by passing `headers: {"Content-Type": "text/plain"}`. (The `Accept` header doesn't trigger anything, so you don't need to override that.) diff --git a/docs/mount.md b/docs/mount.md index 5263ccdb5..cb7908ef0 100644 --- a/docs/mount.md +++ b/docs/mount.md @@ -1,3 +1,7 @@ + + # mount(root, component) - [Description](#description) @@ -60,7 +64,7 @@ Running `mount(element, OtherComponent)` where `element` is a current mount poin #### Unmount -Using `m.mount(element, null)` on an element with a previously mounted component unmounts it and cleans up Mithril internal state. This can be useful to prevent memory leaks when removing the `root` node manually from the DOM. +Using `m.mount(element, null)` on an element with a previously mounted component unmounts it and cleans up Mithril.js internal state. This can be useful to prevent memory leaks when removing the `root` node manually from the DOM. #### Headless mounts @@ -107,6 +111,6 @@ In contrast, traversing a JavaScript data structure has a much more predictable A component rendered via `m.mount` [automatically redraws](autoredraw.md) in response to view events, `m.redraw()` calls or `m.request()` calls. Vnodes rendered via `m.render()` do not. -`m.mount()` is suitable for application developers integrating Mithril widgets into existing codebases where routing is handled by another library or framework, while still enjoying Mithril's auto-redrawing facilities. +`m.mount()` is suitable for application developers integrating Mithril.js widgets into existing codebases where routing is handled by another library or framework, while still enjoying Mithril.js' auto-redrawing facilities. `m.render()` is suitable for library authors who wish to manually control rendering (e.g. when hooking to a third party router, or using third party data-layer libraries like Redux). diff --git a/docs/nav-guides.md b/docs/nav-guides.md index e0244b0f8..cbf5da9ad 100644 --- a/docs/nav-guides.md +++ b/docs/nav-guides.md @@ -19,7 +19,7 @@ - [Keys](keys.md) - [Autoredraw system](autoredraw.md) - Social - - [Mithril Jobs](https://github.com/MithrilJS/mithril.js/wiki/JOBS) + - [Mithril.js Jobs](https://github.com/MithrilJS/mithril.js/wiki/JOBS) - [How to contribute](contributing.md) - [Credits](credits.md) - [Code of Conduct](code-of-conduct.md) diff --git a/docs/parsePathname.md b/docs/parsePathname.md index cabad2d3d..d01c110c0 100644 --- a/docs/parsePathname.md +++ b/docs/parsePathname.md @@ -1,3 +1,6 @@ + # parsePathname(string) - [Description](#description) @@ -23,7 +26,7 @@ var object = m.parsePathname("/path/user?a=1&b=2") Argument | Type | Required | Description ------------ | -------- | -------- | --- -`string` | `String` | Yes | A URL +`url` | `String` | Yes | A URL **returns** | `Object` | | A `{path, params}` pair where `path` is the [normalized path](paths.md#path-normalization) and `params` is the [parsed parameters](paths.md#parameter-normalization). [How to read signatures](signatures.md) diff --git a/docs/parseQueryString.md b/docs/parseQueryString.md index 08e6d92ad..9b34b1f1a 100644 --- a/docs/parseQueryString.md +++ b/docs/parseQueryString.md @@ -1,3 +1,7 @@ + + # parseQueryString(string) - [Description](#description) diff --git a/docs/paths.md b/docs/paths.md index 8618dc6e1..f81a42d41 100644 --- a/docs/paths.md +++ b/docs/paths.md @@ -1,3 +1,6 @@ + # Path Handling - [Path types](#path-types) @@ -124,17 +127,17 @@ m.route(document.body, "/edit/1", { }) ``` -Query parameters are implicitly consumed - you don't need to name them to accept them. You can match based on an existing value, like in `"/edit?type=image"`, but you don't need to use `"/edit?type=:type"` to accept the value. In fact, Mithril would treat that as you trying to literally match against `m.route.param("type") === ":type"`, so you probably don't want to do that. In short, use `m.route.param("key")` or route component attributes to read query parameters. +Query parameters are implicitly consumed - you don't need to name them to accept them. You can match based on an existing value, like in `"/edit?type=image"`, but you don't need to use `"/edit?type=:type"` to accept the value. In fact, Mithril.js would treat that as you trying to literally match against `m.route.param("type") === ":type"`, so you probably don't want to do that. In short, use `m.route.param("key")` or route component attributes to read query parameters. ### Path normalization -Parsed paths are always returned with all the duplicate parameters and extra slashes dropped, and they always start with a slash. These little differences often get in the way, and it makes routing and path handling a lot more complicated than it should be. Mithril internally normalizes paths for routing, but it does not expose the current, normalized route directly. (You could compute it via [`m.parsePathname(m.route.get()).path`](parsePathname.md).) +Parsed paths are always returned with all the duplicate parameters and extra slashes dropped, and they always start with a slash. These little differences often get in the way, and it makes routing and path handling a lot more complicated than it should be. Mithril.js internally normalizes paths for routing, but it does not expose the current, normalized route directly. (You could compute it via [`m.parsePathname(m.route.get()).path`](parsePathname.md).) When parameters are deduplicated during matching, parameters in the query string are preferred over parameters in the path name, and parameters towards the end of the URL are preferred over parameters closer to the start of the URL. ### Path escaping -There are some characters that, if you want to use them literally, you need to escape. Conveniently, `encodeURIComponent` encodes these (and more), and when you substitute parameters and add query parameters, they're encoded as necessary using this. Here's the ones Mithril interprets: +There are some characters that, if you want to use them literally, you need to escape. Conveniently, `encodeURIComponent` encodes these (and more), and when you substitute parameters and add query parameters, they're encoded as necessary using this. Here's the ones Mithril.js interprets: - `:` = `%3A` - `/` = `%2F` (required only in paths) @@ -142,4 +145,4 @@ There are some characters that, if you want to use them literally, you need to e - `?` = `%3F` (required only in paths) - `#` = `%23` -Of course, there's others you have to escape per the URL spec, like spaces. But as already noted, `encodeURIComponent` does that for you, and Mithril uses that implicitly when you substitute parameters. So you only really need to care if you're specifying parameters explicitly like in `m.request("https://example.com/api/user/User%20Name/:field", {params: {field: ...}})`. +Of course, there's others you have to escape per the URL spec, like spaces. But as already noted, `encodeURIComponent` does that for you, and Mithril.js uses that implicitly when you substitute parameters. So you only really need to care if you're specifying parameters explicitly like in `m.request("https://example.com/api/user/User%20Name/:field", {params: {field: ...}})`. diff --git a/docs/promise.md b/docs/promise.md index 1fe8f97ae..a881e5bca 100644 --- a/docs/promise.md +++ b/docs/promise.md @@ -1,3 +1,7 @@ + + # Promise(executor) - [Description](#description) @@ -26,7 +30,7 @@ An [ES6 Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/ A Promise is a mechanism for working with asynchronous computations. -Mithril provides a polyfill when the environment does not support Promises. The polyfill can also be referenced specifically via `m.PromisePolyfill`. +Mithril.js provides a polyfill when the environment does not support Promises. The polyfill can also be referenced specifically via `m.PromisePolyfill`. --- diff --git a/docs/redraw.md b/docs/redraw.md index f53612a57..57d3e9b85 100644 --- a/docs/redraw.md +++ b/docs/redraw.md @@ -1,3 +1,7 @@ + + # redraw() - [Description](#description) @@ -12,7 +16,7 @@ Updates the DOM after a change in the application data layer. -You DON'T need to call it if data is modified within the execution context of an event handler defined in a Mithril view, or after request completion when using `m.request`/`m.jsonp`. The [autoredraw](autoredraw.md) system, which is built on top of `m.redraw()` will take care of it. +You DON'T need to call it if data is modified within the execution context of an event handler defined in a Mithril.js view, or after request completion when using `m.request`/`m.jsonp`. The [autoredraw](autoredraw.md) system, which is built on top of `m.redraw()` will take care of it. You DO need to call it in `setTimeout`/`setInterval`/`requestAnimationFrame` callbacks, or callbacks from 3rd party libraries. @@ -40,7 +44,7 @@ Argument | Type | Required | Description ### How it works -When callbacks outside of Mithril run, you need to notify Mithril's rendering engine that a redraw is needed. External callbacks could be `setTimeout`/`setInterval`/`requestAnimationFrame` callbacks, web socket library callbacks, event handlers in jQuery plugins, third party XHR request callbacks, etc. +When callbacks outside of Mithril.js run, you need to notify Mithril.js' rendering engine that a redraw is needed. External callbacks could be `setTimeout`/`setInterval`/`requestAnimationFrame` callbacks, web socket library callbacks, event handlers in jQuery plugins, third party XHR request callbacks, etc. To trigger a redraw, call `m.redraw()`. Note that `m.redraw` only works if you used `m.mount` or `m.route`. If you rendered via `m.render`, you should use `m.render` to redraw. diff --git a/docs/releasing.md b/docs/releasing.md index 47d068e33..d747eab82 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,11 +1,15 @@ -# Mithril Release Processes + + +# Mithril.js Release Processes **Note** These steps all assume that `MithrilJS/mithril.js` is a git remote named `mithriljs`, adjust accordingly if that doesn't match your setup. -- [Releasing a new Mithril version](#releasing-a-new-mithril-version) +- [Releasing a new Mithril.js version](#releasing-a-new-mithriljs-version) - [Updating mithril.js.org](#updating-mithriljsorg) -## Releasing a new Mithril version +## Releasing a new Mithril.js version ### Prepare the release diff --git a/docs/render.md b/docs/render.md index 1e4b4dd50..428179057 100644 --- a/docs/render.md +++ b/docs/render.md @@ -1,3 +1,7 @@ + + # render(element, vnodes) - [Description](#description) @@ -59,7 +63,7 @@ In contrast, traversing a JavaScript data structure has a much more predictable `m.render()` method is internally called by [`m.mount()`](mount.md), [`m.route()`](route.md), [`m.redraw()`](redraw.md) and [`m.request()`](request.md). It is not called after [stream updates](stream.md) -Unlike with `m.mount()` and `m.route()`, a vnode tree rendered via `m.render()` does not auto-redraw in response to view events, `m.redraw()` calls or `m.request()` calls. It is a low level mechanism suitable for library authors who wish to manually control rendering instead of relying on Mithril's built-in auto-redrawing system. +Unlike with `m.mount()` and `m.route()`, a vnode tree rendered via `m.render()` does not auto-redraw in response to view events, `m.redraw()` calls or `m.request()` calls. It is a low level mechanism suitable for library authors who wish to manually control rendering instead of relying on Mithril.js' built-in auto-redrawing system. Another difference is that `m.render` method expects a [vnode](vnodes.md) (or a array of vnodes) as its second parameter, whereas `m.mount()` and `m.route()` expect components. @@ -69,7 +73,7 @@ Another difference is that `m.render` method expects a [vnode](vnodes.md) (or a `var render = require("mithril/render")` -The `m.render` module is similar in scope to view libraries like Knockout, React and Vue. It 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 aside from normalization exposed via `require("mithril/render/vnode")` and can be used as a standalone library. +The `m.render` module is similar in scope to view libraries like Knockout, React and Vue. It 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.js aside from normalization exposed via `require("mithril/render/vnode")` and can be used as a standalone library. Despite being relatively small, the render module is fully functional and self-sufficient. 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). diff --git a/docs/request.md b/docs/request.md index 90a172cbd..8f36ad239 100644 --- a/docs/request.md +++ b/docs/request.md @@ -1,3 +1,7 @@ + + # request(options) - [Description](#description) @@ -360,14 +364,14 @@ function upload(e) { xhr.upload.addEventListener("progress", function(e) { progress = e.loaded / e.total - m.redraw() // tell Mithril that data changed and a re-render is needed + m.redraw() // tell Mithril.js that data changed and a re-render is needed }) } }) } ``` -In the example above, a file input is rendered. If the user picks a file, an upload is initiated, and in the `config` callback, a `progress` event handler is registered. This event handler is fired whenever there's a progress update in the XMLHttpRequest. Because the XMLHttpRequest's progress event is not directly handled by Mithril's virtual DOM engine, `m.redraw()` must be called to signal to Mithril that data has changed and a redraw is required. +In the example above, a file input is rendered. If the user picks a file, an upload is initiated, and in the `config` callback, a `progress` event handler is registered. This event handler is fired whenever there's a progress update in the XMLHttpRequest. Because the XMLHttpRequest's progress event is not directly handled by Mithril.js' virtual DOM engine, `m.redraw()` must be called to signal to Mithril.js that data has changed and a redraw is required. --- @@ -375,7 +379,7 @@ In the example above, a file input is rendered. If the user picks a file, an upl Depending on the overall application architecture, it may be desirable to transform the response data of a request to a specific class or type (for example, to uniformly parse date fields or to have helper methods). -You can pass a constructor as the `options.type` parameter and Mithril will instantiate it for each object in the HTTP response. +You can pass a constructor as the `options.type` parameter and Mithril.js will instantiate it for each object in the HTTP response. ```javascript function User(data) { @@ -398,7 +402,7 @@ In the example above, assuming `/api/v1/users` returns an array of objects, the ### Non-JSON responses -Sometimes a server endpoint does not return a JSON response: for example, you may be requesting an HTML file, an SVG file, or a CSV file. By default Mithril attempts to parse a response as if it was JSON. To override that behavior, define a custom `options.deserialize` function: +Sometimes a server endpoint does not return a JSON response: for example, you may be requesting an HTML file, an SVG file, or a CSV file. By default Mithril.js attempts to parse a response as if it was JSON. To override that behavior, define a custom `options.deserialize` function: ```javascript m.request({ @@ -454,7 +458,7 @@ m.request({ ### Retrieving response details -By default Mithril attempts to parse `xhr.responseText` as JSON and returns the parsed object. It may be useful to inspect a server response in more detail and process it manually. This can be accomplished by passing a custom `options.extract` function: +By default Mithril.js attempts to parse `xhr.responseText` as JSON and returns the parsed object. It may be useful to inspect a server response in more detail and process it manually. This can be accomplished by passing a custom `options.extract` function: ```javascript m.request({ @@ -475,9 +479,9 @@ The parameter to `options.extract` is the XMLHttpRequest object once its operati Many server-side frameworks provide a view engine that interpolates database data into a template before serving HTML (on page load or via AJAX) and then employ jQuery to handle user interactions. -By contrast, Mithril is framework designed for thick client applications, which typically download templates and data separately and combine them in the browser via JavaScript. Doing the templating heavy-lifting in the browser can bring benefits like reducing operational costs by freeing server resources. Separating templates from data also allow template code to be cached more effectively and enables better code reusability across different types of clients (e.g. desktop, mobile). Another benefit is that Mithril enables a [retained mode](https://en.wikipedia.org/wiki/Retained_mode) UI development paradigm, which greatly simplifies development and maintenance of complex user interactions. +By contrast, Mithril.js is framework designed for thick client applications, which typically download templates and data separately and combine them in the browser via JavaScript. Doing the templating heavy-lifting in the browser can bring benefits like reducing operational costs by freeing server resources. Separating templates from data also allow template code to be cached more effectively and enables better code reusability across different types of clients (e.g. desktop, mobile). Another benefit is that Mithril.js enables a [retained mode](https://en.wikipedia.org/wiki/Retained_mode) UI development paradigm, which greatly simplifies development and maintenance of complex user interactions. -By default, `m.request` expects response data to be in JSON format. In a typical Mithril application, that JSON data is then usually consumed by a view. +By default, `m.request` expects response data to be in JSON format. In a typical Mithril.js application, that JSON data is then usually consumed by a view. You should avoid trying to render server-generated dynamic HTML with Mithril. If you have an existing application that does use a server-side templating system, and you wish to re-architecture it, first decide whether the effort is feasible at all to begin with. Migrating from a thick server architecture to a thick client architecture is typically a somewhat large effort, and involves refactoring logic out of templates into logical data services (and the testing that goes with it). @@ -489,18 +493,18 @@ Data services may be organized in many different ways depending on the nature of [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) is a newer Web API for fetching resources from servers, similar to `XMLHttpRequest`. -Mithril's `m.request` uses `XMLHttpRequest` instead of `fetch()` for a number of reasons: +Mithril.js' `m.request` uses `XMLHttpRequest` instead of `fetch()` for a number of reasons: - `fetch` is not fully standardized yet, and may be subject to specification changes. - `XMLHttpRequest` calls can be aborted before they resolve (e.g. to avoid race conditions in for instant search UIs). - `XMLHttpRequest` provides hooks for progress listeners for long running requests (e.g. file uploads). - `XMLHttpRequest` is supported by all browsers, whereas `fetch()` is not supported by Internet Explorer and older Android (prior to 5.0 Lollipop). -Currently, due to lack of browser support, `fetch()` typically requires a [polyfill](https://github.com/github/fetch), which is over 11kb uncompressed - nearly three times larger than Mithril's XHR module. +Currently, due to lack of browser support, `fetch()` typically requires a [polyfill](https://github.com/github/fetch), which is over 11kb uncompressed - nearly three times larger than Mithril.js' XHR module. -Despite being much smaller, Mithril's XHR module supports many important and not-so-trivial-to-implement features like [URL interpolation](#dynamic-urls), querystring serialization and [JSON-P requests](jsonp.md), in addition to its ability to integrate seamlessly to Mithril's autoredrawing subsystem. The `fetch` polyfill does not support any of those, and requires extra libraries and boilerplates to achieve the same level of functionality. +Despite being much smaller, Mithril.js' XHR module supports many important and not-so-trivial-to-implement features like [URL interpolation](#dynamic-urls), querystring serialization and [JSON-P requests](jsonp.md), in addition to its ability to integrate seamlessly to Mithril.js' autoredrawing subsystem. The `fetch` polyfill does not support any of those, and requires extra libraries and boilerplates to achieve the same level of functionality. -In addition, Mithril's XHR module is optimized for JSON-based endpoints and makes that most common case appropriately terse - i.e. `m.request(url)` - whereas `fetch` requires an additional explicit step to parse the response data as JSON: `fetch(url).then(function(response) {return response.json()})` +In addition, Mithril.js' XHR module is optimized for JSON-based endpoints and makes that most common case appropriately terse - i.e. `m.request(url)` - whereas `fetch` requires an additional explicit step to parse the response data as JSON: `fetch(url).then(function(response) {return response.json()})` The `fetch()` API does have a few technical advantages over `XMLHttpRequest` in a few uncommon cases: diff --git a/docs/route.md b/docs/route.md index a9ee5bdae..91c5fa35e 100644 --- a/docs/route.md +++ b/docs/route.md @@ -1,3 +1,7 @@ + + # route(root, defaultRoute, routes) - [Description](#description) @@ -274,7 +278,7 @@ If `onmatch` returns a promise that gets rejected, the router redirects back to ##### routeResolver.render -The `render` method is called on every redraw for a matching route. It is similar to the `view` method in components and it exists to simplify [component composition](#wrapping-a-layout-component). It also lets you escape from Mithril's normal behavior of replacing the entire subtree. +The `render` method is called on every redraw for a matching route. It is similar to the `view` method in components and it exists to simplify [component composition](#wrapping-a-layout-component). It also lets you escape from Mithril.js' normal behavior of replacing the entire subtree. `vnode = routeResolver.render(vnode)` @@ -372,7 +376,7 @@ In the example above, the `Menu` component has two `m.route.Link`s. That creates You can also navigate programmatically, via `m.route.set(route)`. For example, `m.route.set("/page1")`. -When navigating between routes, the router prefix is handled for you. In other words, leave out the hashbang `#!` (or whatever prefix you set `m.route.prefix` to) when linking Mithril routes, including in both `m.route.set` and in `m.route.Link`. +When navigating between routes, the router prefix is handled for you. In other words, leave out the hashbang `#!` (or whatever prefix you set `m.route.prefix` to) when linking Mithril.js routes, including in both `m.route.set` and in `m.route.Link`. Do note that when navigating between components, the entire subtree is replaced. Use [a route resolver with a `render` method](#routeresolverrender) if you want to just patch the subtree. @@ -380,7 +384,7 @@ Do note that when navigating between components, the entire subtree is replaced. ### Routing parameters -Sometimes we want to have a variable id or similar data appear in a route, but we don't want to explicitly specify a separate route for every possible id. In order to achieve that, Mithril supports [parameterized routes](paths.md#path-parameters): +Sometimes we want to have a variable id or similar data appear in a route, but we don't want to explicitly specify a separate route for every possible id. In order to achieve that, Mithril.js supports [parameterized routes](paths.md#path-parameters): ```javascript var Edit = { @@ -438,7 +442,7 @@ m.route(document.body, "/edit/pictures/image.jpg", { For isomorphic / universal JavaScript app, an url param and a variadic route combined is very useful to display custom 404 error page. -In a case of 404 Not Found error, the server send back the custom page to client. When Mithril is loaded, it will redirect client to the default route because it can't know that route. +In a case of 404 Not Found error, the server send back the custom page to client. When Mithril.js is loaded, it will redirect client to the default route because it can't know that route. ```javascript m.route(document.body, "/", { @@ -771,7 +775,7 @@ Above, `render` only runs after the request completes, making the ternary operat #### Code splitting -In a large application, it may be desirable to download the code for each route on demand, rather than upfront. Dividing the codebase this way is known as code splitting or lazy loading. In Mithril, this can be accomplished by returning a promise from the `onmatch` hook: +In a large application, it may be desirable to download the code for each route on demand, rather than upfront. Dividing the codebase this way is known as code splitting or lazy loading. In Mithril.js, this can be accomplished by returning a promise from the `onmatch` hook: At its most basic form, one could do the following: diff --git a/docs/signatures.md b/docs/signatures.md index 3d0bc04d7..7838bf266 100644 --- a/docs/signatures.md +++ b/docs/signatures.md @@ -1,3 +1,7 @@ + + # How to read signatures Signature sections typically look like this: diff --git a/docs/simple-application.md b/docs/simple-application.md index 30af19bd2..e35cfffa1 100644 --- a/docs/simple-application.md +++ b/docs/simple-application.md @@ -1,3 +1,7 @@ + + # Simple application Let's develop a simple application that shows off how to do most of the major things you would need to deal with while using Mithril. @@ -34,7 +38,7 @@ If npm is installed correctly, a file `package.json` will be created. This file --- -To install Mithril, follow the instructions in the [installation](installation.md) page. Once you have a project skeleton with Mithril installed, we are ready to create the application. +To install Mithril.js, follow the instructions in the [installation](installation.md) page. Once you have a project skeleton with Mithril.js installed, we are ready to create the application. Let's start by creating a module to store our state. Let's create a file called `src/models/User.js` @@ -47,7 +51,7 @@ var User = { module.exports = User ``` -Now let's add code to load some data from a server. To communicate with a server, we can use Mithril's XHR utility, `m.request`. First, we include Mithril in the module: +Now let's add code to load some data from a server. To communicate with a server, we can use Mithril.js' XHR utility, `m.request`. First, we include Mithril.js in the module: ```javascript // src/models/User.js @@ -103,7 +107,7 @@ module.exports = User The `method` option is an [HTTP method](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods). To retrieve data from the server without causing side-effects on the server, we need to use the `GET` method. The `url` is the address for the API endpoint. The `withCredentials: true` line indicates that we're using cookies (which is a requirement for the REM API). -The `m.request` call returns a Promise that resolves to the data from the endpoint. By default, Mithril assumes a HTTP response body are in JSON format and automatically parses it into a JavaScript object or array. The `.then` callback runs when the XHR request completes. In this case, the callback assigns the `result.data` array to `User.list`. +The `m.request` call returns a Promise that resolves to the data from the endpoint. By default, Mithril.js assumes a HTTP response body are in JSON format and automatically parses it into a JavaScript object or array. The `.then` callback runs when the XHR request completes. In this case, the callback assigns the `result.data` array to `User.list`. Notice we also have a `return` statement in `loadList`. This is a general good practice when working with Promises, which allows us to register more callbacks to run after the completion of the XHR request. @@ -113,7 +117,7 @@ This simple model exposes two members: `User.list` (an array of user objects), a Now, let's create a view module so that we can display data from our User model module. -Create a file called `src/views/UserList.js`. First, let's include Mithril and our model, since we'll need to use both: +Create a file called `src/views/UserList.js`. First, let's include Mithril.js and our model, since we'll need to use both: ```javascript // src/views/UserList.js @@ -121,7 +125,7 @@ var m = require("mithril") var User = require("../models/User") ``` -Next, let's create a Mithril component. A component is simply an object that has a `view` method: +Next, let's create a Mithril.js component. A component is simply an object that has a `view` method: ```javascript // src/views/UserList.js @@ -135,7 +139,7 @@ module.exports = { } ``` -By default, Mithril views are described using [hyperscript](hyperscript.md). Hyperscript offers a terse syntax that can be indented more naturally than HTML for complex tags, and since its syntax is just JavaScript, it's possible to leverage a lot of JavaScript tooling ecosystem. For example: +By default, Mithril.js views are described using [hyperscript](hyperscript.md). Hyperscript offers a terse syntax that can be indented more naturally than HTML for complex tags, and since its syntax is just JavaScript, it's possible to leverage a lot of JavaScript tooling ecosystem. For example: - You can use [Babel](es6.md) to transpile ES6+ to ES5 for IE and to transpile [JSX](jsx.md) (an inline HTML-like syntax extension) to appropriate hyperscript calls. - You can use [ESLint](https://eslint.org/) for easy linting with no special plugins. @@ -143,7 +147,7 @@ By default, Mithril views are described using [hyperscript](hyperscript.md). Hyp - You can use [Istanbul](https://github.com/istanbuljs/nyc) for code coverage. - You can use [TypeScript](https://www.typescriptlang.org/) for easy code analysis. (There are [community-supported type definitions available](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mithril), so you don't need to roll your own.) -Let's start off with hyperscript and create a list of items. Hyperscript is the idiomatic way to use Mithril, but [JSX](jsx.md) works pretty similarly. +Let's start off with hyperscript and create a list of items. Hyperscript is the idiomatic way to use Mithril.js, but [JSX](jsx.md) works pretty similarly. ```javascript // src/views/UserList.js @@ -262,7 +266,7 @@ Reloading the browser window now should display some styled elements. Let's add routing to our application. -Routing means binding a screen to a unique URL, to create the ability to go from one "page" to another. Mithril is designed for Single Page Applications, so these "pages" aren't necessarily different HTML files in the traditional sense of the word. Instead, routing in Single Page Applications retains the same HTML file throughout its lifetime, but changes the state of the application via JavaScript. Client side routing has the benefit of avoiding flashes of blank screen between page transitions, and can reduce the amount of data being sent down from the server when used in conjunction with an web service oriented architecture (i.e. an application that downloads data as JSON instead of downloading pre-rendered chunks of verbose HTML). +Routing means binding a screen to a unique URL, to create the ability to go from one "page" to another. Mithril.js is designed for Single Page Applications, so these "pages" aren't necessarily different HTML files in the traditional sense of the word. Instead, routing in Single Page Applications retains the same HTML file throughout its lifetime, but changes the state of the application via JavaScript. Client side routing has the benefit of avoiding flashes of blank screen between page transitions, and can reduce the amount of data being sent down from the server when used in conjunction with an web service oriented architecture (i.e. an application that downloads data as JSON instead of downloading pre-rendered chunks of verbose HTML). We can add routing by changing the `m.mount` call to a `m.route` call: @@ -729,4 +733,4 @@ This concludes the tutorial. In this tutorial, we went through the process of creating a very simple application where we can list users from a server and edit them individually. As an extra exercise, try to implement user creation and deletion on your own. -If you want to see more examples of Mithril code, check the [examples](examples.md) page. If you have questions, feel free to drop by the [Mithril chat room](https://gitter.im/MithrilJS/mithril.js). +If you want to see more examples of Mithril.js code, check the [examples](examples.md) page. If you have questions, feel free to drop by the [Mithril.js chat room](https://gitter.im/MithrilJS/mithril.js). diff --git a/docs/stream.md b/docs/stream.md index c53f184f2..da1eac403 100644 --- a/docs/stream.md +++ b/docs/stream.md @@ -1,3 +1,7 @@ + + # stream() - [Description](#description) @@ -37,7 +41,7 @@ For example, in a spreadsheet, if `A1 = B1 + C1`, then changing the value of `B1 Similarly, you can make a stream depend on other streams so that changing the value of one automatically updates the other. This is useful when you have very expensive computations and want to only run them when necessary, as opposed to, say, on every redraw. -Streams are NOT bundled with Mithril's core distribution. To include the Streams module, use: +Streams are NOT bundled with Mithril.js' core distribution. To include the Streams module, use: ```javascript var Stream = require("mithril/stream") @@ -49,7 +53,7 @@ You can also download the module directly if your environment does not support a ``` -When loaded directly with a `" @@ -99,7 +103,7 @@ Even though there are many obscure ways to make an HTML string run JavaScript, ` For historical reasons, browsers ignore `