Riot.js — The 1kb client-side MVP library

Tero Piirainen2013-11-01

An incredibly fast, powerful yet tiny client side library for building large scale web applications.

Riot.js is a client-side Model-View-Presenter (MVP) library that weighs less than 1kb. Despite the small size, all the building blocks are there: a template engine, router, event library and a strict MVP pattern to keep things organized. On a finished application the views are automatically updated when the underlying model changes.

Smallest

Here is how Riot stack up to some of the popular client-side frameworks.

The total size of minified JavaScript

Riot is the smallest by a clear margin, and the size of a framework does matter:

  1. Less to learn. Fewer books and tutorials to view. The difference here is enormous: think 3 API methods instead of 300. You'll spend more time building the actual application.

  2. Less proprietary idioms, more straightforward JavaScript. It's way more important to master universal programming skills instead of some framework's own, opinionated rules.

  3. Less bugs. Less vulnerable surface and weak spots. All bugs should be user bugs. It's frustrating when your application fails because of a bug on the framework.

  4. Embeddable. Use Riot.js everywhere without additional bloat. Framework should not be bigger than the application itself.

  5. Faster. According to Google, each kilobyte of JavaScript adds 1ms of parse time to the overall page load time (1).

  6. Cheaper. Amazon.com increased its revenue by 1% for every 100 milliseconds of load time improvement (2). In 2012, 1% of Amazon’s revenues amounted to $610.9 Million

If you are building a tool for others' consumption, size is even more important.

Riot is small because it only focuses on things that are required to build modular and maintainable applications. Everything else is purposely left out.

Less application code

Here's the amount of keystrokes to build the Todo MVC application:

Raw amount of minified JS and HTML in Todo MVC

This is not the best metric since the size of the code depends on multiple factors such as the programming style, but it sill gives you some idea of the capabilities of a framework. The whole purpose of a framework is to solve common problems so users won't have to reinvent the wheel. Write less, achieve more, right?

Ultimately your goal should not be small, but simple.

Fast

Riot.js comes with an extremely fast JavaScript templating engine. It's roughly 5 times faster than Resig's "micro templating" or Underscore templating.

Running a variable substitution x amount of times.

Templating affects the rendering time of your application. Faster templating makes for a faster app. On bigger applications the difference can be huge, especially on non- Webkit browsers. The above test can take 1300ms in Hogan and 30ms in Riot with 100k repeats on Firefox. Please try the above test with different browsers to get a better picture.

Riot is fast because the template engine is very simple. It does only variable subsitution. No loops, conditionals, filters or methods. Read on to see why.

MVP design pattern

Riot uses Model-View-Presenter (MVP) design pattern to organize your code so that it's modular, testable and easy to understand.

Model View Presenter

Just like in MVC (Model View Controller) or MVVM (Model View ViewModel), the purpose is to separate your application logic from the view, but MVP is simpler. Let's take MVC for comparison:

Model View Controller

MVC is more complex. The many arrows form a circle. The role of the controller is not clear, and the pattern can be interpreted in many different ways. In fact, this is the root cause for the explosion of client-side frameworks.

MVP, on the other hand, has less room for interpretation. The role of each part is clear. It works for big and small projects, and is the best pattern for unit tests.

Let's see how MVP works in Riot.

Model

Riot models define your application. It's your business logic exposed to outer world with a well-thought API. A completely isolated, testable unit that can be run in browser and server (node.js). Here is one for the Todo app.

function Todo(store) {
 
var self = this,
items = [];
 
self.add = function(label) {
var item = { ... };
items.push(item);
 
// notify others
self.trigger("add", item);
}
 
self.edit = function(item) {
items[item.id] = item;
self.trigger("edit", item);
}
 
// + other public methods ...
 

// Enable MVP pattern (this is the secret for everything)
riot.observable(self);
 
// save state
self.on("add remove edit", function() {
store.put(items);
})
 
}

JavaScript

The Model is a plain old JavaScript object (POJO) without framework specific idioms. In MVC terminology it's a domain model (3) rather than just a data access layer.

You have the freedom to build your models in your preferred way using the prototype object or the object constructor {}. The above is just my personal style of writing JavaScript.

When designing a model it's important to clean your mind. The last thing you want is a framework to steal your focus. It's your precious business logic! JavaScript has tremendous expressive power in itself.

The observable

Observables are they key for separating the model from the rest of your application:

riot.observable(object);

JavaScript

After the above call the given object is able to notify others when something crucial happens. The views can re-render themselves or the users of the API users can make their extensions by listening to the changes.

Observables are the key to splitting your app into maintainable components. It's a classic design pattern to separate the Model from the View (4).

"We want a loosely coupled architecture with functionality broken down into independent modules with ideally no inter-module dependencies. Modules speak to the rest of the application when something interesting happens and an intermediate layer interprets and reacts to these messages." — Addy Osmani, Creator of TodoMVC

"The more tied components are to each other, the less reusable they will be, and the more difficult it becomes to make changes to one without accidentally affecting another" — Rebecca Murphey, author of jQuery Fundamentals

A good event library is the single most important feature in a client-side framework. And this is where Riot places the biggest focus.

The observable call adds following methods to the given object:

  • trigger(event_names, args...) — trigger a named event with optional arguments
  • on(event_names, fn) — call the given function when a particular event is triggered
  • one(event_names, fn) — call the given function once when a particular event is triggered. additional events cause no action
  • off(event_names) — stop listening to a specified event

The difference between jQuery events is that there is no Event object (6) because it's not relevant outside the DOM. You can also send and receive arguments nicely without wrapping them to an array:

// send event
obj.trigger("my-event", "1st argument", "2nd arg", ... argN);
 
// receive event
obj.on("my-event", function(arg1, arg2, ... argN) {
 
});

JavaScript

Observables were introduced in 1988 by Smalltalk and have been used to build user interfaces ever since. It's a design pattern, not a framework. Events and listeners are the essence of good code with separated concerns. They should be a core part of your mindset.

Let me put this in another way:

You don't need a framework to write modular client-side applications.

View

View is the visible part of your application. Text, images, tables, buttons, links and the like. The HTML and the CSS.

Riot views are as dummy as possible. No conditional statements, loops, or data binding. No custom attributes or custom elements. These should be absent from the view. Views are something you can expect from any HTML developer.

There are only "templates" — fragments of HTML that can be inserted in the view at runtime. These templates contain variables that are substituted with data using the extremely fast Riot template engine.

Here's one for a single TodoMVC entry:

<li id="{id}">
<div class="view">
<input class="toggle" type="checkbox">
<label>{name}</label>
<button class="destroy"/>
</div>
<input class="edit" value="{name}">
</li>

HTML

This kind of "logicless HTML" has no weak spots or testable surface. It's faster and passes W3C validator.

The actual logic is inside the presenter.

Presenter

Presenter has all the user interface logic. It listens to what happens on the View (click, scroll, keyboard, back button etc) and on the Model (things are added, removed or modified) and it updates both view and model accordingly. This "middleman" explicitly defines how the user interface behaves. Here's one for the Todo app:

$(function() {
 
/ 1. Initialize /
 
// create a model instance
var model = new Todo(),
 
// Grab the HTML root of this view
root = $("#todo-list"),
 
// HTML template for a single todo- entty
template = $("#todo-tmpl").html(),
 

/ 2. Listen to user events /
 
// "clear completed" is clicked
$("#clear-completed").click(function() {
todo.remove("completed");
})
 
// "toggle all" is clicked
$("#toggle-all").click(function() {
todo.toggle(filter);
})
 
// ...
 

/ 3. Listen to model events /
 

// an entry was removed
todo.on("remove", function(items) {
$.each(items, function() {
$(this.id).remove()
})
 
// an entry was edited
}).on("edit", function(item) {
var el = $(item.id);
el.removeClass("editing");
$("label, .edit", el).text(item.name).val(item.name);
})
 
// ...
 
})

JavaScript

I have put all the logic inside a single presenter, but there can be multiple presenters performing a separate task. For example, I could have put all the logic for a single todo entry in a separate file.

Feel free to decide the role of each presenter that makes the most sense for your application. This can be based on the role on the UI (sidebar, account, header...) or based on functionality (login, join, create, remove...).

jQuery

jQuery is absolutely the best tool for manipulating the DOM, and a perfect tool for building presenters. jQuery API is beautifully designed and all parts of the library are extremely useful. It's currently found on 56% of all websites and 92.0% of all the websites whose JavaScript library is known (7).

There is a depressing misbelief that jQuery leads to "spaghetti code". Nothing could be further from the truth. What people refer to as spaghetti is in fact a mixture of model and view code. To avoid it, simply separate your model code from the view with observables. You don't need a framework for that.

MVP pattern with jQuery is everything you need to build user interfaces.

The worst thing you can do is to ditch jQuery for the wrong reasons. It's a high level API that produces elegant and readable code. The expressive syntax is concise and requires few keystrokes. There is a huge community, extensive documentation, and it works on all browsers. Feel free to use all the aspects of jQuery without fear.

Current data-binding frameworks promote the use of spaghetti on the HTML layer. Suddenly the onclick attribute is back! (I'm looking at you, Angular). Programming books have told us to keep things separated and in the web this means the following (8):

  1. Keep JavaScript Out of CSS
  2. Keep CSS Out of JavaScript
  3. Keep JavaScript Out of HTML
  4. Keep HTML Out of JavaScript

Riot takes a puristic approach and does not allow you to do mix any logic inside HTML views. This is also the reason why Riot templating is fast – the templating logic is so simple.

URL's and back button

Back button is another source of confusion. There is a common agreement that you need a tool called "router".

Not true.

Back button click or a change in the URL is just a global window event that you can listen to. It should not be any different from pressing a keyboard key. Current routers are a massive solution to a very basic thing. There is no need for a separate tool for handling URL events, just like there is no need for a separate tool for handling keyboard events.

On top of that routers cause confusion because they don't fit to classic MVC semantics. Traditional MVC applications from the pre-web era didn't have URL's.

Riot has just one riot.route function to deal with application state and back button

// click on a navi element
$("#filters a").click(function(e) {
 
// skip link's default behaviour
e.preventDefault();
 
// change the hash part of the URL
riot.route($(this).attr("href"));
 
})
 
// Listen to URL change events
riot.route(function(hash) {
/*
Do the thing for Todo list:
 
1. clear list and add new ones
2. highlight active element on the navi
3. update view counters
*/
})

JavaScript

One function is all you need. I call the function "route" since that's what people expect. It could be something like "url" or "go" too. You can have one or multiple URL listeners doing their thing based on the url argument.

You might want to use a full blown router if your application is heavily dependent on different kinds of hashes with arguments, but that's not the case on most applications.

This is a Riot!

Current client-side frameworks base their existence on false assumptions:

  1. There's a big mass of common problems
  2. MVC (or MVP) requires a framework
  3. jQuery leads to spaghetti

These are not true.

Current applications can be faster, simpler and smaller. They can be maintained with less knowledge and less code, less knowledge, and less worries.

Models can be plain old JavaScript, and views can be plain old HTML. A framework should not burden the development and dictate how things should go.

No "Backbone way", "Angular way" or "Ember way". Frameworks come and go but classic programming skills are forever.

 

Riot is a manifesto for vanilla JavaScript and jQuery.

Riot resources

Riot website »

Source code on GithubFrameworkless JavaScriptTemplating performance testsThe todo MVC

Tero Piirainen

tero@muut.com