diff --git a/README.md b/README.md index 9ffb79e2..0370747b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -mithril.js [![NPM Version](https://img.shields.io/npm/v/mithril.svg)](https://www.npmjs.com/package/mithril) [![NPM License](https://img.shields.io/npm/l/mithril.svg)](https://www.npmjs.com/package/mithril) [![NPM Downloads](https://img.shields.io/npm/dm/mithril.svg)](https://www.npmjs.com/package/mithril) [![Donate at OpenCollective](https://img.shields.io/opencollective/all/mithriljs.svg?colorB=brightgreen)](https://opencollective.com/mithriljs) +mithril.js [![npm Version](https://img.shields.io/npm/v/mithril.svg)](https://www.npmjs.com/package/mithril) [![npm License](https://img.shields.io/npm/l/mithril.svg)](https://www.npmjs.com/package/mithril) [![npm Downloads](https://img.shields.io/npm/dm/mithril.svg)](https://www.npmjs.com/package/mithril) [![Donate at OpenCollective](https://img.shields.io/opencollective/all/mithriljs.svg?colorB=brightgreen)](https://opencollective.com/mithriljs) ==========

@@ -29,17 +29,19 @@ Mithril supports IE11, Firefox ESR, and the last two versions of Firefox, Edge, ### CDN ```html - - - + + + + + + + ``` ### npm + ```bash -# For the most recent stable version -$ npm install mithril --save -# For the most recent unstable version -$ npm install mithril@next --save +npm install mithril --save ``` The ["Getting started" guide](https://mithril.js.org/#getting-started) is a good place to start learning how to use mithril. diff --git a/docs/animation.md b/docs/animation.md index 1f7b0b92..e25f5c47 100644 --- a/docs/animation.md +++ b/docs/animation.md @@ -9,7 +9,7 @@ ### Technology choices -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](http://velocityjs.org/) 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. +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://velocityjs.org/) 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. @@ -102,4 +102,4 @@ Note that the `onbeforeremove` hook only fires on the element that loses its `pa When creating animations, it's recommended that you only use the `opacity` and `transform` CSS rules, since these can be hardware-accelerated by modern browsers and yield better performance than animating `top`, `left`, `width`, and `height`. -It's also recommended that you avoid the `box-shadow` rule and selectors like `:nth-child`, since these are also resource intensive options. If you want to animate a `box-shadow`, consider [putting the `box-shadow` rule on a pseudo element, and animate that element's opacity instead](http://tobiasahlin.com/blog/how-to-animate-box-shadow/). Other things that can be expensive include large or dynamically scaled images and overlapping elements with different `position` values (e.g. an absolute positioned element over a fixed element). +It's also recommended that you avoid the `box-shadow` rule and selectors like `:nth-child`, since these are also resource intensive options. If you want to animate a `box-shadow`, consider [putting the `box-shadow` rule on a pseudo element, and animate that element's opacity instead](https://tobiasahlin.com/blog/how-to-animate-box-shadow/). Other things that can be expensive include large or dynamically scaled images and overlapping elements with different `position` values (e.g. an absolute positioned element over a fixed element). diff --git a/docs/api.md b/docs/api.md index 536931fc..2f946a19 100644 --- a/docs/api.md +++ b/docs/api.md @@ -41,7 +41,7 @@ var Home = { } m.route(document.body, "/home", { - "/home": Home, // defines `http://localhost/#!/home` + "/home": Home, // defines `https://example.com/#!/home` }) ``` diff --git a/docs/change-log.md b/docs/change-log.md index a75f0a63..07b85641 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -1,17 +1,11 @@ # Change log -- [v2.0.0-rc](#v200rc) -- [v1.1.6](#v116) -- [v1.1.5](#v115) -- [v1.1.4](#v114) -- [v1.1.3](#v113) -- [v1.1.2](#v112) -- [v1.1.1](#v111) -- [v1.1.0](#v110) -- [v1.0.1](#v101) -- [Migrating from v0.2.x](#migrating-from-v02x) -- [v1.x docs](http://mithril.js.org/archive/v1.1.6/index.html) -- [v0.2 docs](http://mithril.js.org/archive/v0.2.5/index.html) +- [v2.0.0](#v200) +- [Migrating from v1.x](migration-v1x.md) +- [Migrating from v0.2.x](migration-v02x.md) +- [v1.x changelog](https://mithril.js.org/archive/v1.1.6/change-log.html) +- [v1.x docs](https://mithril.js.org/archive/v1.1.6/index.html) +- [v0.2 docs](https://mithril.js.org/archive/v0.2.5/index.html) - [`ospec` change log](https://github.com/MithrilJS/mithril.js/blob/master/ospec/change-log.md) - [`mithril/stream` change log](https://github.com/MithrilJS/mithril.js/blob/master/stream/change-log.md) @@ -19,7 +13,7 @@ ### Upcoming... -### v2.0.0-rc +### v2.0.0 #### Breaking changes @@ -31,9 +25,8 @@ - hyperscript: when attributes have a `null` or `undefined` value, they are treated as if they were absent. [#1773](https://github.com/MithrilJS/mithril.js/issues/1773) ([#2174](https://github.com/MithrilJS/mithril.js/pull/2174)) - API: `m.request` errors no longer copy response fields to the error, but instead assign the parsed JSON response to `error.response` and the HTTP status code `error.code`. - hyperscript: when an attribute is defined on both the first and second argument (as a CSS selector and an `attrs` field, respectively), the latter takes precedence, except for `class` attributes that are still added together. [#2172](https://github.com/MithrilJS/mithril.js/issues/2172) ([#2174](https://github.com/MithrilJS/mithril.js/pull/2174)) -- render: Align custom elements to work like normal elements, minus all the HTML-specific magic. ([#2221](https://github.com/MithrilJS/mithril.js/pull/2221)) - cast className using toString ([#2309](https://github.com/MithrilJS/mithril.js/pull/2309)) -- render: call attrs' hooks first, with express exception of `onbeforeupdate` to allow attrs to block components from even diffing ([#2297](https://github.com/MithrilJS/mithril.js/pull/2297)) +- render: call attrs' hooks last, with express exception of `onbeforeupdate` to allow attrs to block components from even diffing ([#2297](https://github.com/MithrilJS/mithril.js/pull/2297)) - API: `m.withAttr` removed. ([#2317](https://github.com/MithrilJS/mithril.js/pull/2317)) - request: `data` has now been split to `params` and `body` and `useBody` has been removed in favor of just using `body`. ([#2361](https://github.com/MithrilJS/mithril.js/pull/2361)) - route, request: Interpolated arguments are URL-escaped (and for declared routes, URL-unescaped) automatically. If you want to use a raw route parameter, use a variadic parameter like in `/asset/:path.../view`. This was previously only available in `m.route` route definitions, but it's now usable in both that and where paths are accepted. ([#2361](https://github.com/MithrilJS/mithril.js/pull/2361)) @@ -69,11 +62,6 @@ - The new component handles many more edge cases around user interaction, including accessibility. - Link navigation can be disabled and cancelled. - Link targets can be trivially changed. -- API: Full DOM no longer required to execute `require("mithril")`. You just need to set the necessary globals to *something*, even if `null` or `undefined`, so they can be properly used. ([#2469](https://github.com/MithrilJS/mithril.js/pull/2469) [@isiahmeadows](https://github.com/isiahmeadows)) - - This enables isomorphic use of `m.route.Link` and `m.route.prefix`. - - This enables isomorphic use of `m.request`, provided the `background: true` option is set and that an `XMLHttpRequest` polyfill is included as necessary. - - Note that methods requiring DOM operations will still throw errors, such as `m.render(...)`, `m.redraw()`, and `m.route(...)`. - #### News @@ -100,6 +88,11 @@ - route: Use `m.mount(root, null)` to unsubscribe and clean up after a `m.route(root, ...)` call. ([#2453](https://github.com/MithrilJS/mithril.js/pull/2453)) - render: new `redraw` parameter exposed any time a child event handler is used ([#2458](https://github.com/MithrilJS/mithril.js/pull/2458) [@isiahmeadows](https://github.com/isiahmeadows)) - route: `m.route.SKIP` can be returned from route resolvers to skip to the next route ([#2469](https://github.com/MithrilJS/mithril.js/pull/2469) [@isiahmeadows](https://github.com/isiahmeadows)) +- API: Full DOM no longer required to execute `require("mithril")`. You just need to set the necessary globals to *something*, even if `null` or `undefined`, so they can be properly used. ([#2469](https://github.com/MithrilJS/mithril.js/pull/2469) [@isiahmeadows](https://github.com/isiahmeadows)) + - This enables isomorphic use of `m.route.Link` and `m.route.prefix`. + - This enables isomorphic use of `m.request`, provided the `background: true` option is set and that an `XMLHttpRequest` polyfill is included as necessary. + - Note that methods requiring DOM operations will still throw errors, such as `m.render(...)`, `m.redraw()`, and `m.route(...)`. +- render: Align custom elements to work like normal elements, minus all the HTML-specific magic. ([#2221](https://github.com/MithrilJS/mithril.js/pull/2221)) #### Bug fixes @@ -135,826 +128,3 @@ - docs: clarify valid key usage ([#2452](https://github.com/MithrilJS/mithril.js/pull/2452) [@isiahmeadows](https://github.com/isiahmeadows)) - route: don't pollute globals ([#2453](https://github.com/MithrilJS/mithril.js/pull/2453) [@isiahmeadows](https://github.com/isiahmeadows)) - request: track xhr replacements correctly ([#2455](https://github.com/MithrilJS/mithril.js/pull/2455) [@isiahmeadows](https://github.com/isiahmeadows)) - ---- - - -### v1.2.0 - -#### News - -- Promise polyfill implementation separated from polyfilling logic. -- `PromisePolyfill` is now available on the exported/global `m`. - -#### Bug fixes - -- core: Workaround for [Internet Explorer bug](https://www.tjvantoll.com/2013/08/30/bugs-with-document-activeelement-in-internet-explorer/) when running in an iframe - -#### Note - -- Stream references no longer magically coerce to their underlying values ([#2150](https://github.com/MithrilJS/mithril.js/pull/2150), stream breaking change: `mithril-stream@2.0.0`) - ---- - -### v1.1.6 - -#### Bug fixes - -- core: render() function can no longer prevent from changing `document.activeElement` in lifecycle hooks ([#1988](https://github.com/MithrilJS/mithril.js/pull/1988), [@purplecode](https://github.com/purplecode)) -- core: don't call `onremove` on the children of components that return null from the view [#1921](https://github.com/MithrilJS/mithril.js/issues/1921) [@octavore](https://github.com/octavore) ([#1922](https://github.com/MithrilJS/mithril.js/pull/1922)) -- hypertext: correct handling of shared attributes object passed to `m()`. Will copy attributes when it's necessary [#1941](https://github.com/MithrilJS/mithril.js/issues/1941) [@s-ilya](https://github.com/s-ilya) ([#1942](https://github.com/MithrilJS/mithril.js/pull/1942)) - -#### Ospec improvements - -- ospec v1.4.0 - - Added support for async functions and promises in tests ([#1928](https://github.com/MithrilJS/mithril.js/pull/1928), [@StephanHoyer](https://github.com/StephanHoyer)) - - Error handling for async tests with `done` callbacks supports error as first argument ([#1928](https://github.com/MithrilJS/mithril.js/pull/1928)) - - Error messages which include newline characters do not swallow the stack trace [#1495](https://github.com/MithrilJS/mithril.js/issues/1495) ([#1984](https://github.com/MithrilJS/mithril.js/pull/1984), [@RodericDay](https://github.com/RodericDay)) -- ospec v2.0.0 (to be released) - - Added support for custom reporters ([#2009](https://github.com/MithrilJS/mithril.js/pull/2020)) - - Make Ospec more [Flems](https://flems.io)-friendly ([#2034](https://github.com/MithrilJS/mithril.js/pull/2034)) - - Works either as a global or in CommonJS environments - - the o.run() report is always printed asynchronously (it could be synchronous before if none of the tests were async). - - Properly point to the assertion location of async errors [#2036](https://github.com/MithrilJS/mithril.js/issues/2036) - - expose the default reporter as `o.report(results)` - - Don't try to access the stack traces in IE9 - ---- - -### v1.1.5 - -#### Bug fixes - -- API: If a user sets the Content-Type header within a request's options, that value will be the entire header value rather than being appended to the default value [#1919](https://github.com/MithrilJS/mithril.js/issues/1919) ([#1924](https://github.com/MithrilJS/mithril.js/pull/1924), [@tskillian](https://github.com/tskillian)) - ---- - -### v1.1.4 - -#### Bug fixes - -- Fix IE bug where active element is null causing render function to throw error ([#1943](https://github.com/MithrilJS/mithril.js/pull/1943), [@JacksonJN](https://github.com/JacksonJN)) - -#### Ospec improvements: - -- Log using util.inspect to show object content instead of "[object Object]" ([#1661](https://github.com/MithrilJS/mithril.js/issues/1661), [@porsager](https://github.com/porsager)) - ---- - -### v1.1.3 - -#### Bug fixes - -- move out npm dependencies added by mistake - ---- - -### v1.1.2 - -#### Bug fixes - -- core: Namespace fixes [#1819](https://github.com/MithrilJS/mithril.js/issues/1819), ([#1825](https://github.com/MithrilJS/mithril.js/pull/1825) [@SamuelTilly](https://github.com/SamuelTilly)), [#1820](https://github.com/MithrilJS/mithril.js/issues/1820) ([#1864](https://github.com/MithrilJS/mithril.js/pull/1864)), [#1872](https://github.com/MithrilJS/mithril.js/issues/1872) ([#1873](https://github.com/MithrilJS/mithril.js/pull/1873)) -- core: Fix select option to allow empty string value [#1814](https://github.com/MithrilJS/mithril.js/issues/1814) ([#1828](https://github.com/MithrilJS/mithril.js/pull/1828) [@spacejack](https://github.com/spacejack)) -- core: Reset e.redraw when it was set to `false` [#1850](https://github.com/MithrilJS/mithril.js/issues/1850) ([#1890](https://github.com/MithrilJS/mithril.js/pull/1890)) -- core: differentiate between `{ value: "" }` and `{ value: 0 }` for form elements [#1595 comment](https://github.com/MithrilJS/mithril.js/pull/1595#issuecomment-304071453) ([#1862](https://github.com/MithrilJS/mithril.js/pull/1862)) -- core: Don't reset the cursor of textareas in IE10 when setting an identical `value` [#1870](https://github.com/MithrilJS/mithril.js/issues/1870) ([#1871](https://github.com/MithrilJS/mithril.js/pull/1871)) -- hypertext: Correct handling of `[value=""]` ([#1843](https://github.com/MithrilJS/mithril.js/issues/1843), [@CreaturesInUnitards](https://github.com/CreaturesInUnitards)) -- router: Don't overwrite the options object when redirecting from `onmatch with m.route.set()` [#1857](https://github.com/MithrilJS/mithril.js/issues/1857) ([#1889](https://github.com/MithrilJS/mithril.js/pull/1889)) -- stream: Move the "use strict" directive inside the IIFE [#1831](https://github.com/MithrilJS/mithril.js/issues/1831) ([#1893](https://github.com/MithrilJS/mithril.js/pull/1893)) - ---- - -#### Docs / Repo maintenance - -Our thanks to [@0joshuaolson1](https://github.com/0joshuaolson1), [@ACXgit](https://github.com/ACXgit), [@cavemansspa](https://github.com/cavemansspa), [@CreaturesInUnitards](https://github.com/CreaturesInUnitards), [@dlepaux](https://github.com/dlepaux), [@isaaclyman](https://github.com/isaaclyman), [@kevinkace](https://github.com/kevinkace), [@micellius](https://github.com/micellius), [@spacejack](https://github.com/spacejack) and [@yurivish](https://github.com/yurivish) - -#### Other - -- Addition of a performance regression test suite ([#1789](https://github.com/MithrilJS/mithril.js/issues/1789)) - ---- - -### v1.1.1 - -#### Bug fixes - -- hyperscript: Allow `0` as the second argument to `m()` - [#1752](https://github.com/MithrilJS/mithril.js/issues/1752) / [#1753](https://github.com/MithrilJS/mithril.js/pull/1753) ([@StephanHoyer](https://github.com/StephanHoyer)) -- hyperscript: restore `attrs.class` handling to what it was in v1.0.1 - [#1764](https://github.com/MithrilJS/mithril.js/issues/1764) / [#1769](https://github.com/MithrilJS/mithril.js/pull/1769) -- documentation improvements ([@JAForbes](https://github.com/JAForbes), [@smuemd](https://github.com/smuemd), [@hankeypancake](https://github.com/hankeypancake)) - ---- - -### v1.1.0 - -#### News - -- support for ES6 class components -- support for closure components -- improvements in build and release automation - -#### Bug fixes - -- fix IE11 input[type] error - [#1610](https://github.com/MithrilJS/mithril.js/issues/1610) -- apply [#1609](https://github.com/MithrilJS/mithril.js/issues/1609) to unkeyed children case -- fix abort detection [#1612](https://github.com/MithrilJS/mithril.js/issues/1612) -- fix input value focus issue when value is loosely equal to old value [#1593](https://github.com/MithrilJS/mithril.js/issues/1593) - ---- - -### v1.0.1 - -#### News - -- performance improvements in IE [#1598](https://github.com/MithrilJS/mithril.js/pull/1598) - -#### Bug fixes - -- prevent infinite loop in non-existent default route - [#1579](https://github.com/MithrilJS/mithril.js/issues/1579) -- call correct lifecycle methods on children of recycled keyed vnodes - [#1609](https://github.com/MithrilJS/mithril.js/issues/1609) - ---- - -### Migrating from `v0.2.x` - -`v1.x` is largely API-compatible with `v0.2.x`, but there are some breaking changes. - -If you are migrating, consider using the [mithril-codemods](https://www.npmjs.com/package/mithril-codemods) tool to help automate the most straightforward migrations. - -- [`m.prop` removed](#mprop-removed) -- [`m.component` removed](#mcomponent-removed) -- [`config` function](#config-function) -- [Changes in redraw behaviour](#changes-in-redraw-behaviour) - - [No more redraw locks](#no-more-redraw-locks) - - [Cancelling redraw from event handlers](#cancelling-redraw-from-event-handlers) - - [Synchronous redraw removed](#synchronous-redraw-removed) - - [`m.startComputation`/`m.endComputation` removed](#mstartcomputationmendcomputation-removed) -- [Component `controller` function](#component-controller-function) -- [Component arguments](#component-arguments) -- [`view()` parameters](#view-parameters) -- [Passing components to `m()`](#passing-components-to-m) -- [Passing vnodes to `m.mount()` and `m.route()`](#passing-vnodes-to-mmount-and-mroute) -- [`m.route.mode`](#mroutemode) -- [`m.route` and anchor tags](#mroute-and-anchor-tags) -- [Reading/writing the current route](#readingwriting-the-current-route) -- [Accessing route params](#accessing-route-params) -- [Building/Parsing query strings](#buildingparsing-query-strings) -- [Preventing unmounting](#preventing-unmounting) -- [Run code on component removal](#run-code-on-component-removal) -- [`m.request`](#mrequest) -- [`m.deferred` removed](#mdeferred-removed) -- [`m.sync` removed](#msync-removed) -- [`xlink` namespace required](#xlink-namespace-required) -- [Nested arrays in views](#nested-arrays-in-views) -- [`vnode` equality checks](#vnode-equality-checks) - ---- - -## `m.prop` removed - -In `v1.x`, `m.prop()` is now a more powerful stream micro-library, but it's no longer part of core. You can read about how to use the optional Streams module in [the documentation](stream.md). - -### `v0.2.x` - -```javascript -var m = require("mithril") - -var num = m.prop(1) -``` - -### `v1.x` - -```javascript -var m = require("mithril") -var prop = require("mithril/stream") - -var num = prop(1) -var doubled = num.map(function(n) {return n * 2}) -``` - ---- - -## `m.component` removed - -In `v0.2.x` components could be created using either `m(component)` or `m.component(component)`. `v1.x` only supports `m(component)`. - -### `v0.2.x` - -```javascript -// These are equivalent -m.component(component) -m(component) -``` - -### `v1.x` - -```javascript -m(component) -``` - ---- - -## `config` function - -In `v0.2.x` mithril provided a single lifecycle method, `config`. `v1.x` provides much more fine-grained control over the lifecycle of a vnode. - -### `v0.2.x` - -```javascript -m("div", { - config : function(element, isInitialized) { - // runs on each redraw - // isInitialized is a boolean representing if the node has been added to the DOM - } -}) -``` - -### `v1.x` - -More documentation on these new methods is available in [lifecycle-methods.md](lifecycle-methods.md). - -```javascript -m("div", { - // Called before the DOM node is created - oninit : function(vnode) { /*...*/ }, - // Called after the DOM node is created - oncreate : function(vnode) { /*...*/ }, - // Called before the node is updated, return false to cancel - onbeforeupdate : function(vnode, old) { /*...*/ }, - // Called after the node is updated - onupdate : function(vnode) { /*...*/ }, - // Called before the node is removed, return a Promise that resolves when - // ready for the node to be removed from the DOM - onbeforeremove : function(vnode) { /*...*/ }, - // Called before the node is removed, but after onbeforeremove calls done() - onremove : function(vnode) { /*...*/ } -}) -``` - -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: - -### 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 v1.x. 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 - -`m.mount()` and `m.route()` still automatically redraw after a DOM event handler runs. Cancelling these redraws from within your event handlers is now done by setting the `redraw` property on the passed-in event object to `false`. - -#### `v0.2.x` - -```javascript -m("div", { - onclick : function(e) { - m.redraw.strategy("none") - } -}) -``` - -#### `v1.x` - -```javascript -m("div", { - onclick : function(e) { - e.redraw = false - } -}) -``` - -### Synchronous redraw removed - -In v0.2.x it was possible to force mithril to redraw immediately by passing a truthy value to `m.redraw()`. This behavior complicated usage of `m.redraw()` and caused some hard-to-reason about issues and has been removed. - -#### `v0.2.x` - -```javascript -m.redraw(true) // redraws immediately & synchronously -``` - -#### `v1.x` - -```javascript -m.redraw() // schedules a redraw on the next requestAnimationFrame tick -``` - -### `m.startComputation`/`m.endComputation` removed - -They are considered anti-patterns and have a number of problematic edge cases, so they no longer exist in v1.x. - ---- - -## Component `controller` function - -In `v1.x` there is no more `controller` property in components, use `oninit` instead. - -### `v0.2.x` - -```javascript -m.mount(document.body, { - controller : function() { - var ctrl = this - - ctrl.fooga = 1 - }, - - view : function(ctrl) { - return m("p", ctrl.fooga) - } -}) -``` - -### `v1.x` - -```javascript -m.mount(document.body, { - oninit : function(vnode) { - vnode.state.fooga = 1 - }, - - view : function(vnode) { - return m("p", vnode.state.fooga) - } -}) - -// OR - -m.mount(document.body, { - oninit : function(vnode) { - var state = this // this is bound to vnode.state by default - - state.fooga = 1 - }, - - view : function(vnode) { - var state = this // this is bound to vnode.state by default - - return m("p", state.fooga) - } -}) -``` - ---- - -## Component arguments - -Arguments to a component in `v1.x` must be an object, simple values like `String`/`Number`/`Boolean` will be treated as text children. Arguments are accessed within the component by reading them from the `vnode.attrs` object. - -### `v0.2.x` - -```javascript -var component = { - controller : function(options) { - // options.fooga === 1 - }, - - view : function(ctrl, options) { - // options.fooga == 1 - } -} - -m("div", m.component(component, { fooga : 1 })) -``` - -### `v1.x` - -```javascript -var component = { - oninit : function(vnode) { - // vnode.attrs.fooga === 1 - }, - - view : function(vnode) { - // vnode.attrs.fooga == 1 - } -} - -m("div", m(component, { fooga : 1 })) -``` - ---- - -## `view()` parameters - -In `v0.2.x` view functions are passed a reference to the `controller` instance and (optionally) any options passed to the component. In `v1.x` they are passed **only** the `vnode`, exactly like the `controller` function. - -### `v0.2.x` - -```javascript -m.mount(document.body, { - controller : function() {}, - - view : function(ctrl, options) { - // ... - } -}) -``` - -### `v1.x` - -```javascript -m.mount(document.body, { - oninit : function(vnode) { - // ... - }, - - view : function(vnode) { - // Use vnode.state instead of ctrl - // Use vnode.attrs instead of options - } -}) -``` - ---- - -## Passing components to `m()` - -In `v0.2.x` you could pass components as the second argument of `m()` w/o any wrapping required. To help with consistency in `v1.x` they must always be wrapped with a `m()` invocation. - -### `v0.2.x` - -```javascript -m("div", component) -``` - -### `v1.x` - -```javascript -m("div", m(component)) -``` - ---- - -## Passing vnodes to `m.mount()` and `m.route()` - -In `v0.2.x`, `m.mount(element, component)` tolerated [vnodes](vnodes.md) as second arguments instead of [components](components.md) (even though it wasn't documented). Likewise, `m.route(element, defaultRoute, routes)` accepted vnodes as values in the `routes` object. - -In `v1.x`, components are required instead in both cases. - -### `v0.2.x` - -```javascript -m.mount(element, m('i', 'hello')) -m.mount(element, m(Component, attrs)) - -m.route(element, '/', { - '/': m('b', 'bye') -}) -``` - -### `v1.x` - -```javascript -m.mount(element, {view: function () {return m('i', 'hello')}}) -m.mount(element, {view: function () {return m(Component, attrs)}}) - -m.route(element, '/', { - '/': {view: function () {return m('b', 'bye')}} -}) -``` - ---- - -## `m.route.mode` - -In `v0.2.x` the routing mode could be set by assigning a string of `"pathname"`, `"hash"`, or `"search"` to `m.route.mode`. In `v.1.x` it is replaced by `m.route.prefix(prefix)` where `prefix` can be `#`, `?`, or an empty string (for "pathname" mode). The new API also supports hashbang (`#!`), which is the default, and it supports non-root pathnames and arbitrary mode variations such as querybang (`?!`) - -### `v0.2.x` - -```javascript -m.route.mode = "pathname" -m.route.mode = "search" -``` - -### `v1.x` - -```javascript -m.route.prefix("") -m.route.prefix("?") -``` - ---- - -## `m.route()` and anchor tags - -Handling clicks on anchor tags via the mithril router is similar to `v0.2.x` but uses a new lifecycle method and API. - -### `v0.2.x` - -```javascript -// When clicked this link will load the "/path" route instead of navigating -m("a", { - href : "/path", - config : m.route -}) -``` - -### `v1.x` - -```javascript -// When clicked this link will load the "/path" route instead of navigating -m("a", { - href : "/path", - oncreate : m.route.link -}) -``` - ---- - -## Reading/writing the current route - -In `v0.2.x` all interaction w/ the current route happened via `m.route()`. In `v1.x` this has been broken out into two functions. - -### `v0.2.x` - -```javascript -// Getting the current route -m.route() - -// Setting a new route -m.route("/other/route") -``` - -### `v1.x` - -```javascript -// Getting the current route -m.route.get() - -// Setting a new route -m.route.set("/other/route") -``` - ---- - -## Accessing route params - -In `v0.2.x` reading route params was entirely handled through `m.route.param()`. This API is still available in `v1.x`, and additionally any route params are passed as properties in the `attrs` object on the vnode. - -### `v0.2.x` - -```javascript -m.route(document.body, "/booga", { - "/:attr" : { - controller : function() { - m.route.param("attr") // "booga" - }, - view : function() { - m.route.param("attr") // "booga" - } - } -}) -``` - -### `v1.x` - -```javascript -m.route(document.body, "/booga", { - "/:attr" : { - oninit : function(vnode) { - vnode.attrs.attr // "booga" - m.route.param("attr") // "booga" - }, - view : function(vnode) { - vnode.attrs.attr // "booga" - m.route.param("attr") // "booga" - } - } -}) -``` - ---- - -## Building/Parsing query strings - -`v0.2.x` used methods hanging off of `m.route`, `m.route.buildQueryString()` and `m.route.parseQueryString()`. In `v1.x` these have been broken out and attached to the root `m`. - -### `v0.2.x` - -```javascript -var qs = m.route.buildQueryString({ a : 1 }); - -var obj = m.route.parseQueryString("a=1"); -``` - -### `v1.x` - -```javascript -var qs = m.buildQueryString({ a : 1 }); - -var obj = m.parseQueryString("a=1"); -``` - ---- - -## Preventing unmounting - -It is no longer possible to prevent unmounting via `onunload`'s `e.preventDefault()`. Instead you should explicitly call `m.route.set` when the expected conditions are met. - -### `v0.2.x` - -```javascript -var Component = { - controller: function() { - this.onunload = function(e) { - if (condition) e.preventDefault() - } - }, - view: function() { - return m("a[href=/]", {config: m.route}) - } -} -``` - -### `v1.x` - -```javascript -var Component = { - view: function() { - return m("a", {onclick: function() {if (!condition) m.route.set("/")}}) - } -} -``` - ---- - -## Run code on component removal - -Components no longer call `this.onunload` when they are being removed. They now use the standardized lifecycle hook `onremove`. - -### `v0.2.x` - -```javascript -var Component = { - controller: function() { - this.onunload = function(e) { - // ... - } - }, - view: function() { - // ... - } -} -``` - -### `v1.x` - -```javascript -var Component = { - onremove : function() { - // ... - } - view: function() { - // ... - } -} -``` - ---- - -## m.request - -Promises returned by [m.request](request.md) are no longer `m.prop` getter-setters. In addition, `initialValue`, `unwrapSuccess` and `unwrapError` are no longer supported options. - -In addition, requests no longer have `m.startComputation`/`m.endComputation` semantics. Instead, redraws are always triggered when a request promise chain completes (unless `background:true` is set). - -### `v0.2.x` - -```javascript -var data = m.request({ - method: "GET", - url: "https://api.github.com/", - initialValue: [], -}) - -setTimeout(function() { - console.log(data()) -}, 1000) -``` - -### `v1.x` - -```javascript -var data = [] -m.request({ - method: "GET", - url: "https://api.github.com/", -}) -.then(function (responseBody) { - data = responseBody -}) - -setTimeout(function() { - console.log(data) // note: not a getter-setter -}, 1000) -``` - -Additionally, if the `extract` option is passed to `m.request` the return value of the provided function will be used directly to resolve the request promise, and the `deserialize` callback is ignored. - ---- - -## `m.deferred` removed - -`v0.2.x` used its own custom asynchronous contract object, exposed as `m.deferred`, which was used as the basis for `m.request`. `v1.x` uses Promises instead, and implements a [polyfill](promises.md) in non-supporting environments. In situations where you would have used `m.deferred`, you should use Promises instead. - -### `v0.2.x` - -```javascript -var greetAsync = function() { - var deferred = m.deferred() - setTimeout(function() { - deferred.resolve("hello") - }, 1000) - return deferred.promise -} - -greetAsync() - .then(function(value) {return value + " world"}) - .then(function(value) {console.log(value)}) //logs "hello world" after 1 second -``` - -### `v1.x` - -```javascript -var greetAsync = function() { - return new Promise(function(resolve){ - setTimeout(function() { - resolve("hello") - }, 1000) - }) -} - -greetAsync() - .then(function(value) {return value + " world"}) - .then(function(value) {console.log(value)}) //logs "hello world" after 1 second -``` - ---- - -## `m.sync` removed - -Since `v1.x` uses standards-compliant Promises, `m.sync` is redundant. Use `Promise.all` instead. - -### `v0.2.x` - -```javascript -m.sync([ - m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }), - m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }), -]) -.then(function (users) { - console.log("Contributors:", users[0].name, "and", users[1].name) -}) -``` - -### `v1.x` - -```javascript -Promise.all([ - m.request({ method: 'GET', url: 'https://api.github.com/users/lhorie' }), - m.request({ method: 'GET', url: 'https://api.github.com/users/isiahmeadows' }), -]) -.then(function (users) { - console.log("Contributors:", users[0].name, "and", users[1].name) -}) -``` - ---- - -## `xlink` namespace required - -In `v0.2.x`, the `xlink` namespace was the only supported attribute namespace, and it was supported via special casing behavior. Now namespace parsing is fully supported, and namespaced attributes should explicitly declare their namespace. - -### `v0.2.x` - -```javascript -m("svg", - // the `href` attribute is namespaced automatically - m("image[href='image.gif']") -) -``` - -### `v1.x` - -```javascript -m("svg", - // User-specified namespace on the `href` attribute - m("image[xlink:href='image.gif']") -) -``` - ---- - -## Nested arrays in views - -Arrays now represent [fragments](fragment.md), which are structurally significant in v1.x virtual DOM. Whereas nested arrays in v0.2.x would be flattened into one continuous list of virtual nodes for the purposes of diffing, v1.x preserves the array structure - the children of any given array are not considered siblings of those of adjacent arrays. - ---- - -## `vnode` equality checks - -If a vnode is strictly equal to the vnode occupying its place in the last draw, v1.x will skip that part of the tree without checking for mutations or triggering any lifecycle methods in the subtree. The component documentation contains [more detail on this issue](components.md#avoid-creating-component-instances-outside-views). diff --git a/docs/code-of-conduct.md b/docs/code-of-conduct.md index a8875af5..3f856028 100644 --- a/docs/code-of-conduct.md +++ b/docs/code-of-conduct.md @@ -68,7 +68,7 @@ members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +available at [https://contributor-covenant.org/version/1/4][version] -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/docs/credits.md b/docs/credits.md index 7fd2a048..df95dca5 100644 --- a/docs/credits.md +++ b/docs/credits.md @@ -21,6 +21,6 @@ Special thanks to: Other people who also deserve recognition: -- Arthur Clemens - creator of [Polythene](https://github.com/ArthurClemens/Polythene) and the [HTML-to-Mithril converter](http://arthurclemens.github.io/mithril-template-converter/index.html) +- 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 diff --git a/docs/examples.md b/docs/examples.md index 68e93dc3..c5615f45 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,11 +2,10 @@ Here are some examples of Mithril in action -- [Animation](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/animation/mosaic.html) +- [Animation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/animation/mosaic.html) - [Community Added Examples](https://how-to-mithril.js.org) -- [DBMonster](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html) -- [Markdown Editor](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/editor/index.html) -- SVG: [Clock](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/svg/clock.html), [Ring](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/svg/ring.html), [Tiger](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/svg/tiger.html) -- [ThreadItJS](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/threaditjs/index.html) -- [TodoMVC](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/todomvc/index.html) - +- [DBMonster](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html) +- [Markdown Editor](https://raw.githack.com/MithrilJS/mithril.js/master/examples/editor/index.html) +- SVG: [Clock](https://raw.githack.com/MithrilJS/mithril.js/master/examples/svg/clock.html), [Ring](https://raw.githack.com/MithrilJS/mithril.js/master/examples/svg/ring.html), [Tiger](https://raw.githack.com/MithrilJS/mithril.js/master/examples/svg/tiger.html) +- [ThreadItJS](https://raw.githack.com/MithrilJS/mithril.js/master/examples/threaditjs/index.html) +- [TodoMVC](https://raw.githack.com/MithrilJS/mithril.js/master/examples/todomvc/index.html) diff --git a/docs/framework-comparison.md b/docs/framework-comparison.md index be015d49..55ae4aeb 100644 --- a/docs/framework-comparison.md +++ b/docs/framework-comparison.md @@ -74,7 +74,7 @@ What these numbers show is that not only does Mithril initializes significantly 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](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/react/index.html) and a [Mithril implementation](http://cdn.rawgit.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 implementation](https://raw.githack.com/MithrilJS/mithril.js/master/examples/dbmonster/mithril/index.html). Sample results are shown below: React | Mithril ------- | ------- @@ -139,7 +139,7 @@ Also, remember that frameworks like Angular and Mithril are designed for non-tri ##### 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](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/angular/index.html) and a [Mithril implementation](http://cdn.rawgit.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 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 ------- | ------- @@ -193,7 +193,7 @@ 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](http://cdn.rawgit.com/MithrilJS/mithril.js/master/examples/dbmonster/vue/index.html) and a [Mithril implementation](http://cdn.rawgit.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 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 ------ | ------- diff --git a/docs/hyperscript.md b/docs/hyperscript.md index 1b9a6eec..731b5073 100644 --- a/docs/hyperscript.md +++ b/docs/hyperscript.md @@ -122,8 +122,8 @@ m("section.container") m("input[type=text][placeholder=Name]") // -m("a#exit.external[href='http://example.com']", "Leave") -// Leave +m("a#exit.external[href='https://example.com']", "Leave") +// Leave ``` If you omit the tag name, Mithril assumes a `div` tag. @@ -215,7 +215,7 @@ m("a-scene", [ ]) ``` -And yes, this translates to both attributes and properties, and it works just like they would in the DOM. Using [Brick's `brick-deck`](http://brick.mozilla.io/docs/brick-deck) as an example, they have a `selected-index` attribute with a corresponding `selectedIndex` getter/setter property. +And yes, this translates to both attributes and properties, and it works just like they would in the DOM. Using [Brick's `brick-deck`](https://brick.mozilla.io/docs/brick-deck) as an example, they have a `selected-index` attribute with a corresponding `selectedIndex` getter/setter property. ```javascript m("brick-deck[selected-index=0]", [/* ... */]) // lowercase @@ -232,7 +232,7 @@ For custom elements, it doesn't auto-stringify properties, in case they are obje m("my-special-element", { whitelist: [ "https://example.com", - "http://neverssl.com", + "https://neverssl.com", "https://google.com", ], }) @@ -462,7 +462,7 @@ You cannot use JavaScript statements such as `if` or `for` within JavaScript exp 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. -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](http://arthurclemens.github.io/mithril-template-converter/index.html). +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). --- diff --git a/docs/index.md b/docs/index.md index f795464a..fa558fba 100644 --- a/docs/index.md +++ b/docs/index.md @@ -69,7 +69,7 @@ Let's create an HTML file to follow along: To make things simpler you can fork this pen which already has the latest version of mithril loaded. -

See the Pen Mithril Scaffold by Pat Cavit (@tivac) on CodePen.

+

See the Pen Mithril Scaffold by Pat Cavit (@tivac) on CodePen.

Mithril is also loaded onto this page already, so you can start poking at the `m` object in the developer console right away if you'd like! @@ -96,7 +96,7 @@ As you can see, you use the same code to both create and update HTML. Mithril au #### Live Example -

See the Pen Mithril Hello World by Pat Cavit (@tivac) on CodePen.

+

See the Pen Mithril Hello World by Pat Cavit (@tivac) on CodePen.

--- @@ -135,7 +135,7 @@ m("main", [ #### Live Example -

See the Pen Simple Mithril Example by Pat Cavit (@tivac) on CodePen.

+

See the Pen Simple Mithril Example by Pat Cavit (@tivac) on CodePen.

Note: If you prefer `` syntax, [it's possible to use it via a Babel plugin](jsx.md). @@ -206,7 +206,7 @@ If you're wondering about performance, it turns out Mithril is very fast at rend #### Live Example -

See the Pen Mithril Component Example by Pat Cavit (@tivac) on CodePen.

+

See the Pen Mithril Component Example by Pat Cavit (@tivac) on CodePen.

--- @@ -238,13 +238,13 @@ 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 `"/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 `http://localhost`, then you get redirected to `http://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 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 `http://localhost/#!/hello`. You can navigate back and forth to the splash page using the browser's back and next button. +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. #### Live Example -

See the Pen Mithril Routing Example by Pat Cavit (@tivac) on CodePen.

+

See the Pen Mithril Routing Example by Pat Cavit (@tivac) on CodePen.

--- @@ -253,7 +253,7 @@ Also, as you would expect, clicking on the link on the splash page takes you to Basically, XHR is just a way to talk to a server. -Let's change our click counter to make it save data on a server. For the server, we'll use [REM](http://rem-rest-api.herokuapp.com), a mock REST API designed for toy apps like this tutorial. +Let's change our click counter to make it save data on a server. For the server, we'll use [REM](https://rem-rest-api.herokuapp.com), a mock REST API designed for toy apps like this tutorial. First we create a function that calls `m.request`. The `url` specifies an endpoint that represents a resource, the `method` specifies the type of action we're taking (typically the `PUT` method [upserts](https://en.wiktionary.org/wiki/upsert)), `body` is the payload that we're sending to the endpoint and `withCredentials` means to enable cookies (a requirement for the REM API to work) @@ -291,7 +291,7 @@ Clicking the button should now update the count. #### Live Example -

See the Pen Mithril XHR Example by Pat Cavit (@tivac) on CodePen.

+

See the Pen Mithril XHR Example by Pat Cavit (@tivac) on CodePen.

--- diff --git a/docs/installation.md b/docs/installation.md index 6a2b8989..7ef19766 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,7 +1,7 @@ # Installation - [CDN](#cdn) -- [NPM](#npm) +- [npm](#npm) - [Quick start with Webpack](#quick-start-with-webpack) - [TypeScript](#typescript) @@ -15,7 +15,7 @@ If you're new to JavaScript or just want a very simple setup to get your feet we --- -### NPM +### npm ```bash $ npm install mithril --save @@ -83,11 +83,11 @@ $ npm start #### 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 is to use npm. -NPM (Node package manager) 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.js](https://nodejs.org); NPM will be automatically installed as well. +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 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 @@ -113,11 +113,11 @@ m.render(document.body, "hello world") Modularization is the practice of separating the code into files. Doing so makes it easier to find code, understand what code relies on what code, and test. -CommonJS is a de-facto standard for modularizing JavaScript code, and it's used by Node.js, as well as tools like [Browserify](http://browserify.org/) and [Webpack](https://webpack.js.org/). It's a robust, battle-tested precursor to ES6 modules. Although the syntax for ES6 modules is specified in Ecmascript 6, the actual module loading mechanism is not. If you wish to use ES6 modules despite the non-standardized status of module loading, you can use tools like [Rollup](http://rollupjs.org/) or [Babel](https://babeljs.io/). +CommonJS is a de-facto standard for modularizing JavaScript code, and it's used by Node.js, as well as tools like [Browserify](https://browserify.org/) and [Webpack](https://webpack.js.org/). It's a robust, battle-tested precursor to ES6 modules. Although the syntax for ES6 modules is specified in Ecmascript 6, the actual module loading mechanism is not. If you wish to use ES6 modules despite the non-standardized status of module loading, you can use tools like [Rollup](https://rollupjs.org/) or [Babel](https://babeljs.io/). Most browser today do not natively support modularization systems (CommonJS or ES6), so modularized code must be bundled into a single JavaScript file before running in a client-side application. -A popular way for creating a bundle is to setup an NPM script for [Webpack](https://webpack.js.org/). To install Webpack, run this from the command line: +A popular way for creating a bundle is to setup an npm script for [Webpack](https://webpack.js.org/). To install Webpack, run this from the command line: ```bash npm install webpack webpack-cli --save-dev @@ -140,7 +140,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 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 @@ -159,7 +159,7 @@ Now that you have created a bundle, you can then reference the `bin/app.js` file ``` -As you've seen above, importing a module in CommonJS is done via the `require` function. You can reference NPM modules by their library names (e.g. `require("mithril")` or `require("jquery")`), and you can reference your own modules via relative paths minus the file extension (e.g. if you have a file called `mycomponent.js` in the same folder as the file you're importing to, you can import it by calling `require("./mycomponent")`). +As you've seen above, importing a module in CommonJS is done via the `require` function. You can reference npm modules by their library names (e.g. `require("mithril")` or `require("jquery")`), and you can reference your own modules via relative paths minus the file extension (e.g. if you have a file called `mycomponent.js` in the same folder as the file you're importing to, you can import it by calling `require("./mycomponent")`). To export a module, assign what you want to export to the special `module.exports` object: @@ -187,7 +187,7 @@ Note that in this example, we're using `m.mount`, which wires up the component t If you open bin/app.js, you'll notice that the Webpack bundle is not minified, so this file is not ideal for a live application. To generate a minified file, open `package.json` and add a new npm script: -``` +```json { "name": "my-project", "scripts": { @@ -236,21 +236,6 @@ npm start The source file `index.js` will be compiled (bundled) and a browser window opens showing the result. Any changes in the source files will instantly get recompiled and the browser will refresh reflecting the changes. -#### Mithril bundler - -Mithril comes with a bundler tool of its own. It is sufficient for ES5-based projects that have no other dependencies other than Mithril, but it's currently considered experimental for projects that require other NPM dependencies. It produces smaller bundles than webpack, but you should not use it in production yet. - -If you want to try it and give feedback, you can open `package.json` and change the npm script for webpack to this: - -``` -{ - "name": "my-project", - "scripts": { - "build": "bundle src/index.js --output bin/app.js --watch" - } -} -``` - #### Vanilla If you don't have the ability to run a bundler script due to company security policies, there's an options to not use a module system at all: diff --git a/docs/jsx.md b/docs/jsx.md index e4d7c747..0142047b 100644 --- a/docs/jsx.md +++ b/docs/jsx.md @@ -38,9 +38,9 @@ When using JSX, it's possible to interpolate JavaScript expressions within JSX t ```jsx var greeting = "Hello" -var url = "http://google.com" +var url = "https://google.com" var link = {greeting}! -// yields Hello! +// yields Hello! ``` Components can be used by using a convention of uppercasing the first letter of the component name: @@ -56,7 +56,7 @@ m.render(document.body, ) The simplest way to use JSX is via a [Babel](https://babeljs.io/) plugin. -Babel requires NPM, which is automatically installed when you install [Node.js](https://nodejs.org/en/). Once NPM is installed, create a project folder and run this command: +Babel requires npm, which is automatically installed when you install [Node.js](https://nodejs.org/en/). Once npm is installed, create a project folder and run this command: ```bash npm init -y @@ -340,4 +340,4 @@ function SummaryView() { 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. -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](http://arthurclemens.github.io/mithril-template-converter/index.html) to do much of it for you. +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/lint.js b/docs/lint.js index 6a3d8cb9..356eb76a 100644 --- a/docs/lint.js +++ b/docs/lint.js @@ -176,7 +176,7 @@ function traverseDirectory(pathname, callback) { //run traverseDirectory("./docs", function(pathname) { - if (pathname.indexOf(".md") > -1 && !pathname.match(/change-log|node_modules/)) { + if (pathname.indexOf(".md") > -1 && !pathname.match(/change-log|migration-|node_modules/)) { fs.readFile(pathname, "utf8", function(err, data) { if (err) console.log(err) else lint(pathname, data) diff --git a/docs/migration-v02x.md b/docs/migration-v02x.md new file mode 100644 index 00000000..ea441b6c --- /dev/null +++ b/docs/migration-v02x.md @@ -0,0 +1,935 @@ +# Migrating from v0.2.x + +v1.x and v2.x are largely API-compatible with v0.2.x, but there are some breaking changes. Migrating to v2.x is nearly identical, so the notes below apply mostly to both. + +If you are migrating, consider using the [mithril-codemods](https://www.npmjs.com/package/mithril-codemods) tool to help automate the most straightforward migrations. + +- [`m.prop` removed](#mprop-removed) +- [`m.component` removed](#mcomponent-removed) +- [`m.withAttr` removed](#mwithattr-removed) +- [`m.version` removed](#mversion-removed) +- [`config` function](#config-function) +- [Changes in redraw behaviour](#changes-in-redraw-behaviour) + - [No more redraw locks](#no-more-redraw-locks) + - [Cancelling redraw from event handlers](#cancelling-redraw-from-event-handlers) + - [Synchronous redraw changed](#synchronous-redraw) + - [`m.startComputation`/`m.endComputation` removed](#mstartcomputationmendcomputation-removed) +- [Component `controller` function](#component-controller-function) +- [Component arguments](#component-arguments) +- [Component vnode children](#component-children) +- [DOM vnode children](#dom-vnode-children) +- [Keys](#keys) +- [`view()` parameters](#view-parameters) +- [Passing components to `m()`](#passing-components-to-m) +- [Passing vnodes to `m.mount()` and `m.route()`](#passing-vnodes-to-mmount-and-mroute) +- [`m.route.mode`](#mroutemode) +- [`m.route()` and anchor tags](#mroute-and-anchor-tags) +- [Path templates](#path-templates) +- [Reading/writing the current route](#readingwriting-the-current-route) +- [Accessing route params](#accessing-route-params) +- [Building/Parsing query strings](#buildingparsing-query-strings) +- [Preventing unmounting](#preventing-unmounting) +- [Run code on component removal](#run-code-on-component-removal) +- [`m.request`](#mrequest) +- [Default `responseType` for `m.request`](#default-responsetype-for-mrequest) +- [`m.deferred` removed](#mdeferred-removed) +- [`m.sync` removed](#msync-removed) +- [`xlink` namespace required](#xlink-namespace-required) +- [Nested arrays in views](#nested-arrays-in-views) +- [`vnode` equality checks](#vnode-equality-checks) + +--- + +## `m.prop` removed + +In v2.x, `m.prop()` was converted into now a more powerful stream micro-library, but it's no longer part of core. You can read about how to use the optional Streams module in [the documentation](stream.md). + +### v0.2.x + +```javascript +var m = require("mithril") + +var num = m.prop(1) +``` + +### v2.x + +```javascript +var m = require("mithril") +var prop = require("mithril/stream") + +var num = prop(1) +var doubled = num.map(function(n) { return n * 2 }) +``` + +--- + +## `m.component` removed + +In v0.2.x components could be created using either `m(Component)` or `m.component(Component)`. v2.x only support `m(Component)`. + +### v0.2.x + +```javascript +// These are equivalent +m.component(Component) +m(Component) +``` + +### v2.x + +```javascript +m(Component) +``` + +--- + +## `m.withAttr` removed + +In v0.2.x event listeners could use `oninput: m.withAttr("value", func)` and similar. In v2.x, just read them directly from the event's target. It synergized well with `m.prop`, but since that was removed in favor of an out of core solution and v1.x didn't see similar broad, idiomatic usage of streams, `m.withAttr` lost most of its usefulness. + +### v0.2.x + +```javascript +var value = m.prop("") + +// In your view +m("input[type=text]", { + value: value(), + oninput: m.withAttr("value", value), +}) +``` + +### v2.x + +```javascript +var value = "" + +// In your view +m("input[type=text]", { + value: value, + oninput: function (ev) { value = ev.target.value }, +}) +``` + +--- + +## `m.version` removed + +It served little use in general, and you can always add it back yourself. You should prefer feature detection for knowing what features are available, and the v2.x API is designed to better enable this. + +--- + +## `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. + +### v0.2.x + +```javascript +m("div", { + config: function(element, isInitialized) { + // runs on each redraw + // isInitialized is a boolean representing if the node has been added to the DOM + } +}) +``` + +### v2.x + +More documentation on these new methods is available in [lifecycle-methods.md](lifecycle-methods.md). + +```javascript +m("div", { + // Called before the DOM node is created + oninit: function(vnode) { /*...*/ }, + // Called after the DOM node is created + oncreate: function(vnode) { /*...*/ }, + // Called before the node is updated, return false to cancel + onbeforeupdate: function(vnode, old) { /*...*/ }, + // Called after the node is updated + onupdate: function(vnode) { /*...*/ }, + // Called before the node is removed, return a Promise that resolves when + // ready for the node to be removed from the DOM + onbeforeremove: function(vnode) { /*...*/ }, + // Called before the node is removed, but after onbeforeremove calls done() + onremove: function(vnode) { /*...*/ } +}) +``` + +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: + +### 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. + +### Cancelling redraw from event handlers + +`m.mount()` and `m.route()` still automatically redraw after a DOM event handler runs. Cancelling these redraws from within your event handlers is now done by setting the `redraw` property on the passed-in event object to `false`. + +#### v0.2.x + +```javascript +m("div", { + onclick: function(e) { + m.redraw.strategy("none") + } +}) +``` + +#### v2.x + +```javascript +m("div", { + onclick: function(e) { + e.redraw = false + } +}) +``` + +### 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. + +#### v0.2.x + +```javascript +m.redraw(true) // redraws immediately & synchronously +``` + +#### v2.x + +```javascript +m.redraw() // schedules a redraw on the next requestAnimationFrame tick +m.redraw.sync() // invokes a redraw immediately and waits for it to complete +``` + +### `m.startComputation`/`m.endComputation` removed + +They are considered anti-patterns and have a number of problematic edge cases, so they were removed without replacement in v2.x. + +--- + +## Component `controller` function + +In v2.x, there is no more `controller` property in components - use `oninit` instead. + +### v0.2.x + +```javascript +m.mount(document.body, { + controller: function() { + var ctrl = this + + ctrl.fooga = 1 + }, + + view: function(ctrl) { + return m("p", ctrl.fooga) + } +}) +``` + +### v2.x + +```javascript +m.mount(document.body, { + oninit: function(vnode) { + vnode.state.fooga = 1 + }, + + view: function(vnode) { + return m("p", vnode.state.fooga) + } +}) + +// OR + +m.mount(document.body, { + // this is bound to vnode.state by default + oninit: function(vnode) { + this.fooga = 1 + }, + + view: function(vnode) { + return m("p", this.fooga) + } +}) +``` + +--- + +## Component arguments + +Arguments to a component in v2.x must be an object, simple values like `String`/`Number`/`Boolean` will be treated as text children. Arguments are accessed within the component by reading them from the `vnode.attrs` object. + +### v0.2.x + +```javascript +var Component = { + controller: function(options) { + // options.fooga === 1 + }, + + view: function(ctrl, options) { + // options.fooga === 1 + } +} + +m("div", m.component(Component, { fooga: 1 })) +``` + +### v2.x + +```javascript +var Component = { + oninit: function(vnode) { + // vnode.attrs.fooga === 1 + }, + + view: function(vnode) { + // vnode.attrs.fooga === 1 + } +} + +m("div", m(Component, { fooga: 1 })) +``` + +--- + +## Component vnode children + +In v0.2.x, component vnode children were not normalized, just passed as extra arguments, and they were not flattened, either. (Internally, it was just returning a partially applied component that was diffed based on the component being partially applied.) In v2.x, component vnode children are passed via `vnode.children` as a resolved array of children, but like v0.2.x, the individual children themselves are not normalized, nor is the children array flattened. + +### v0.2.x + +```javascript +var Component = { + controller: function(value, renderProp) { + // value === "value" + // typeof renderProp === "function" + }, + + view: function(ctrl, value, renderProp) { + // value === "value" + // typeof renderProp === "function" + } +} + +m("div", m.component(Component, "value", function(key) { return "child" })) +``` + +### v2.x + +```javascript +var Component = { + oninit: function(vnode) { + // vnode.children[0] === "value" + // typeof vnode.children[1] === "function" + }, + + view: function(vnode) { + // vnode.children[0] === "value" + // typeof vnode.children[1] === "function" + }, +} + +m("div", m(Component, "value", function(key) { return "child" })) +``` + +--- + +## DOM vnode children + +In v0.2.x, the children of DOM nodes were represented literally with no normalization aside from using the children directly if only a single array child is present. It returned a structure more like this, with the strings represented literally. + +```js +m("div", "value", ["nested"]) + +// Becomes: +{ + tag: "div", + attrs: {}, + children: [ + "value", + ["nested"], + ] +} +``` + +In v2.x, children of DOM vnodes are normalized to objects of a single consistent structure. + +```js +m("div", "value", ["nested"]) + +// Becomes roughly: +{ + tag: "div", + attrs: null, + children: [ + {tag: "#", children: "value"}, + {tag: "[", children: [ + {tag: "#", children: "nested"}, + ]}, + ] +} +``` + +If only a single text child is present on a DOM vnode, it instead sets `text` to that value. + +```js +m("div", "value") + +// Becomes roughly: +{ + tag: "div", + attrs: null, + text: "", + children: undefined, +} +``` + +See [the vnode docs](vnodes.md) for more details on the v2.x vnode structure and how things are normalized. + +*Most of the v2.x vnode properties here are omitted for brevity.* + +--- + +## Keys + +In v0.2.x, you could mix keyed and unkeyed vnodes freely. + +In v2.x, children lists of both fragments and elements must be either all keyed or all unkeyed. Holes are considered unkeyed for the purposes of this check, too - it no longer ignores them. + +If you need to work around it, use the idiom of a fragment containing a single vnode, like `[m("div", {key: whatever})]`. + +--- + +## `view()` parameters + +In v0.2.x view functions are passed a reference to the `controller` instance and (optionally) any options passed to the component. In v2.x they are passed **only** the `vnode`, exactly like the `controller` function. + +### v0.2.x + +```javascript +m.mount(document.body, { + controller: function() {}, + + view: function(ctrl, options) { + // ... + } +}) +``` + +### v2.x + +```javascript +m.mount(document.body, { + oninit: function(vnode) { + // ... + }, + + view: function(vnode) { + // Use vnode.state instead of ctrl + // Use vnode.attrs instead of options + } +}) +``` + +--- + +## Passing components to `m()` + +In v0.2.x you could pass components as the second argument of `m()` w/o any wrapping required. To help with consistency in v2.x they must always be wrapped with a `m()` invocation. + +### v0.2.x + +```javascript +m("div", Component) +``` + +### v2.x + +```javascript +m("div", m(Component)) +``` + +--- + +## Passing vnodes to `m.mount()` and `m.route()` + +In v0.2.x, `m.mount(element, component)` tolerated [vnodes](vnodes.md) as second arguments instead of [components](components.md) (even though it wasn't documented). Likewise, `m.route(element, defaultRoute, routes)` accepted vnodes as values in the `routes` object. + +In v2.x, components are required instead in both cases. + +### v0.2.x + +```javascript +m.mount(element, m('i', 'hello')) +m.mount(element, m(Component, attrs)) + +m.route(element, '/', { + '/': m('b', 'bye') +}) +``` + +### v2.x + +```javascript +m.mount(element, {view: function () {return m('i', 'hello')}}) +m.mount(element, {view: function () {return m(Component, attrs)}}) + +m.route(element, '/', { + '/': {view: function () {return m('b', 'bye')}} +}) +``` + +--- + +## `m.route.mode` + +In v0.2.x the routing mode could be set by assigning a string of `"pathname"`, `"hash"`, or `"search"` to `m.route.mode`. In `v.1.x` it is replaced by `m.route.prefix = prefix` where `prefix` can any prefix. If it starts with `#`, it works in "hash" mode, `?` for "search" mode, and any other character (or the empty string) for "pathname" mode. It also supports combinations of the above like `m.route.prefix = "/path/#!"` or `?#`. + +The default was changed to also use a `#!` (hashbang) prefix instead of just `#`. So if you were using the default behavior and want to retain your existing URLs, specify `m.route.prefix = "#"` before initializing the routes. + +### v0.2.x + +```javascript +m.route.mode = "hash" +m.route.mode = "pathname" +m.route.mode = "search" +``` + +### v2.x + +```javascript +// Direct equivalents +m.route.prefix = "#" +m.route.prefix = "" +m.route.prefix = "?" +``` + +--- + +## `m.route()` and anchor tags + +Handling routable links now uses a special built-in component instead of an attribute. If you were using this on `