DIO.js

Dio is a blazing fast, lightweight (~10kb) feature rich Virtual DOM framework.
- ~10kb minified+gzipped
- ~25kb minified

Browser Support
- Edge
- IE 9+
- Chrome
- Firefox
- Safari
Installation
direct download
<script src=dio.min.js></script>
CDN
<script src=https://cdnjs.cloudflare.com/ajax/libs/dio/3.1.0/dio.min.js></script>
<script src=https://cdn.jsdelivr.net/dio/3.1.0/dio.min.js></script>
<script src=https://unpkg.com/dio.js@3.1.0/dio.min.js></script>
bower
bower install dio.js
npm
npm install dio.js --save
You can also play with Dio on this jsbin
Getting Started
Dio is a blazing fast, lightweight (~10kb) feature rich Virtual DOM framework
built around the concept that any function/object can become a component.
Components in Dio share the same api's as react with a few additions,
this means that you can easily port both code and and your knowledge of the api
both ways between Dio and React as and when needed.
Having said that Dio can be used as just a "view" library but it does come
self containeed with everything you would need to build an application,
from routing, http requests, server-side rendering to testing, state stores, animations and more.
In that respect this getting started guide aims to show you how to go from zero to hello world in Dio.
Hello world
function HelloWorld () {
return {
render: function (props) {
return h('h1', props.text);
}
}
}
dio.render(HelloWorld)({text: 'Hello World'});
Will mount a h1 element onto the page the contents of which will be 'Hello World'.
Performance
API Reference
components schema
{
shouldComponentUpdate: (newProps, newState) => {}
componentWillReceiveProps: (newProps) => {}
componentWillUpdate: (newProps, newState) => {}
componentDidUpdate: (oldProps, oldState) => {}
componentWillMount: () => {}
componentDidMount: () => {}
componentWillUnmount: () => {}
displayName: {string}
propTypes: {Object}
statics: {Object}
render: (props, state, this) => {}
stylesheet: () => ''
this.forceUpdate: ()
this.setState: ({Object}, callback: {function=})
this.withAttr ({string|string[]}, {function|function[]})
this.autoBind (string[])
}
creating hyperscript objects
h(
type: {String},
props?|children?: {Object} | {Array|Object}...,
children?: {Array|Object}...
)
h('.wrap')
h('input[type=checkbox]')
h('input', {value: 'text', onInput: ()=>{}})
h('div', 'Text')
h('div', h('span', 'Text'))
h('div', [h('h1', '1st'), h('h1', '2nd'), ...])
h('div', {innerHTML: "<script>alert('hello')</script>"});
h(Component, {who: 'World'}, 1, 2, 3, 4)
h('div', ComponentFunction)
h('div', Component)
h('@fragment', 'Hello', 'World')
h('@foo', 'Hello', 'World')
{
nodeType: 1,
type: 'div',
props: {},
children: [
{
nodeType: 3,
type: 'text',
props: {},
children: 'Hello World'
}
]
}
h('div', 'Hello World');
dio.createClass
dio.createClass({Function|Object})
class Component extends dio.Component {
render() {
}
}
var myComponent = dio.createClass({
render: () => { return h('div') }
});
var myComponent = dio.createClass(function () {
return {
render: () => { return h('div') }
};
});
class myComponent extends dio.Component {
render() {
return h('div')
}
}
dio.render
dio.render(component: {function|Object},mount?: {string|Element})
var instance = dio.render(Component);
instance(props: {Object});
dio.render(h(Component, {...props}, ...children));
component examples.
function () {
return h('div', 'Hello World')
}
function () {
return {
render: function () {
return h('div', 'Hello World')
}
}
}
h('div', 'Hello World');
{
render: function () {
return h('div', 'Hello World')
}
}
dio.createClass({
render: function () {
return h('div', 'Hello World')
}
})
dio.createClass(function () {
return {
render: function () {
return h('div', 'Hello World')
}
}
})
class Component extends dio.Component {
constructor(props) {
super(props)
}
render() {
return h('div', 'Hello World')
}
}
Components created with .createClass/extends dio.Component
are
statefull by default. There are also other scenarios that pure functions
may become statefull, below are some examples.
function Pure () {
return {
render: function () {
return h('h1');
}
}
}
function Parent () {
return {
render: function () {
return h('div', Pure);
}
}
}
var render = dio.render(Pure);
var render = dio.render(Parent);
Note that in the getting started section 'Hello World'
we did not create a component with dio.createClass()
but rather just used a pure function that we passed
to dio.render(here)
this is because
.render
will create a component if
what is passed to it is not already a component as detailed above.
mount examples.
document
document.body
document.querySelector('.myapp')
'.myapp'
document.createElement('div')
document.createDocumentFragment();
function el () {
return document.body;
}
dio.render(__, mount);
How do i render one component within another?
Components are for the most part functions(statefull/stateless),
to render place them h('div', A)
or h('div', h(A, {}, []))
.
note: render instances(created with .render) are not components
function () {
var elementHolder = dio.stream();
return {
render: function () {
return h('div', {ref: elementHolder},
Foo({text: 'Hello'}),
Bar({text: 'World'})
)
}
}
}
dio.Component.prototype.stylesheet
Before we continue to server-side rendering to explain the stylesheet signature
that appeared in the schema. It allows use to define a component as follows
class Button extends Component {
stylesheet () {
return `
{
color: black;
border: 1px solid red;
padding: 10px;
}
`
}
render () {
return button(null, [text('Click Me')]);
}
}
The return value of the stylesheet method when it exists is expected to
be a string representing css for that component.
The returned styles will be applied to every instance of that component.
Behind the scenes when dio first encounters an element with a stylesheet method
it parses the css returned, prefixes(if applicable), namespaces and caches the output of this.
It will then add this output(string) to the document(browser) or template(server-side).
prefixes supported are appearance, transform, keyframes & animation.
@keyframes are namespaced and so are animations to match. Eveything else works
as it would with regular css with the addition of &{}
and {}
which match the component itself
for example
`
{
color: 'red'
}
&, & h1{
color: 'blue'
}
`
dio.renderToString
Like the name suggests this methods outputs the html
of a specific component this is normally used for server side rendering.
When used as a server-side tool adding the attribute hydrate
will tell dio to hydrate the present structure.
dio.renderToString(
subject: {(function|Object|VNode[])},
template: {(string|function)=}
)
dio.renderToString(h('div', 'Text'));
dio.renderToString(Component);
const http = require('http');
const dio = require('./dio.js');
const {Component, renderToString} = dio;
const {button, text, h1} = dio.DOM(['button', 'h1', 'text']);
class Button extends Component {
stylesheet () {
return `
{
color: black;
border: 1px solid red;
padding: 10px;
}
`
}
render () {
return button(null, [text('Click Me')]);
}
}
class Heading extends Component {
render () {
return h1(null, [text('Hello World')])
}
}
const body = renderToString([Heading, Button], `
<html>
<head>
<title>Example</title>
{{style}}
</head>
<body hydrate>
{{body}}
</body>
</html>
`);
const body = renderToString([Heading, Button], function (body, style) {
return `
<html>
<head>
<title>Example</title>
${style}
</head>
<body hydrate>
${body}
</body>
</html>
`;
});
http.createServer(function(request, response) {
response.writeHeader(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}).listen(3000, '127.0.0.1');
`
<html>
<head>
<title>Example</title>
<style id="ButtontJroa">[data-scope=ButtontJroa] {color:black;border:1px solid red;padding:10px;}</style>
</head>
<body hydrate>
<h1>Hello World</h1><button data-scope="ButtontJroa">Click Me</button>
</body>
</html>
`
dio.router
dio.router(
routes: {(function|Object)},
rootAddress: {string=},
onInit: {(string|function)=},
mount: {(string|Node)=}
middleware: {function}
)
dio.router({(function|Object)}, {
root: {string=},
init: {(string|function)=},
mount: {(string|Node)=}
middleware: {function}
})
example router
dio.router({
'/': function (data) {
dio.render(h(home, data))
},
'/user/:id': function (data) {
dio.render(H(user, data));
}
}, '/backend', '/user/sultan')
dio.router({
'/': ComponentA,
'/user/:id': ComponentA
}, {
mount: document.body
});
You can then assign this route to a variable and use it to navigate across views
var router = dio.router({
'/': () => {...}
'/user/:id': () => {...}
});
router.nav('/user/sultan')
router.back()
router.go(-2)
router.forward()
router.link('href')
h('h1', {onClick: router.link('href'), href: '/'}, 'Home')
h('h1', {onClick: router.link('/'), href: '/'}, 'Home')
h('h1', {onClick: router.link(el => el.getAttribute('href')), href: '/'}, 'Home')
h('a', {href: '/about', onClick: router.link(
function () {
return this.getAttribute('href');
}
)});
dio.createStore
dio.createStore(
reducer: {(function|Object)},
initalState: {Any}
enhancer: {Function}
)
Rather exactly like redux createStore
with the different of .connect
that accepts a component & mount/callback
with which to update everytime the store is updated.
Which is mostly a short hand for creating a listerner with .subscribe
that updates your component on state changes.
var store = dio.createStore(reducer: {Function})
var store = dio.createStore(object of reducers: {Object})
store.dispatch({type: '' ...})
store.getState()
store.subscribe(listener: {Function})
store.connect(callback: {function})
store.connect(callback: {(function|Object)}, element: {(string|Node)})
dio.stream
dio.stream(store: {Any|Function}, mapper);
var alwaysString = dio.stream(100, String);
alwaysString()
var foo = dio.stream('initial value')
foo('changed value')
foo('changed')('again')
foo()
var bar = foo.map(function(foo){
return foo + ' and bar';
});
bar()
foo('hello world')
bar()
var faz = dio.stream.combine(function(foo, bar, prevValueOfFaz){
return foo() + bar();
}, foo, bar);
foo(1)
bar(2)
faz()
faz.then(function(faz){
console.log(faz);
});
faz('changed')
faz.then(fn).then(fn)....
dio.stream.all([dep1, dep2]).then(fn);
var async = dio.stream(function (resolve, reject) {
setTimeout(resolve, 500, 'value');
});
var async = dio.stream(function () {
setTimeout(reject, 500, 'just because');
}).then(function (value) {
console.log(value + 'resolved')
}).catch(function (reason) {
console.log('why:' + reason)
});
var foo = dio.stream(function (resolve, reject) {
xhr.onload = resolve;
xhr.onerror = reject;
xhr.send();
});
foo.then(function (value) { return 100 })
.then(function (value) { console.log(value+20) })
.catch(function (value) { return 100 })
.catch(function (value) { console.log(value+200) })
var numbers = stream();
var sum = stream.scan(function(sum, numbers) {
return sum + numbers();
}, 0, numbers);
numbers(2)(3)(5);
sum();
dio.defer
var foo = dio.defer(
fn: {function},
args...: {any[]},
preventDefault: {boolean},
)
onClick: dio.defer(
(a) => {'look no e.preventDefault()'},
['a'],
true
)
function DoesOneThing (component, arg1, arg2) {
component.setState({...});
}
h('input', {
onInput: dio.defer(DoesOneThing, [this, 1, 2], true)
})
dio.createFactory
dio.createFactory(element);
Like it would work in react createFactory returns a function that
produces a hyperscript element of a single given type.
var div = dio.createFactory('div');
h('div', 'Hello World');
div('Hello World');
dio.findDOMNode
Given a component findDOMNode will return the dom node
attached to that component if the component is mounted.
class Component extends dio.Component {
componentDidMount(){
var node = dio.findDOMNode(this);
}
render(){
return h('div')
}
}
dio.DOM
creates common element factories
var {div, li, input} = dio.DOM();
h('div', {}, [div('input')])
div({}, [input()])
[
'h1','h2','h3','h4','h5', 'h6','audio','video','canvas',
'header','nav','main','aside','footer','section','article','div',
'form','button','fieldset','form','input','label','option','select','textarea',
'ul','ol','li','p','a','pre','code','span','img','strong','time','small','hr','br',
'table','tr','td','th','tbody','thead',
];
[
'rect','path','polygon','circle','ellipse','line','polyline','image','marker','a','symbol',
'linearGradient','radialGradient','stop','filter','use','clipPath','view','pattern','svg',
'g','defs','text','textPath','tspan','mpath','defs','g','marker','mask'
];
dio.request
a http helper that makes ajax requests.
dio.request(
url: {String},
payload?: {Object},
enctype?: {String},
withCredentials?: {Boolean},
initial?: {Any},
config?: {Function},
username?: {String},
password?: {String}
)
dio.request.post('/url', {id: 1234}, 'json')
.then((res)=>{return res})
.then((res)=>{'do something'})
.catch((err)=>{throw err});
dio.request({
method: 'GET',
url: '/url',
payload: {id: 1234},
enctype: 'json',
withCredentials: false,
config: function (xhr) {
xhr.setRequestHeader('x-auth-token',"xxxx");
}
}).then((res)=>{return res}).catch((err)=>{throw err});
dio.animateWith
dio.animateWith.flip(
className: {string},
duration: {number},
transform: {string},
transformOrigin: {string},
easing: {string}
)(
element: {(Node|string)}
)
dio.animateWith.transitions(
className: {string},
type?: {(string|number|boolean)}
)(
element: {Node},
callback: {function} => (element: {Node}, transitions: {function})
)
dio.animateWith.animations(...)
for example dio.animateWith.flip
can be used within a render as follows
render: function () {
return h('.card',
{
onclick: dio.animateWith.flip('active-state', 200)
},
''
)
}
since dio.animateWith.flip(...)
returns a function this is the same as
dio.animateWith.flip('active-state', 200)(elementNode)
another animation helper is animateWith.transitions
and animateWith.animations
the callback function
supplied after the element will execute after the
resulting animation/transition from adding/removing the class completes.
handleDelete: function (e) {
var element = e.currentTarget,
self = this
dio.animateWith.transitions('slideUp')(element, function(el, next) {
store.dispatch({type: 'DELETE', id: 1234});
next('slideLeft')(el, function (el, next) {
next('slideLeft', -1)(el);
});
})
}
dio.PropTypes
validates props passed to components insuring they are
of the the specificied type. The built in validtors work
exactly as they do in react land namely...
[
number, string, bool, array, object, func, element, node
any, shape, instanceOf, oneOf, oneOfType, arrayOf, objectOf
]
It should however be noted that propTypes/validations are only evaluated
when process.env.NODE_ENV
is set to 'development'
or
if you set dio.enviroment = 'development'
dio.createClass({
propTypes: {
id: dio.PropTypes.string.isRequired,
name: dio.PropTypes.string,
custom: function (
props,
propName,
displayName,
createInvalidPropTypeError,
createRequiredPropTypeError
) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + displayName + '`. Validation failed.'
);
}
}
}
render: function () {
}
})
createInvalidPropTypeError(
propName: {string},
propValue: {any},
displayName: {string},
expectedType: {string}
)
createRequiredPropTypeError(
propName: {string},
displayName: {string}
)
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
return createInvalidPropTypeError(
propName, props[propName], displayName, 'expected type'
)
dio.window
assigns a mock window object to be used for writting tests for
features that do not exist outside of browser enviroment,
like XMLHttpRequest
and #document
operations.
dio.window = {Object}
Utilities
{
panic
sandbox
compose
random
flatten
input
isObject
isFunction
isString
isArray
isDefined
isNumber
isArrayLike
}
Performance Tips
The hyperscript helper h
for creating vnodes is very versatile,
however if you are trying to draw the most out of performance
the following way of creating vnodes are the most optimal performance wise..
{
nodeType: 1,
type: 'div',
props: {},
children: [
{
nodeType: 3,
type: 'text',
props: {},
children: 'Hello World'
}
]
}
dio.VElement('div', {}, [VText('Hello World')])
dio.VComponent(Component)
dio.VFragment([...])
dio.VSvg({}, [...])
dio.VText('Content')
dio.VBlueprint(vnode);