We want to create modular and performant client-side applications, with a maximum of "just plain JavaScript" and a minimum of boilerplate.
Webpack is an opinionated module bundler for front-end assets. Like Browserify, it lets us treat local files as modules, concatenating intelligently.
var something = require('./something.js');
Mithril is a lightweight client-side MVC framework. Like React, it has one-way data flows and virtual DOM diffing.
var m = require('mithril');
m.mount(document.body, {
view: function() { return m('p', 'Hello, world.') }
});
Mithril does not have a module registry, because JavaScript already does: require/import. Webpack provides that until we're able to adopt ES2015 and HTTP/2.0.
Grunt / Gulp / Brunch / RequireJS / Browserify
If you need a cabin, why start with a mere pile of logs? - Cory House
Convention > Configuration.
Webpack is like the best of Brunch (lightweight configuration) plus the best of Browserify (fluent dependency resolution).
webpack.config.js
module.exports = {
entry: "./index",
output: {
path: __dirname,
filename: "[name].bundle.js"
}
};
npm install -g webpack webpack-dev-server
webpack-dev-server
The dev server is one of the nice things webpack gives us - a server running on http://localhost:8080/ that rebuilds bundles and live-reloads connected browsers.
bundle : destination asset
chunk : the content of the bundle
loader : plugin responsible for processing files based on match patterns
index.js
require('./a');
a.js
console.log('a here');
index.html
<!doctype html>
<body>
<script type="text/javascript" src="main.bundle.js"></script>
</body>
main.bundle.js
/******/ (function(modules) { // webpackBootstrap
... ~243B min ...
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(1);
/***/ },
/* 1 */
/***/ function(module, exports) {
console.log('a here');
/***/ }
/******/ ]);
npm install immutable
var Immutable = require('immutable');
Yep, just like Browserify...
npm install raw-loader style-loader
webpack.config.js
module.exports = {
entry: "./index",
output: {
path: __dirname,
filename: "[name].bundle.js"
}
};
Now when we require('./something.css');
we'll get those styles inlined into our JavaScript bundle.
npm install purecss
require('purecss');
Because purecss has a CSS file as main
in its package.json
.
added to webpack.config.js:
var webpack = require('webpack');
...
plugins: [new webpack.optimize.CommonsChunkPlugin('common.js');]
added to index.html:
<script src="common.js"></script>
if (window.location.pathname === '/feed') {
// this syntax is weird but it works
require.ensure([], function() {
// this module is now synchronously available.
require('./feed').show();
});
}
From Pete Hunt's webpack-howto.
Functions don't have side effects. They return new values, leaving passed values unchanged.
Helps us be functional. Operations on immutable data structures don't have side effects, they return new structures.
A data structure that mirrors the state of the actual DOM, so a renderer can quickly know what needs to be redrawn.
Don't listen for changes to values from the UI. Let the UI make updates to the Virtual DOM, which will be used to redraw the UI.
Updating the DOM is expensive! Batch operations that would change the DOM, at minimum within one animation frame.
React, Mithril, Om, and others use these concepts to make snappy applications that are easy to reason around.
npm install mithril
index.js:
var m = require('mithril');
Mithril is an MVC, but its parts are just POJOs.
m.mount(document.body, {
view: function() {
return m('p', 'Hello.');
}
});
m.mount() attaches a Mithril component to the DOM. m() is the Virtual DOM builder.
m.mount(document.body, {
controller: function() {
return {
num: 1
}
},
view: function(controller) {
return m('p', ['Hello.', controller.num]);
}
});
var Nums = {
awesomeNum: 1
};
m.mount(document.body, {
controller: function() {
return {
num: Nums.awesomeNum
}
},
view: function(controller) {
return m('p', ['Hello.', controller.num]);
}
});
controller: function() {
return {
num: function() {
return Nums.awesomeNum
},
add: function() {
Nums.awesomeNum = Nums.awesomeNum + 1;
}
}
},
view: function(controller) {
return m('p', {onclick: controller.add},
['Hello.', controller.num()]);
}
var Nums = {
awesomeNum: m.prop(1)
};
m.mount(document.body, {
controller: function() {
return {
num: Nums.awesomeNum,
add: function() {
Nums.awesomeNum(Nums.awesomeNum() + 1);
}
}
},
view: function(controller) {
return m('p', {onclick: controller.add},
['Hello.', controller.num()]);
}
});
setInterval(function(){
Nums.awesomeNum(Nums.awesomeNum() - 1);
}, 1000);
That only updates when we click the p. What gives?
Mithril needs to know something has changed. Mithril wraps Virtual DOM events in m.startComputation() and m.endComputation() to manage an internal counter.
setInterval(function(){
m.startComputation();
Nums.awesomeNum(Nums.awesomeNum() - 1);
m.endComputation();
}, 1000);
When the counter is 0, a redraw happens.
Mithril doesn't come with many bells and whistles. There are a few things that are pretty usefult.
Manages HTTP requests (wrapped in m.startComputation() and m.endComputation()).
m.mount(document.body, {
controller: function() {
return {
posts: m.request({method: "GET", url: "http://jsonplaceholder.typicode.com/posts"})
}
},
view: function(controller) {
return m('ul', controller.posts().map(function(post){
return m('li', [m('h3', post.title), m('p', post.body)]);
}));
}
});
a.js:
module.exports = {
view: function() { return m('p.awesome', 'Component A.') }
}
index.js:
m.route(document.body, '/a-url', {
'/a-url': require('./a'),
'/b-url': require('./b')
});
Templates can be even faster, if they're pre-compiled as a build step.
var view = function() {
return m("a", {href: "http://google.com"}, "test");
}
->
var view = function() {
return {tag: "a", attrs: {href: "http://google.com"}, children: "test"};
}
The mithril-node-render project lets us render Mithril components from Node.
npm install mithril-node-render
var m = require('mithril');
var render = require('mithril-node-render');
console.log(render(require('./a')));
$ node node.js
<p class="awesome">Component A.</p>
Size (still) matters!
And that's a good thing!