Mithril JS

Andrea Coiutti

Codroipo, 18/07/16

MITHRIL FROM

A UX DESIGNER'S PERSPECTIVE

Me and my beloved mountains.

What is Mithril?

Mithril is a client-side MVC framework.

 

A tool to organize code in a way that is easy to think about and to maintain.

LIGHT.   FAST.   ROBUST.

Mithril.JS

Website mithril.js.org
Github https://github.com/lhorie/mithril.js
Wiki https://github.com/lhorie/mithril.js/wiki
Gitter gitter.im/lhorie/mithril.js
Twitter @mithriljs

Created by Leo Horie

First release: ~ March 2014

current version: v0.2.5 - v1.0 rewrite in progress

LIGHT

Filesize

AngularJS

Ember.js

jQuery

40kb

100kb

35kb

mithril

7kb

react

35kb

Why filesize matters

  • Faster download (mobile friendly)
  • Faster parsing and compiling (also mobile friendly)
  • Less code to reason about
  • Less code with bugs
  • More space for own code :)

API Surface

AngularJS

Ember.js

jQuery

200+

100+

200+

mithril

20

react

~ 50

???

CORE
ROUTING DATA
m m.route m.request
m.prop m.route.param m.deferred
m.component m.sync
m.mount
m.withAttr
RENDERING HTML TESTING
m.render m.trust m.depts
m.redraw
m.startComputation
m.endComputation

Why API surface matters

  • fewer to learn (fast learning curve)
  • fewer to reason about
  • fewer to memorize
  • more freedom for integration

FAST

Performance

AngularJS

Ember.js

Knockout

800ms

1000ms

300ms

mithril

80ms

react

600ms

http://matt-esch.github.io/mercury-perf/

Smart rendering engine

  • VDOM implementation (like React)
  • intelligent auto-redrawing system
  • data always flows from the model to the view
  • global redraw every time data changes
  • efficient thanks to its fast diffing algorithm

Why performance matters

  • for obvious reasons...
  • mobile friendly
  • UX
  • native app feeling

ROBUST

The goal of the framework is to make application code discoverable, readable and maintainable, and hopefully help you become an even better developer.

 

Leo Horie

Minimal but golden toolset

  • MVC hierarchical components out of the box
  • utilities for high-level modelling via functional composition
  • small routes layer
  • AJAX helpers
  • bi-directional data binding

 

Nothing more. No crap you probably don't need.

Practical effects

  • learn to use functional programming in real world scenarios
  • code becomes easy to reuse, extend, refactor and test
  • less bugs because of side effects
  • invite to apply good programming conventions
  • solidify good coding practices for OOP and MVC
  • even medium-experienced UX/UI-Dev can change stuff

BONUS

Uber-simple setup

  • no dependencies
  • no precompiler needed
  • no special CLI tools
  • compatible with most bundlers (Browserify, webpack, ...)
  • easy to integrate third party stuff

Plain Javascript views

  • simple Javascript Array methods
  • proper error reporting
  • proper lexical scoping
  • clean HTML markup output

Great support

Used in production

https://github.com/lhorie/mithril.js/wiki/Who-Uses-Mithril

Basic methods:

m() and m.mount()

m('.container'); //<div class="container"></div>

m('#layout'); //<div id="layout"></div>

m('a[name=top]'); //<a name="top"></a>

m('[contenteditable]'); //<div contenteditable></div>

m('a#google.external[href="http://google.com"]', 'Google');
//<a id="google" class="external" href="http://google.com">Google</a>


m.mount(document.getElementById('content'), app);

"Hello world" example

var app = {};

// notice the lack of boilerplate
app.view = function(){
    return m('h1', 'Hello World!');
};

// mount the application
m.mount(document.body, app);
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Mithril Example</title>
</head>
<body>
    <script src="mithril.js"></script>
    <script src="hello-world.js"></script>
</body>
</html>

Two-way data binding

var app = {
    controller: function() {
        this.name = m.prop('');
    },
    view: function(ctrl) {
        return [
            m('h1', 'My name is: ' + ctrl.name()),
            m('input', {
                value: ctrl.name(),
                oninput: m.withAttr('value', ctrl.name)
            })
        ];
    }
};

m.mount(document.body, app);

Embracing arrays

var app = {
    getColors: function() {
        return m.request({url: 'colors.json', method: 'GET', initialValue: []});
    },
    controller: function() {
        this.colors = app.getColors();
    },
    view: function(ctrl) {
        return m('ul', [
            ctrl.colors().map(function(color) {
                return m('li', 'Hex code for "' + color.name + '" is ' + color.value);
            })
        ]);
    }
};

m.mount(document.body, app);

Embracing functions

var app = {
    vm: {data: []},
    controller: function() {
        window.setInterval(function() {
            for (var i = 0; i < 100; ++i) {
                app.vm.data[i] = Math.random();
            }
            m.redraw();
        }, 20);
    },
    view: function(ctrl) {
        return m('table', [
            m('thead', m('tr', [m('th', 'Key'), m('th', 'Value')])),
            m('tbody', [
                app.vm.data.map(function(value, index){
                    return m('tr', [m('td', index), m('td', value)]);
                })
            ])
        ]);
    }
};

m.mount(document.body, app);

Embracing functions

Templates and logic

var app = {
    vm: {happy: false},
    meHappy: function(framework) {
        return m('figure', [
            m('img[src="http://demo.acxwebdesign.com/mithril-examples/happy.jpg"]'),
            m('h2', 'Me when I work with ' + framework)
        ]);
    },
    meNotHappy: function(framework) {
        return m('figure', [
            m('img[src="http://demo.acxwebdesign.com/mithril-examples/not-happy.jpg"]'),
            m('h2', 'Me when I work with ' + framework)
        ]);
    },
    controller: function() {
        window.setInterval(function() {
            app.vm.happy = !app.vm.happy;
            m.redraw();
        }, 2000);
    },
    view: function(ctrl) {
        return app.vm.happy ? app.meHappy('Mithril') : app.meNotHappy('Angular');
    }
};

m.mount(document.body, app);

Templates and logic

Handling real DOM stuff

var app = {
    drawCanvas: function(element, isInitialized) {
        // executes this only once, avoids to repeat it at every redraw
        if (isInitialized) return;
        var ctx = element.getContext('2d');
        ctx.fillStyle = '#FF0000';
        ctx.fillRect(0,0,150,75);
    },
    controller: function() { /* ... */ },
    view: function(ctrl) {
        return [
            m('p', 'This is a red rectangle:'),
            m('canvas', {config: app.drawCanvas})
        ];
    }
};

m.mount(document.body, app);

REAL WORLD EXAMPLES...

THANK YOU, SIRS

FOR YOUR ATTENTION

Mithril JS

By Andrea Coiutti

Mithril JS

An introduction to Mithril.js for coworkers and friends. Thanks to Einar Norðfjörð, Bondi French and Stephan Hoyer for the heavy inspiration.

  • 1,378