Minimal
Tiny (~4kB), super-fast, refined, reactive, fractal, unidirectional, isomorphic, pro-standards, declarative, Just Works™ framework for frontend development. There's only one API:
// getter // setterripple
Get and set things in single store. A change
event is emitted when something is updated, enabling reactive updates.
## Components
Define a component:
Use it on the page:
Ripple is agnostic to how you write your components, they should just be idempotent (a single render function). This is fine:
Or using some DOM-diff helper:
Or using once/D3 joins:
For more info about writing idempotent components, see this spec.
## State/Data
The first parameter of the component is the node to update.
The second parameter contains all the state and data the component needs to render:
{ ... }
You can pass down data by adding the name of the resources to the data attribute:
{ ... }
Declaring the data needed on a component is used to reactively rerender it when the data changes.
The other option is to explicitly pass down data to the component using the (D3) data binding:
'my-shop' stock
If you want to just use DOM, you can invoke .draw()
on a custom element to redraw it:
const shop = documentdocumentbodyshopstate = stock shop
## Defaults
You can set defaults using the ES6 syntax:
{ ... }
If you need to persist defaults on the component's state object, you can use a small helper function:
{ const stock = }
## Updates
Local state
Whenever you need to update local state, just change the state
and invoke a redraw (like a game loop):
{ const o = counter = 0 = state textcounter text'increment' }
Global state
Whenever you need to update global state, you can simply compute the new value and register it again which will trigger an update:
Or if you just want to change a part of the resource, use a functional operator to apply a finer-grained diff and trigger an update:
// same as: set({ type: 'update', key: 'pomegranate', value: 20 })(ripple('stock'))
Using logs of atomic diffs combines the benefits of immutability with a saner way to synchronise state across a distributed environment.
You can also use the list of all relevant changes since the last render in your component via element.changes
to make it more performant.
## Events
Dispatch an event on the root element to communicate changes to parents (node.dispatchEvent
).
## Routing
Just invoke a redraw of your application when the route has changed:
{ const o = text'You are currently on: ' + locationpathname window}
Decouter emitterifies window
to give you the change
event, go(url)
for navigating, and sets location.params
with current route parameters.
## Bundling
Ripple does not care how you load/bundle your resources. You only just need to register them at some point. This means you are free to use whatever tool chain you like:
// index.js
$ browserify index.js > app.js
An application is just a component that composes other components, so you shouldn't need any other scripts.
## Folder Convention
I recommend using the folder convention: a resources
directory, with a folder for each component, and a data
folder for data resources.
resources
├── data
│ ├── stock.js
│ └── order.js
└── my-app
│ ├── my-app.js
│ ├── my-app.css
│ └── test.js
└── another-component
├── another-component.js
├── another-component.css
└── test.js
You can then use a helper script to automatically generate a single require
able index.js
from a directory of resources.
## Debugging
-
Check
ripple.resources
for a snapshot of your application. Resources are in the tuple format{ name, body, headers }
. -
Check
$0.state
on an element to see the state object it was last rendered with or manipulate it.
## Middleware
By default the draw function just invokes the function on an element. You can extend this without any framework hooks using the explicit decorator pattern:
// in component{ } // around component{ } // for all componentsrippledraw =
A couple of useful middleware included in this build are:
Needs
This middleware reads the needs
header and applies the attributes onto the element. The component does not render until all dependencies are available. This is useful when a component needs to define its own dependencies.
name: 'my-component' {} headers: needs: '[css=..][data=..]'
Helpers
This middleware makes the specified helper functions available from the resource (hidden properties). This is useful to co-locate all logic for each resource in one place.
name: 'stock' body: {} headers: helpers: addNewStock removeStock
Styling
Stylesheet(s) can be modularly applied to an element: This middlware simply reads the css
attribute and inserts them in the shadow root or scopes them and adds to head
:
## Fullstack
If you have a backend for your frontend, checkout rijs/fullstack which transparently adds a few more modules to synchronise state between client-server or for more docs.
You can also adjust your own framework by adding/removing modules.
## Flavours
dist/ripple.js
provides ripple
and also some small, useful, high power-to-weight ratio functions that enriches the language grammar. If you don't want the helper functions, use dist/ripple.pure.js
. Add .min
for prod. Minified and gzipped the sizes are ~12kB and ~4kB respectively.