2017-11-13 02:14:04 -08:00
|
|
|
|
<h1 align="center">Choo</h1>
|
2016-05-10 23:23:33 +07:00
|
|
|
|
|
2016-05-25 04:28:20 +09:00
|
|
|
|
<div align="center">
|
|
|
|
|
|
:steam_locomotive::train::train::train::train::train:
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div align="center">
|
2016-05-25 04:45:49 +09:00
|
|
|
|
<strong>Fun functional programming</strong>
|
2016-05-25 04:28:20 +09:00
|
|
|
|
</div>
|
|
|
|
|
|
<div align="center">
|
2017-03-21 03:00:45 +01:00
|
|
|
|
A <code>4kb</code> framework for creating sturdy frontend applications
|
2016-05-25 04:28:20 +09:00
|
|
|
|
</div>
|
2016-05-13 12:49:22 +07:00
|
|
|
|
|
2016-05-25 04:28:20 +09:00
|
|
|
|
<br />
|
2016-05-10 23:23:33 +07:00
|
|
|
|
|
2016-05-25 04:28:20 +09:00
|
|
|
|
<div align="center">
|
2016-05-25 04:45:49 +09:00
|
|
|
|
<!-- Stability -->
|
|
|
|
|
|
<a href="https://nodejs.org/api/documentation.html#documentation_stability_index">
|
|
|
|
|
|
<img src="https://img.shields.io/badge/stability-experimental-orange.svg?style=flat-square"
|
|
|
|
|
|
alt="API stability" />
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<!-- NPM version -->
|
|
|
|
|
|
<a href="https://npmjs.org/package/choo">
|
|
|
|
|
|
<img src="https://img.shields.io/npm/v/choo.svg?style=flat-square"
|
|
|
|
|
|
alt="NPM version" />
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<!-- Build Status -->
|
2017-08-02 14:06:47 +02:00
|
|
|
|
<a href="https://travis-ci.org/choojs/choo">
|
|
|
|
|
|
<img src="https://img.shields.io/travis/choojs/choo/master.svg?style=flat-square"
|
2016-05-25 04:45:49 +09:00
|
|
|
|
alt="Build Status" />
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<!-- Test Coverage -->
|
2017-08-02 14:06:47 +02:00
|
|
|
|
<a href="https://codecov.io/github/choojs/choo">
|
|
|
|
|
|
<img src="https://img.shields.io/codecov/c/github/choojs/choo/master.svg?style=flat-square"
|
2016-05-25 04:45:49 +09:00
|
|
|
|
alt="Test Coverage" />
|
|
|
|
|
|
</a>
|
2016-06-04 20:11:26 +02:00
|
|
|
|
<!-- Downloads -->
|
|
|
|
|
|
<a href="https://npmjs.org/package/choo">
|
2018-03-08 20:11:32 +01:00
|
|
|
|
<img src="https://img.shields.io/npm/dt/choo.svg?style=flat-square"
|
2018-06-26 01:18:57 +05:30
|
|
|
|
alt="Download" />
|
2016-06-04 20:11:26 +02:00
|
|
|
|
</a>
|
2016-05-25 04:45:49 +09:00
|
|
|
|
<!-- Standard -->
|
2017-03-23 01:51:12 -07:00
|
|
|
|
<a href="https://standardjs.com">
|
2016-05-25 04:45:49 +09:00
|
|
|
|
<img src="https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square"
|
|
|
|
|
|
alt="Standard" />
|
|
|
|
|
|
</a>
|
2016-05-25 04:28:20 +09:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2016-07-03 14:56:24 +02:00
|
|
|
|
<div align="center">
|
|
|
|
|
|
<h3>
|
2017-03-21 03:00:45 +01:00
|
|
|
|
<a href="https://choo.io">
|
|
|
|
|
|
Website
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<span> | </span>
|
2017-08-02 14:06:47 +02:00
|
|
|
|
<a href="https://github.com/choojs/choo-handbook">
|
2016-07-03 14:56:24 +02:00
|
|
|
|
Handbook
|
|
|
|
|
|
</a>
|
2016-07-10 18:38:27 +02:00
|
|
|
|
<span> | </span>
|
2017-03-21 03:00:45 +01:00
|
|
|
|
<a href="https://github.com/YerkoPalma/awesome-choo">
|
|
|
|
|
|
Ecosystem
|
|
|
|
|
|
</a>
|
2016-07-10 18:38:27 +02:00
|
|
|
|
<span> | </span>
|
2017-03-21 03:00:45 +01:00
|
|
|
|
<!-- <a href="https://github.com/trainyard/choo-cli"> -->
|
|
|
|
|
|
<!-- CLI -->
|
|
|
|
|
|
<!-- </a> -->
|
|
|
|
|
|
<!-- <span> | </span> -->
|
2017-08-02 14:06:47 +02:00
|
|
|
|
<a href="https://github.com/choojs/choo/blob/master/.github/CONTRIBUTING.md">
|
2016-07-03 14:56:24 +02:00
|
|
|
|
Contributing
|
|
|
|
|
|
</a>
|
2016-07-10 18:38:27 +02:00
|
|
|
|
<span> | </span>
|
2017-09-20 16:55:46 +02:00
|
|
|
|
<a href="https://www.reddit.com/r/choojs/">
|
|
|
|
|
|
Reddit
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<span> | </span>
|
2016-07-10 18:38:27 +02:00
|
|
|
|
<a href="https://webchat.freenode.net/?channels=choo">
|
|
|
|
|
|
Chat
|
|
|
|
|
|
</a>
|
2016-07-03 14:56:24 +02:00
|
|
|
|
</h3>
|
|
|
|
|
|
</div>
|
2016-05-25 04:28:20 +09:00
|
|
|
|
|
|
|
|
|
|
<div align="center">
|
|
|
|
|
|
<sub>The little framework that could. Built with ❤︎ by
|
|
|
|
|
|
<a href="https://twitter.com/yoshuawuyts">Yoshua Wuyts</a> and
|
2017-08-02 14:06:47 +02:00
|
|
|
|
<a href="https://github.com/choojs/choo/graphs/contributors">
|
2016-05-25 04:28:20 +09:00
|
|
|
|
contributors
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2017-11-13 02:14:04 -08:00
|
|
|
|
## Table of Contents
|
2017-03-21 03:00:45 +01:00
|
|
|
|
- [Features](#features)
|
|
|
|
|
|
- [Example](#example)
|
|
|
|
|
|
- [Philosophy](#philosophy)
|
2017-06-28 15:53:37 +02:00
|
|
|
|
- [Events](#events)
|
2017-06-28 16:06:02 +02:00
|
|
|
|
- [State](#state)
|
2017-06-29 17:20:00 +02:00
|
|
|
|
- [Routing](#routing)
|
2017-06-28 15:53:37 +02:00
|
|
|
|
- [Server Rendering](#server-rendering)
|
2018-07-10 14:40:51 +02:00
|
|
|
|
- [Components](#components)
|
2017-03-21 03:00:45 +01:00
|
|
|
|
- [Optimizations](#optimizations)
|
|
|
|
|
|
- [FAQ](#faq)
|
|
|
|
|
|
- [API](#api)
|
|
|
|
|
|
- [Installation](#installation)
|
|
|
|
|
|
- [See Also](#see-also)
|
|
|
|
|
|
- [Support](#support)
|
2016-05-21 22:44:03 +09:00
|
|
|
|
|
2016-05-10 23:23:33 +07:00
|
|
|
|
## Features
|
2017-11-13 02:14:04 -08:00
|
|
|
|
- __minimal size:__ weighing `4kb`, Choo is a tiny little framework
|
2017-03-21 03:00:45 +01:00
|
|
|
|
- __event based:__ our performant event system makes writing apps easy
|
|
|
|
|
|
- __small api:__ with only 6 methods there's not much to learn
|
2016-05-10 23:23:33 +07:00
|
|
|
|
- __minimal tooling:__ built for the cutting edge `browserify` compiler
|
2016-06-22 10:48:41 +01:00
|
|
|
|
- __isomorphic:__ renders seamlessly in both Node and browsers
|
2016-05-10 23:23:33 +07:00
|
|
|
|
- __very cute:__ choo choo!
|
|
|
|
|
|
|
2016-07-03 14:56:24 +02:00
|
|
|
|
## Example
|
2016-06-01 01:03:25 +02:00
|
|
|
|
```js
|
2017-03-25 10:09:23 +01:00
|
|
|
|
var html = require('choo/html')
|
2017-10-31 08:24:43 -03:00
|
|
|
|
var devtools = require('choo-devtools')
|
2017-03-25 10:09:23 +01:00
|
|
|
|
var choo = require('choo')
|
2017-03-21 03:00:45 +01:00
|
|
|
|
|
2016-12-11 19:35:29 +01:00
|
|
|
|
var app = choo()
|
2017-10-31 08:24:43 -03:00
|
|
|
|
app.use(devtools())
|
2017-03-21 03:00:45 +01:00
|
|
|
|
app.use(countStore)
|
|
|
|
|
|
app.route('/', mainView)
|
|
|
|
|
|
app.mount('body')
|
2016-06-01 01:03:25 +02:00
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
function mainView (state, emit) {
|
2016-12-11 19:35:29 +01:00
|
|
|
|
return html`
|
2017-03-21 03:00:45 +01:00
|
|
|
|
<body>
|
|
|
|
|
|
<h1>count is ${state.count}</h1>
|
|
|
|
|
|
<button onclick=${onclick}>Increment</button>
|
|
|
|
|
|
</body>
|
2016-12-11 19:35:29 +01:00
|
|
|
|
`
|
2016-05-10 23:23:33 +07:00
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
function onclick () {
|
|
|
|
|
|
emit('increment', 1)
|
2016-12-11 19:35:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-05-10 23:23:33 +07:00
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
function countStore (state, emitter) {
|
|
|
|
|
|
state.count = 0
|
|
|
|
|
|
emitter.on('increment', function (count) {
|
|
|
|
|
|
state.count += count
|
|
|
|
|
|
emitter.emit('render')
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2016-07-03 14:56:24 +02:00
|
|
|
|
```
|
2017-03-21 03:00:45 +01:00
|
|
|
|
Want to see more examples? Check out the [Choo handbook][handbook].
|
2016-07-03 14:56:24 +02:00
|
|
|
|
|
|
|
|
|
|
## Philosophy
|
|
|
|
|
|
We believe programming should be fun and light, not stern and stressful. It's
|
|
|
|
|
|
cool to be cute; using serious words without explaining them doesn't make for
|
|
|
|
|
|
better results - if anything it scares people off. We don't want to be scary,
|
|
|
|
|
|
we want to be nice and fun, and then _casually_ be the best choice around.
|
|
|
|
|
|
_Real casually._
|
|
|
|
|
|
|
|
|
|
|
|
We believe frameworks should be disposable, and components recyclable. We don't
|
2017-04-07 22:21:40 +02:00
|
|
|
|
want a web where walled gardens jealously compete with one another. By making
|
|
|
|
|
|
the DOM the lowest common denominator, switching from one framework to another
|
2017-11-13 02:14:04 -08:00
|
|
|
|
becomes frictionless. Choo is modest in its design; we don't believe it will
|
2017-04-07 22:22:47 +02:00
|
|
|
|
be top of the class forever, so we've made it as easy to toss out as it is to
|
|
|
|
|
|
pick up.
|
2016-07-03 14:56:24 +02:00
|
|
|
|
|
2017-04-07 22:21:40 +02:00
|
|
|
|
We don't believe that bigger is better. Big APIs, large complexities, long
|
|
|
|
|
|
files - we see them as omens of impending userland complexity. We want everyone
|
|
|
|
|
|
on a team, no matter the size, to fully understand how an application is laid
|
|
|
|
|
|
out. And once an application is built, we want it to be small, performant and
|
|
|
|
|
|
easy to reason about. All of which makes for easy to debug code, better results
|
|
|
|
|
|
and super smiley faces.
|
2016-07-03 14:56:24 +02:00
|
|
|
|
|
2017-06-28 15:53:37 +02:00
|
|
|
|
## Events
|
|
|
|
|
|
At the core of Choo is an event emitter, which is used for both application
|
|
|
|
|
|
logic but also to interface with the framework itself. The package we use for
|
2017-08-02 14:06:47 +02:00
|
|
|
|
this is [nanobus](https://github.com/choojs/nanobus).
|
2017-06-28 15:53:37 +02:00
|
|
|
|
|
2017-10-31 08:24:43 -03:00
|
|
|
|
You can access the emitter through `app.use(state, emitter, app)`, `app.route(route,
|
2017-06-28 15:53:37 +02:00
|
|
|
|
view(state, emit))` or `app.emitter`. Routes only have access to the
|
|
|
|
|
|
`emitter.emit` method to encourage people to separate business logic from
|
|
|
|
|
|
render logic.
|
|
|
|
|
|
|
|
|
|
|
|
The purpose of the emitter is two-fold: it allows wiring up application code
|
|
|
|
|
|
together, and splitting it off nicely - but it also allows communicating with
|
|
|
|
|
|
the Choo framework itself. All events can be read as constants from
|
|
|
|
|
|
`state.events`. Choo ships with the following events built in:
|
|
|
|
|
|
|
|
|
|
|
|
### `'DOMContentLoaded'`|`state.events.DOMCONTENTLOADED`
|
|
|
|
|
|
Choo emits this when the DOM is ready. Similar to the DOM's
|
|
|
|
|
|
`'DOMContentLoaded'` event, except it will be emitted even if the listener is
|
|
|
|
|
|
added _after_ the DOM became ready. Uses
|
|
|
|
|
|
[document-ready](https://github.com/bendrucker/document-ready) under the hood.
|
|
|
|
|
|
|
|
|
|
|
|
### `'render'`|`state.events.RENDER`
|
|
|
|
|
|
This event should be emitted to re-render the DOM. A common pattern is to
|
|
|
|
|
|
update the `state` object, and then emit the `'render'` event straight after.
|
|
|
|
|
|
Note that `'render'` will only have an effect once the `DOMContentLoaded` event
|
|
|
|
|
|
has been fired.
|
|
|
|
|
|
|
|
|
|
|
|
### `'navigate'`|`state.events.NAVIGATE`
|
|
|
|
|
|
Choo emits this event whenever routes change. This is triggered by either
|
|
|
|
|
|
`'pushState'`, `'replaceState'` or `'popState'`.
|
|
|
|
|
|
|
|
|
|
|
|
### `'pushState'`|`state.events.PUSHSTATE`
|
|
|
|
|
|
This event should be emitted to navigate to a new route. The new route is added
|
|
|
|
|
|
to the browser's history stack, and will emit `'navigate'` and `'render'`.
|
|
|
|
|
|
Similar to
|
|
|
|
|
|
[history.pushState](http://devdocs.io/dom/history_api).
|
|
|
|
|
|
|
|
|
|
|
|
### `'replaceState'`|`state.events.REPLACESTATE`
|
|
|
|
|
|
This event should be emitted to navigate to a new route. The new route replaces
|
|
|
|
|
|
the current entry in the browser's history stack, and will emit `'navigate'`
|
|
|
|
|
|
and `'render'`. Similar to
|
|
|
|
|
|
[history.replaceState](http://devdocs.io/dom/history#history-replacestate).
|
|
|
|
|
|
|
|
|
|
|
|
### `'popState'`|`state.events.POPSTATE`
|
2018-02-13 09:43:02 +00:00
|
|
|
|
This event is emitted when the user hits the 'back' button in their browser.
|
|
|
|
|
|
The new route will be a previous entry in the browser's history stack, and
|
|
|
|
|
|
immediately afterward the`'navigate'` and `'render'`events will be emitted.
|
|
|
|
|
|
Similar to [history.popState](http://devdocs.io/dom_events/popstate). (Note
|
|
|
|
|
|
that `emit('popState')` will _not_ cause a popState action - use
|
|
|
|
|
|
`history.go(-1)` for that - this is different to the behaviour of `pushState`
|
|
|
|
|
|
and `replaceState`!)
|
2017-06-28 15:53:37 +02:00
|
|
|
|
|
2017-07-03 10:43:44 +02:00
|
|
|
|
### `'DOMTitleChange'`|`state.events.DOMTITLECHANGE`
|
|
|
|
|
|
This event should be emitted whenever the `document.title` needs to be updated.
|
|
|
|
|
|
It will set both `document.title` and `state.title`. This value can be used
|
|
|
|
|
|
when server rendering to accurately include a `<title>` tag in the header.
|
|
|
|
|
|
This is derived from the
|
|
|
|
|
|
[DOMTitleChanged event](https://developer.mozilla.org/en-US/docs/Web/Events/DOMTitleChanged).
|
|
|
|
|
|
|
2017-06-28 15:53:37 +02:00
|
|
|
|
## State
|
|
|
|
|
|
Choo comes with a shared state object. This object can be mutated freely, and
|
|
|
|
|
|
is passed into the view functions whenever `'render'` is emitted. The state
|
|
|
|
|
|
object comes with a few properties set.
|
|
|
|
|
|
|
2017-10-20 16:30:58 +02:00
|
|
|
|
When initializing the application, `window.initialState` is used to provision
|
|
|
|
|
|
the initial state. This is especially useful when combined with server
|
|
|
|
|
|
rendering. See [server rendering](#server-rendering) for more details.
|
|
|
|
|
|
|
2017-06-28 15:53:37 +02:00
|
|
|
|
### `state.events`
|
|
|
|
|
|
A mapping of Choo's built in events. It's recommended to extend this object
|
|
|
|
|
|
with your application's events. By defining your event names once and setting
|
|
|
|
|
|
them on `state.events`, it reduces the chance of typos, generally autocompletes
|
|
|
|
|
|
better, makes refactoring easier and compresses better.
|
|
|
|
|
|
|
|
|
|
|
|
### `state.params`
|
|
|
|
|
|
The current params taken from the route. E.g. `/foo/:bar` becomes available as
|
|
|
|
|
|
`state.params.bar` If a wildcard route is used (`/foo/*`) it's available as
|
|
|
|
|
|
`state.params.wildcard`.
|
|
|
|
|
|
|
2017-06-29 17:20:00 +02:00
|
|
|
|
### `state.query`
|
|
|
|
|
|
An object containing the current queryString. `/foo?bin=baz` becomes `{ bin:
|
|
|
|
|
|
'baz' }`.
|
|
|
|
|
|
|
2017-07-26 19:31:15 +02:00
|
|
|
|
### `state.href`
|
|
|
|
|
|
An object containing the current href. `/foo?bin=baz` becomes `foo`.
|
|
|
|
|
|
|
2017-06-28 15:53:37 +02:00
|
|
|
|
### `state.route`
|
|
|
|
|
|
The current name of the route used in the router (e.g. `/foo/:bar`).
|
|
|
|
|
|
|
2017-07-03 10:43:44 +02:00
|
|
|
|
### `state.title`
|
|
|
|
|
|
The current page title. Can be set using the `DOMTitleChange` event.
|
|
|
|
|
|
|
2018-07-10 14:40:51 +02:00
|
|
|
|
### `state.components`
|
|
|
|
|
|
An object _recommended_ to use for local component state.
|
|
|
|
|
|
|
|
|
|
|
|
### `state.cache(Component, id, [...args])`
|
|
|
|
|
|
Generic class cache. Will lookup Component instance by id and create one if not
|
|
|
|
|
|
found. Usefull for working with statefull [components](#components).
|
|
|
|
|
|
|
2017-06-29 17:20:00 +02:00
|
|
|
|
## Routing
|
|
|
|
|
|
Choo is an application level framework. This means that it takes care of
|
|
|
|
|
|
everything related to routing and pathnames for you.
|
|
|
|
|
|
|
|
|
|
|
|
### Params
|
|
|
|
|
|
Params can be registered by prepending the routename with `:routename`, e.g.
|
|
|
|
|
|
`/foo/:bar/:baz`. The value of the param will be saved on `state.params` (e.g.
|
|
|
|
|
|
`state.params.bar`). Wildcard routes can be registered with `*`, e.g. `/foo/*`.
|
|
|
|
|
|
The value of the wildcard will be saved under `state.params.wildcard`.
|
|
|
|
|
|
|
|
|
|
|
|
### Default routes
|
|
|
|
|
|
Sometimes a route doesn't match, and you want to display a page to handle it.
|
|
|
|
|
|
You can do this by declaring `app.route('*', handler)` to handle all routes
|
|
|
|
|
|
that didn't match anything else.
|
|
|
|
|
|
|
|
|
|
|
|
### Querystrings
|
|
|
|
|
|
Querystrings (e.g. `?foo=bar`) are ignored when matching routes. An object
|
|
|
|
|
|
containing the key-value mappings exists as `state.query`.
|
|
|
|
|
|
|
|
|
|
|
|
### Hash routing
|
|
|
|
|
|
Using hashes to delimit routes is supported out of the box (e.g. `/foo#bar`).
|
|
|
|
|
|
When a hash is found we also check if there's an available anchor on the same
|
|
|
|
|
|
page, and will scroll the screen to the position. Using both hashes in URLs and
|
|
|
|
|
|
anchor links on the page is generally not recommended.
|
|
|
|
|
|
|
|
|
|
|
|
### Following links
|
2017-07-03 10:48:38 +02:00
|
|
|
|
By default all clicks on `<a>` tags are handled by the router through the
|
2017-08-02 14:06:47 +02:00
|
|
|
|
[nanohref](https://github.com/choojs/nanohref) module. This can be
|
2017-06-29 17:20:00 +02:00
|
|
|
|
disabled application-wide by passing `{ href: false }` to the application
|
2017-07-03 10:48:38 +02:00
|
|
|
|
constructor. The event is not handled under the following conditions:
|
|
|
|
|
|
- the click event had `.preventDefault()` called on it
|
|
|
|
|
|
- the link has a `target="_blank"` attribute with `rel="noopener noreferrer"`
|
|
|
|
|
|
- a modifier key is enabled (e.g. `ctrl`, `alt`, `shift` or `meta`)
|
|
|
|
|
|
- the link's href starts with protocol handler such as `mailto:` or `dat:`
|
|
|
|
|
|
- the link points to a different host
|
|
|
|
|
|
- the link has a `download` attribute
|
|
|
|
|
|
|
|
|
|
|
|
:warn: Note that we only handle `target=_blank` if they also have
|
|
|
|
|
|
`rel="noopener noreferrer"` on them. This is needed to [properly sandbox web
|
|
|
|
|
|
pages](https://mathiasbynens.github.io/rel-noopener/).
|
2017-06-29 17:20:00 +02:00
|
|
|
|
|
|
|
|
|
|
### Navigating programmatically
|
2018-02-15 12:02:21 +01:00
|
|
|
|
To navigate routes you can emit `'pushState'`, `'popState'` or
|
2017-06-29 17:20:00 +02:00
|
|
|
|
`'replaceState'`. See [#events](#events) for more details about these events.
|
|
|
|
|
|
|
2017-06-28 15:53:37 +02:00
|
|
|
|
## Server Rendering
|
2017-10-20 16:30:58 +02:00
|
|
|
|
Choo was built with Node in mind. To render on the server call
|
2018-02-15 12:02:21 +01:00
|
|
|
|
`.toString(route, [state])` on your `choo` instance.
|
2017-06-28 15:53:37 +02:00
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
|
var html = require('choo/html')
|
|
|
|
|
|
var choo = require('choo')
|
|
|
|
|
|
|
|
|
|
|
|
var app = choo()
|
|
|
|
|
|
app.route('/', function (state, emit) {
|
|
|
|
|
|
return html`<div>Hello ${state.name}</div>`
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
var state = { name: 'Node' }
|
|
|
|
|
|
var string = app.toString('/', state)
|
|
|
|
|
|
|
|
|
|
|
|
console.log(string)
|
|
|
|
|
|
// => '<div>Hello Node</div>'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2017-10-20 16:30:58 +02:00
|
|
|
|
When starting an application in the browser, it's recommended to provide the
|
|
|
|
|
|
same `state` object available as `window.initialState`. When the application is
|
|
|
|
|
|
started, it'll be used to initialize the application state. The process of
|
|
|
|
|
|
server rendering, and providing an initial state on the client to create the
|
|
|
|
|
|
exact same document is also known as "rehydration".
|
|
|
|
|
|
|
|
|
|
|
|
For security purposes, after `window.initialState` is used it is deleted from
|
|
|
|
|
|
the `window` object.
|
|
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
|
<html>
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<script>window.initialState = { initial: 'state' }</script>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2018-07-10 14:40:51 +02:00
|
|
|
|
## Components
|
|
|
|
|
|
From time to time there will arise a need to have an element in an application
|
|
|
|
|
|
hold a self-contained state or to not rerender when the application does. This
|
|
|
|
|
|
is common when using 3rd party libraries to e.g. display an interactive map or a
|
|
|
|
|
|
graph and you rely on this 3rd party library to handle modifications to the DOM.
|
|
|
|
|
|
Components come baked in to Choo for these kinds of situations. See
|
|
|
|
|
|
[nanocomponent][nanocomponent] for documentation on the component class.
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// map.js
|
|
|
|
|
|
var html = require('choo/html')
|
|
|
|
|
|
var mapboxgl = require('mapbox-gl')
|
|
|
|
|
|
var Component = require('choo/component')
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = class Button extends Component {
|
|
|
|
|
|
constructor (id, state, emit) {
|
|
|
|
|
|
super(id)
|
|
|
|
|
|
this.local = state.components[id] = {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
load (element) {
|
|
|
|
|
|
this.map = new mapboxgl.Map({
|
|
|
|
|
|
container: element,
|
|
|
|
|
|
center: this.local.center
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
update (center) {
|
|
|
|
|
|
if (center.join() !== this.local.center.join()) {
|
|
|
|
|
|
this.map.setCenter(center)
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
createElement (center) {
|
|
|
|
|
|
this.local.center = center
|
|
|
|
|
|
return html`<div></div>`
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// index.js
|
|
|
|
|
|
var choo = require('choo')
|
|
|
|
|
|
var html = require('choo/html')
|
|
|
|
|
|
var Map = require('./map.js')
|
|
|
|
|
|
|
|
|
|
|
|
var app = choo()
|
|
|
|
|
|
app.route('/', mainView)
|
|
|
|
|
|
app.mount('body')
|
|
|
|
|
|
|
|
|
|
|
|
function mainView (state, emit) {
|
|
|
|
|
|
return html`
|
|
|
|
|
|
<body>
|
|
|
|
|
|
<button onclick=${onclick}>Where am i?</button>
|
|
|
|
|
|
${state.cache(Map, 'my-map').render(state.center)}
|
|
|
|
|
|
</body>
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
function onclick () {
|
|
|
|
|
|
emit('locate')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
app.use(function (state, emitter) {
|
|
|
|
|
|
state.center = [18.0704503, 59.3244897]
|
|
|
|
|
|
emitter.on('locate', function () {
|
|
|
|
|
|
window.navigator.geolocation.getCurrentPosition(function (position) {
|
|
|
|
|
|
state.center = [position.coords.longitude, position.coords.latitude]
|
|
|
|
|
|
emitter.emit('render')
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Caching components
|
|
|
|
|
|
When working with stateful components, one will need to keep track of component
|
|
|
|
|
|
instances – `state.cache` does just that. The component cache is a function
|
|
|
|
|
|
which takes a component class and a unique id (`string`) as it's first two
|
|
|
|
|
|
arguments. Any following arguments will be forwarded to the component contructor
|
|
|
|
|
|
together with `state` and `emit`.
|
|
|
|
|
|
|
|
|
|
|
|
The default class cache is an LRU cache (using [nanolru][nanolru]), meaning it
|
|
|
|
|
|
will only hold on to a fixed amount of class instances (`100` by default) before
|
|
|
|
|
|
starting to evict the least-recently-used instances. This behavior can be
|
|
|
|
|
|
overriden with [options](#app--chooopts).
|
|
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
## Optimizations
|
|
|
|
|
|
Choo is reasonably fast out of the box. But sometimes you might hit a scenario
|
|
|
|
|
|
where a particular part of the UI slows down the application, and you want to
|
|
|
|
|
|
speed it up. Here are some optimizations that are possible.
|
2016-05-19 13:26:11 +09:00
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
### Caching DOM elements
|
|
|
|
|
|
Sometimes we want to tell the algorithm to not evaluate certain nodes (and its
|
|
|
|
|
|
children). This can be because we're sure they haven't changed, or perhaps
|
|
|
|
|
|
because another piece of code is managing that part of the DOM tree. To achieve
|
|
|
|
|
|
this `nanomorph` evaluates the `.isSameNode()` method on nodes to determine if
|
|
|
|
|
|
they should be updated or not.
|
2016-07-05 18:52:51 +02:00
|
|
|
|
|
2016-07-20 18:30:19 -07:00
|
|
|
|
```js
|
2017-03-21 03:00:45 +01:00
|
|
|
|
var el = html`<div>node</div>`
|
2016-12-11 19:35:29 +01:00
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
// tell nanomorph to not compare the DOM tree if they're both divs
|
|
|
|
|
|
el.isSameNode = function (target) {
|
|
|
|
|
|
return (target && target.nodeName && target.nodeName === 'DIV')
|
2016-06-20 23:57:46 -04:00
|
|
|
|
}
|
|
|
|
|
|
```
|
2016-12-11 19:35:29 +01:00
|
|
|
|
|
2017-06-28 15:53:37 +02:00
|
|
|
|
### Reordering lists
|
|
|
|
|
|
It's common to work with lists of elements on the DOM. Adding, removing or
|
|
|
|
|
|
reordering elements in a list can be rather expensive. To optimize this you can
|
|
|
|
|
|
add an `id` attribute to a DOM node. When reordering nodes it will compare
|
|
|
|
|
|
nodes with the same ID against each other, resulting in far fewer re-renders.
|
|
|
|
|
|
This is especially potent when coupled with DOM node caching.
|
|
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
|
var el = html`
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<div id="first">hello</div>
|
|
|
|
|
|
<div id="second">world</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
`
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
### Pruning dependencies
|
|
|
|
|
|
We use the `require('assert')` module from Node core to provide helpful error
|
|
|
|
|
|
messages in development. In production you probably want to strip this using
|
|
|
|
|
|
[unassertify][unassertify].
|
2016-07-21 21:59:01 +02:00
|
|
|
|
|
2018-06-05 15:58:08 +02:00
|
|
|
|
To convert inlined HTML to valid DOM nodes we use `require('nanohtml')`. This has
|
2017-03-21 03:00:45 +01:00
|
|
|
|
overhead during runtime, so for production environments we should unwrap this
|
2018-06-05 15:58:08 +02:00
|
|
|
|
using the [nanohtml transform][nanohtml].
|
2016-07-21 21:59:01 +02:00
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
Setting up browserify transforms can sometimes be a bit of hassle; to make this
|
2018-02-15 12:02:21 +01:00
|
|
|
|
more convenient we recommend using [bankai build][bankai] to build your assets for production.
|
2017-02-07 13:59:18 +01:00
|
|
|
|
|
2016-05-21 22:44:03 +09:00
|
|
|
|
## FAQ
|
2017-11-13 02:14:04 -08:00
|
|
|
|
### Why is it called Choo?
|
2016-05-22 20:54:39 +09:00
|
|
|
|
Because I thought it sounded cute. All these programs talk about being
|
2016-07-03 14:56:24 +02:00
|
|
|
|
_"performant"_, _"rigid"_, _"robust"_ - I like programming to be light, fun and
|
2017-11-13 02:14:04 -08:00
|
|
|
|
non-scary. Choo embraces that.
|
2016-05-22 20:54:39 +09:00
|
|
|
|
|
|
|
|
|
|
Also imagine telling some business people you chose to rewrite something
|
2017-03-21 03:00:45 +01:00
|
|
|
|
critical for serious bizcorp using a train themed framework.
|
2016-05-22 20:54:39 +09:00
|
|
|
|
:steam_locomotive::train::train::train:
|
|
|
|
|
|
|
2017-11-13 02:14:04 -08:00
|
|
|
|
### Is it called Choo, Choo.js or...?
|
|
|
|
|
|
It's called "Choo", though we're fine if you call it "Choo-choo" or
|
|
|
|
|
|
"Chugga-chugga-choo-choo" too. The only time "choo.js" is tolerated is if /
|
2016-07-03 14:56:24 +02:00
|
|
|
|
when you shimmy like you're a locomotive.
|
2016-05-23 14:40:52 +09:00
|
|
|
|
|
2017-11-13 02:14:04 -08:00
|
|
|
|
### Does Choo use a virtual-dom?
|
|
|
|
|
|
Choo uses [nanomorph][nanomorph], which diffs real DOM nodes instead of
|
2017-03-21 03:00:45 +01:00
|
|
|
|
virtual nodes. It turns out that [browsers are actually ridiculously good at
|
|
|
|
|
|
dealing with DOM nodes][morphdom-bench], and it has the added benefit of
|
|
|
|
|
|
working with _any_ library that produces valid DOM nodes. So to put a long
|
|
|
|
|
|
answer short: we're using something even better.
|
2016-05-21 21:59:03 +09:00
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
### How can I support older browsers?
|
|
|
|
|
|
Template strings aren't supported in all browsers, and parsing them creates
|
|
|
|
|
|
significant overhead. To optimize we recommend running `browserify` with
|
2018-06-05 15:58:08 +02:00
|
|
|
|
[nanohtml][nanohtml] as a global transform or using [bankai][bankai] directly.
|
2016-05-24 22:11:21 +09:00
|
|
|
|
```sh
|
2018-06-05 15:58:08 +02:00
|
|
|
|
$ browserify -g nanohtml
|
2016-05-24 22:11:21 +09:00
|
|
|
|
```
|
2016-05-11 15:26:27 +07:00
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
### Is choo production ready?
|
2016-05-22 02:15:33 +09:00
|
|
|
|
Sure.
|
|
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
## API
|
2017-11-13 02:14:04 -08:00
|
|
|
|
This section provides documentation on how each function in Choo works. It's
|
2017-03-21 03:00:45 +01:00
|
|
|
|
intended to be a technical reference. If you're interested in learning choo for
|
|
|
|
|
|
the first time, consider reading through the [handbook][handbook] first
|
|
|
|
|
|
:sparkles:
|
|
|
|
|
|
|
|
|
|
|
|
### `app = choo([opts])`
|
|
|
|
|
|
Initialize a new `choo` instance. `opts` can also contain the following values:
|
2017-03-29 10:43:42 +02:00
|
|
|
|
- __opts.history:__ default: `true`. Listen for url changes through the
|
2017-03-21 03:00:45 +01:00
|
|
|
|
history API.
|
|
|
|
|
|
- __opts.href:__ default: `true`. Handle all relative `<a
|
|
|
|
|
|
href="<location>"></a>` clicks and call `emit('render')`
|
2018-07-10 14:40:51 +02:00
|
|
|
|
- __opts.cache:__ default: `undefined`. Override default class cache used by
|
|
|
|
|
|
`state.cache`. Can be a a `number` (maximum number of instances in cache,
|
|
|
|
|
|
default `100`) or an `object` with a [nanolru][nanolru]-compatible API.
|
2017-03-21 03:00:45 +01:00
|
|
|
|
|
2017-10-31 08:24:43 -03:00
|
|
|
|
### `app.use(callback(state, emitter, app))`
|
|
|
|
|
|
Call a function and pass it a `state`, `emitter` and `app`. `emitter` is an instance
|
2017-08-02 14:06:47 +02:00
|
|
|
|
of [nanobus](https://github.com/choojs/nanobus/). You can listen to
|
2017-06-28 15:53:37 +02:00
|
|
|
|
messages by calling `emitter.on()` and emit messages by calling
|
2017-10-31 08:24:43 -03:00
|
|
|
|
`emitter.emit()`. `app` is the same Choo instance. Callbacks passed to `app.use()` are commonly referred to as
|
2017-10-03 12:34:36 -04:00
|
|
|
|
`'stores'`.
|
|
|
|
|
|
|
|
|
|
|
|
If the callback has a `.storeName` property on it, it will be used to identify
|
|
|
|
|
|
the callback during tracing.
|
2017-03-21 03:00:45 +01:00
|
|
|
|
|
2017-06-28 15:53:37 +02:00
|
|
|
|
See [#events](#events) for an overview of all events.
|
2017-04-11 15:36:27 +03:00
|
|
|
|
|
2017-06-29 17:20:00 +02:00
|
|
|
|
### `app.route(routeName, handler(state, emit))`
|
|
|
|
|
|
Register a route on the router. The handler function is passed `app.state`
|
|
|
|
|
|
and `app.emitter.emit` as arguments. Uses [nanorouter][nanorouter] under the
|
|
|
|
|
|
hood.
|
2017-03-21 16:56:33 +01:00
|
|
|
|
|
2017-06-29 17:20:00 +02:00
|
|
|
|
See [#routing](#routing) for an overview of how to use routing efficiently.
|
2017-03-21 03:00:45 +01:00
|
|
|
|
|
|
|
|
|
|
### `app.mount(selector)`
|
2017-11-15 12:44:45 +01:00
|
|
|
|
Start the application and mount it on the given `querySelector`,
|
|
|
|
|
|
the given selector can be a String or a DOM element.
|
|
|
|
|
|
|
2018-02-13 22:09:08 +01:00
|
|
|
|
In the browser, this will _replace_ the selector provided with the tree returned from `app.start()`.
|
2017-11-15 12:44:45 +01:00
|
|
|
|
If you want to add the app as a child to an element, use `app.start()` to obtain the tree and manually append it.
|
2017-03-21 03:00:45 +01:00
|
|
|
|
|
2018-02-13 22:09:08 +01:00
|
|
|
|
On the server, this will save the `selector` on the app instance.
|
|
|
|
|
|
When doing server side rendering, you can then check the `app.selector` property to see where the render result should be inserted.
|
|
|
|
|
|
|
|
|
|
|
|
Returns `this`, so you can easily export the application for server side rendering:
|
|
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
|
module.exports = app.mount('body')
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2017-03-21 03:00:45 +01:00
|
|
|
|
### `tree = app.start()`
|
|
|
|
|
|
Start the application. Returns a tree of DOM nodes that can be mounted using
|
|
|
|
|
|
`document.body.appendChild()`.
|
|
|
|
|
|
|
|
|
|
|
|
### `app.toString(location, [state])`
|
|
|
|
|
|
Render the application to a string. Useful for rendering on the server.
|
|
|
|
|
|
|
2017-04-03 03:53:17 +02:00
|
|
|
|
### `choo/html`
|
2017-03-21 03:00:45 +01:00
|
|
|
|
Create DOM nodes from template string literals. Exposes
|
2018-06-05 15:58:08 +02:00
|
|
|
|
[nanohtml](https://github.com/choojs/nanohtml). Can be optimized using
|
|
|
|
|
|
[nanohtml][nanohtml].
|
2016-06-29 08:06:04 -07:00
|
|
|
|
|
2017-09-26 20:31:25 +01:00
|
|
|
|
### `choo/html/raw`
|
2018-06-05 15:58:08 +02:00
|
|
|
|
Exposes [nanohtml/raw](https://github.com/shama/nanohtml#unescaping) helper for rendering raw HTML content.
|
2017-09-26 20:31:25 +01:00
|
|
|
|
|
2016-05-10 23:23:33 +07:00
|
|
|
|
## Installation
|
|
|
|
|
|
```sh
|
|
|
|
|
|
$ npm install choo
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2016-05-22 20:54:39 +09:00
|
|
|
|
## See Also
|
2017-08-02 14:06:47 +02:00
|
|
|
|
- [bankai](https://github.com/choojs/bankai) - streaming asset compiler
|
2016-05-22 20:54:39 +09:00
|
|
|
|
- [stack.gl](http://stack.gl/) - open software ecosystem for WebGL
|
2016-06-26 00:34:00 +02:00
|
|
|
|
- [yo-yo](https://github.com/maxogden/yo-yo) - tiny library for modular UI
|
|
|
|
|
|
- [bel](https://github.com/shama/bel) - composable DOM elements using template
|
|
|
|
|
|
strings
|
2016-07-03 14:56:24 +02:00
|
|
|
|
- [tachyons](https://github.com/tachyons-css/tachyons) - functional CSS for
|
|
|
|
|
|
humans
|
|
|
|
|
|
- [sheetify](https://github.com/stackcss/sheetify) - modular CSS bundler for
|
|
|
|
|
|
`browserify`
|
2016-05-22 20:54:39 +09:00
|
|
|
|
|
2016-08-27 14:28:09 +02:00
|
|
|
|
## Support
|
|
|
|
|
|
Creating a quality framework takes a lot of time. Unlike others frameworks,
|
|
|
|
|
|
Choo is completely independently funded. We fight for our users. This does mean
|
|
|
|
|
|
however that we also have to spend time working contracts to pay the bills.
|
|
|
|
|
|
This is where you can help: by chipping in you can ensure more time is spent
|
|
|
|
|
|
improving Choo rather than dealing with distractions.
|
|
|
|
|
|
|
|
|
|
|
|
### Sponsors
|
|
|
|
|
|
Become a sponsor and help ensure the development of independent quality
|
|
|
|
|
|
software. You can help us keep the lights on, bellies full and work days sharp
|
|
|
|
|
|
and focused on improving the state of the web. [Become a
|
|
|
|
|
|
sponsor](https://opencollective.com/choo#sponsor)
|
|
|
|
|
|
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/0/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/0/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/1/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/1/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/2/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/2/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/3/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/3/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/4/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/4/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/5/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/5/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/6/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/6/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/7/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/7/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/8/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/8/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/9/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/9/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/10/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/10/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/11/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/11/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/12/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/12/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/13/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/13/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/14/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/14/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/15/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/15/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/16/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/16/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/17/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/17/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/18/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/18/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/19/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/19/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/20/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/20/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/21/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/21/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/22/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/22/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/23/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/23/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/24/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/24/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/25/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/25/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/26/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/26/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/27/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/27/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/28/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/28/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/sponsor/29/website" target="_blank"><img src="https://opencollective.com/choo/sponsor/29/avatar.svg"></a>
|
|
|
|
|
|
|
|
|
|
|
|
### Backers
|
|
|
|
|
|
Become a backer, and buy us a coffee (or perhaps lunch?) every month or so.
|
|
|
|
|
|
[Become a backer](https://opencollective.com/choo#backer)
|
|
|
|
|
|
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/0/website" target="_blank"><img src="https://opencollective.com/choo/backer/0/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/1/website" target="_blank"><img src="https://opencollective.com/choo/backer/1/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/2/website" target="_blank"><img src="https://opencollective.com/choo/backer/2/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/3/website" target="_blank"><img src="https://opencollective.com/choo/backer/3/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/4/website" target="_blank"><img src="https://opencollective.com/choo/backer/4/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/5/website" target="_blank"><img src="https://opencollective.com/choo/backer/5/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/6/website" target="_blank"><img src="https://opencollective.com/choo/backer/6/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/7/website" target="_blank"><img src="https://opencollective.com/choo/backer/7/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/8/website" target="_blank"><img src="https://opencollective.com/choo/backer/8/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/9/website" target="_blank"><img src="https://opencollective.com/choo/backer/9/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/10/website" target="_blank"><img src="https://opencollective.com/choo/backer/10/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/11/website" target="_blank"><img src="https://opencollective.com/choo/backer/11/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/12/website" target="_blank"><img src="https://opencollective.com/choo/backer/12/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/13/website" target="_blank"><img src="https://opencollective.com/choo/backer/13/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/14/website" target="_blank"><img src="https://opencollective.com/choo/backer/14/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/15/website" target="_blank"><img src="https://opencollective.com/choo/backer/15/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/16/website" target="_blank"><img src="https://opencollective.com/choo/backer/16/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/17/website" target="_blank"><img src="https://opencollective.com/choo/backer/17/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/18/website" target="_blank"><img src="https://opencollective.com/choo/backer/18/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/19/website" target="_blank"><img src="https://opencollective.com/choo/backer/19/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/20/website" target="_blank"><img src="https://opencollective.com/choo/backer/20/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/21/website" target="_blank"><img src="https://opencollective.com/choo/backer/21/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/22/website" target="_blank"><img src="https://opencollective.com/choo/backer/22/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/23/website" target="_blank"><img src="https://opencollective.com/choo/backer/23/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/24/website" target="_blank"><img src="https://opencollective.com/choo/backer/24/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/25/website" target="_blank"><img src="https://opencollective.com/choo/backer/25/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/26/website" target="_blank"><img src="https://opencollective.com/choo/backer/26/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/27/website" target="_blank"><img src="https://opencollective.com/choo/backer/27/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/28/website" target="_blank"><img src="https://opencollective.com/choo/backer/28/avatar.svg"></a>
|
|
|
|
|
|
<a href="https://opencollective.com/choo/backer/29/website" target="_blank"><img src="https://opencollective.com/choo/backer/29/avatar.svg"></a>
|
|
|
|
|
|
|
2016-05-10 23:23:33 +07:00
|
|
|
|
## License
|
|
|
|
|
|
[MIT](https://tldrlegal.com/license/mit-license)
|
|
|
|
|
|
|
2018-07-10 14:40:51 +02:00
|
|
|
|
[nanocomponent]: https://github.com/choojs/nanocomponent
|
|
|
|
|
|
[nanolru]: https://github.com/s3ththompson/nanolru
|
2017-08-02 14:06:47 +02:00
|
|
|
|
[bankai]: https://github.com/choojs/bankai
|
2016-07-03 14:56:24 +02:00
|
|
|
|
[bel]: https://github.com/shama/bel
|
2018-06-05 15:58:08 +02:00
|
|
|
|
[nanohtml]: https://github.com/choojs/nanohtml
|
2016-09-02 21:02:41 -04:00
|
|
|
|
[browserify]: https://github.com/substack/node-browserify
|
2016-10-23 00:16:33 -07:00
|
|
|
|
[budo]: https://github.com/mattdesl/budo
|
|
|
|
|
|
[es2020]: https://github.com/yoshuawuyts/es2020
|
2016-07-05 18:52:51 +02:00
|
|
|
|
[handbook]: https://github.com/yoshuawuyts/choo-handbook
|
2016-10-23 00:16:33 -07:00
|
|
|
|
[hyperx]: https://github.com/substack/hyperx
|
|
|
|
|
|
[morphdom-bench]: https://github.com/patrick-steele-idem/morphdom#benchmarks
|
2017-08-02 14:06:47 +02:00
|
|
|
|
[nanomorph]: https://github.com/choojs/nanomorph
|
|
|
|
|
|
[nanorouter]: https://github.com/choojs/nanorouter
|
2016-10-23 00:16:33 -07:00
|
|
|
|
[yo-yo]: https://github.com/maxogden/yo-yo
|
2017-03-21 03:00:45 +01:00
|
|
|
|
[unassertify]: https://github.com/unassert-js/unassertify
|
2017-03-29 10:43:42 +02:00
|
|
|
|
[window-performance]: https://developer.mozilla.org/en-US/docs/Web/API/Performance
|