dio.js

Dio is a fast and lightweight (~7kb) feature rich Virtual DOM framework.
- ~7kb minified+gzipped
- ~17kb 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/1.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 fast and lightweight (~7kb) 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 ways between Dio and React as and when needed
without any significant changes to the code base,
the minimal change in most cases being a simple React, ReactDOM = dio
.
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.
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.createRender(HelloWorld)({text: 'Hello World'})
Will mount a h1 element onto the .app
div the contents of which will be 'Hello World.
Performance
Single File Components
Single file components are self contained components with their own stylesheets,
this is possible with the use of dio.createStyle
allowing us to create a component
within one file or function that has no external dependencies.
var namespace = '#id';
var styles = {
'h1': {
'font-size': '40px',
width: function () {
return window.innerWidth
},
'&:hover': {
color: 'blue'
},
'&:after': {
content: ' '
},
'@keyframes blink': {
0: {
backgroundColor: 'blue'
},
50: {
backgroundColor: 'black'
}
}
}
}
dio.createStyle(styles, namespace);
function HelloWorld () {
return {
render: function (props) {
return h('div'+namespace,
h('h1', props.text)
)
}
}
}
dio.createRender(HelloWorld, '.app')({text: 'Hello World'});
API Reference
components schema
{
shouldComponentUpdate: (props, state, this) => {}
componentWillReceiveProps: (props, state, this) => {}
componentWillUpdate: (props, state, this) => {}
componentDidUpdate: (props, state, this) => {}
componentWillMount: (props, state, this) => {}
componentDidMount: (props, state, this) => {}
componentWillUnmount: (props, state, this) => {}
componentDidUnmount: (props, state, this) => {}
this.withAttr ({String|String[]}, {Function|Function[]})
this.forceUpdate: (this?: {Object})
this.setState: ({Object})
this.setProps: ({Object})
displayName: {String}
propTypes: {Object}
render: (props, state, this) => {}
}
creating hyperscript objects
h(
tag: {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>"});
dio.createComponent
dio.createComponent({Function|Object})
dio.createClass({Function|Object})
class Component extends dio.Component {}
var myComponent = dio.createComponent({
render: () => { return h('div') }
});
var myComponent = dio.createComponent(function () {
return {
render: () => { return h('div') }
};
});
myComponent(__,__,true)
dio.createRender
dio.createRender(
component: {Function|Object},
mount?: {String|Element|Function}
)
dio.render(
component: {Function|Object},
mount?: {String|Element|Function}
)
var instance = dio.createRender(Component)
instance(props: {Object}, children: {Any}, forceUpdate: {Boolean})
instance(__, __, '@@dio/COMPONENT')
dio.createRender(h(Component, {...props}, ...children));
dio.createRender(Component, mount, {...props}, [...children]);
Components that do not return an object with a render function
or are itself an object with a render function will not feature
the component lifecycles and methods as presented above in the schema.
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.createComponent({
render: function () {
return h('div', 'Hello World')
}
})
dio.createComponent(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 dio.createComponent
that feature a
render method either returned from a function or within the
object passed to .createComponent
are statefull by default.
One thing to note is that in the 'Hello World'
example that we began with, we did not create a component with dio.createComponent()
but rather just used a pure function that we passed
to dio.createRender(here)
this is because
.createRender
will create a component if what is passed to it is not already a component.
mount examples.
document
document.body
document.querySelector('.myapp')
'.myapp'
document.createElement('div')
dio.createRender(__, mount)
How do i render one component within another?
Components are just functions(statefull/stateless),
to render them just execute them where needed.
note: render instances are not components
function Foo (props) {
return h('h1', props.text)
}
var Bar = dio.createComponent({
render: function (props) {
return h('h2', props.text)
}
});
function () {
var elementHolder = dio.createStream()
return {
render: function () {
return h('div', {ref: elementHolder},
Foo({text: 'Hello'}),
Bar({text: 'World'})
)
}
}
}
dio.createRouter
dio.createRouter(
routes: {Function|Object},
rootAddress?: {String},
onInitNavTo?: {String},
mount?: {String|Element}
)
example router
dio.createRouter({
'/': function () {
dio.createRender(home)()
},
'/user/:id': function (data) {
dio.createRender(user)(data)
}
}, '/backend', '/user/sultan')
dio.createRouter({
'/': ComponentA,
'/user/:id': ComponentA
}, null, null, document.body);
You can then assign this route to a variable and use it to navigate across views
var myrouter = ...
myrouter.nav('/user/sultan')
myrouter.go(-2)
myrouter.back()
myrouter.forward()
dio.createStore
Alot like the redux createStore or rather it's exactly like redux createStore
with the addition of .connect
that accepts a render insance or a component
and mount 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(render: {Function})
store.connect(render: {Function|Object}, element: '.myapp')
dio.createHTML
Like the name suggests this methods outputs the html
of a specific component/render instance with the props passed
to it, this is normally used within the context of server-side rendering.
When used add the attribute data-hydrate=true
to the container
you wish to ouput the html, this will tell dio not to
initialize a fresh mount and rather hydrate the current dom.
dio.createHTML(component, props, children);
dio.createHTML(h('div', 'Text'));
dio.createStream
dio.createStream(store: {Any|Function}, mapper);
var alwaysString = dio.createStream(100, String);
alwaysString()
var foo = dio.createStream('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.createStream.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.createStream.all([dep1, dep2]).then(fn);
var async = dio.createStream(function (resolve, reject) {
setTimeout(resolve, 500, 'value');
});
var async = dio.createStream(function () {
setTimeout(reject, 500, 'just because');
}).then(function (value) {
console.log(value + 'resolved')
}).catch(function (reason) {
console.log('why:' + reason)
});
var foo = dio.createStream(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 = createStream();
var sum = createStream.scan(function(sum, numbers) {
return sum + numbers();
}, 0, numbers);
numbers(2)(3)(5);
sum();
dio.curry
var foo = dio.curry(
fn: {Function|String},
args...: {Any[]},
preventDefault: {Boolean},
)
onClick: dio.curry(
(a) => {'look no e.preventDefault()'},
['a'],
true
)
function DoesOneThing (component, arg1, arg2) {
component.setState({...});
}
h('input', {
onInput: dio.curry(DoesOneThing, [this, 1, 2], true)
})
dio.createStyle
creates a style element that is mounted to document.head
,
the output is auto prefixed and resembles sass/scss in the use of the "&" character
in nested styles. See the Single File Components section for an example.
dio.createStyle(css: {Object}, namespace?: {String});
dio.createStyle({'p': {color:'red'}}, '#id');
dio.createFactory
dio.createFactory(any[]|...arguments);
createFactory returns or exposes a function to the window that
produces a hyperscript element of a given type.
If the last argument is a Boolean true
the element factories are added to the global namespace
otherwise an object of the element factories are returned.
If only one element is specified, only that factory is returned and not an object,
if this is coupled with true as the second argument, the factory is added to
the global namespace instead.
dio.createFactory('div', 'input', true);
h('div', 'Hello World');
div('Hello World');
input({value: 'empty'});
dio.createFactory('div', 'input', true);
var {div, input} = dio.createFactory('div', 'input');
var div = dio.createFactory('div');
dio.createFactory('div', true);
dio.request
a http helper that makes ajax requests.
dio.request(
url: {String},
payload?: {Object},
enctype?: {String},
withCredentials?: {Boolean}
)
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
})
.then((res)=>{return res})
.catch((err)=>{throw err});
dio.animateWith
dio.animateWith.flip(
className: {String},
duration: {Number},
transform: {String},
transformOrigin: {String},
easing: {String}
)(
element: {Element|String}
)
dio.animateWith.transitions(
className: {String},
type?: {String|Number|Boolean}
)(
element: {Element},
callback: {Function} => (element: {Element}, 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)(Element)
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,
works just like it would in react-land.
The built in validtors are [number, string, bool, array, object, func]
and you can also create your own validators.
note that propTypes/validations are only evaluated when NODE_ENV
or process.env.NODE_ENV
are defined and set to 'development'.
dio.createComponent({
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.'
);
}
}
}
...
})
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.injectWindowDependency
injects a mock window object to be used for writting tests for
features that do not exist outside of the browser enviroment,
like XMLHttpRequest
and other #document
operations.
dio.injectWindowDependency({Object})