You need to enable JavaScript to run this website.

DB.js: a tiny MVVM framework

Introduction

DB.js is a tiny (5.8K minimized) MVVM library written by Oleg Shalaev.

Small is elegant

It also stands for "easy", "fast", "flexible", and "reliable".

Consider a simple probabilistic problem: there is a machine (or a car, airplane, space ship, software) composed of many parts (or lines of code), each part having small probability to fail. How does the probability to have no failures in the machine scale with the number of parts?

It decreases exponentially: a device built of 3.000 parts will be more reliable (and easier to fix) than the one built using 30.000 parts, even if parts of the latter have (say, twice) better quality. High quality of every single part is suppressed by the overall complexity of the device.

Most frameworks are over-engineered

The size of frameworks at the bottom of this table seems unreasonable: were the creators paid per kilobyte of code?

FrameworkSize (K)
DB.js5.8
Preact 7.2.016
Inferno 1.2.248
Vue 2.4.258.8
KnockoutJS68
React 16.2.0 + React DOM97.5
React 0.14.5 + React DOM133
React 0.14.5 + React DOM + Redux139
Angular 1.4.5143
Angular 1.5.6156
Ember 2.2.0435
Ember 1.13.8486
Angular 2566
Angular 2 + Rx766

With the general probabilistic consideration above one does not have to try every other MVVM in order to conclude that DB.js is less buggy and more versatile than most other MVVMs.

Open source code essentially becomes closed when it is huge

Being dependent on someone else's large code is bad because its complexity means that you cannot fix the errors and optimize the code for your needs.

Web developers already have hard time being dependent on creators of Firefox, Safari, and Chrome. I do not like certain things in Firefox but I realize that I will never have time to fix them. Fortunately there is no monopoly for now: if Mozilla becomes too awful, people can easily escape to Chrome and vice versa.

But for businesses relying upon React or Angular it would be hard to switch: they become dependent on Facebook or Google – huge companies with lots of power and unclear intentions.

DB.js – basic usage

DB.js was inspired by KnockoutJS, so their syntax is similar.

Observables

Observable is an object created by DB.observable, for example,

JS
var spanText=new DB.observable([],{type:'str',value:"ku-ku"});

creates an observable spanText having string value and sets its value to "ku-ku".

Any observable is a function:

How it works

  1. The web browser loads HTML.
  2. HTML loads DB.js.
  3. The function DB.scanHTML() is called (inside HTML or from another JS-file). It scans all nodes for the DB.js-related properties. For example, if it encounters <span db="text:spanText"></span>, the text content (more precisely, textContent) of this span-node will be bound to the value of the observable spanText.
  4. Now if we change the value of spanText by calling spanText("bu-bu"), the corresponding span element will be automatically changed from the initial value "ku-ku" to "bu-bu".

Note: If you create DOM-elements (DOMs) dynamically after you have already called DB.scanHTML() at startup, call it again for these new DOMs. With no arguments, DB.scanHTML() scans the entire HTML. One can limit the scan to certain DOMs and their children: DB.scanHTML([DOM1,DOM2,…]).

Dependencies

An observable may depend on other observables. For example, consider the following definition:

JS
var husbandWeight=new DB.observable([],{type:'int',value:220}),
       wifeWeight=new DB.observable([],{type:'int',value:160}),
     coupleWeight=new DB.observable([husbandWeight,wifeWeight],
{type:'int', compute:()=>husbandWeight()+wifeWeight()});

where type:'int' specifies the (integer) value type, and value:220 together with value:160 assigns the initial values.

The first two observables, husbandWeight and wifeWeight do not depend on any other observable.

The third observable, coupleWeight is declared to be dependent on both husbandWeight and wifeWeight. Conversely, the value of coupleWeight will be re-calculated every time husbandWeight or wifeWeight is updated. (The function specified in compute field will be used for that.)

With these observables, the following piece of HTML code will always be up to date:

HTML
Now the husband weights <span db="text:husbandWeight"></span> pounds,
and his wife weights <span db="text:wifeWeight"></span> pounds,
so their total weight is <span db="text:coupleWeight"></span> pounds.

where text is the binding type connecting observable values to textContent of the corresponding span elements.

Suppose that during the dinner the husband gained 2 pounds, and the wife gained one. In order to update the HTML with their new weight, we execute

JS
husbandWeight(2+husbandWeight()); wifeWeight(1+wifeWeight());

After that, the value of coupleWeight together with all HTML elements bound to our observables husbandWeight, wifeWeight, and coupleWeight, will be automatically updated.

More bindings

In the above example we saw how text binding works. Other binding names:

  • attr:href
  • attr:id
  • checked
  • class
  • display
  • ev:input
  • ev:keydown
  • forEach
  • onClick
  • onKey
  • value
  • visible

This list of bindings is complete and sufficient even for most sophisticated web pages.

Due to its size, DB.js can be easily extended by whoever wants to use it. This flexibility is the main advantage of brevity; better performance (faster web pages) is just an extra benefit.

Demo and tests

Although DB.js is small, its code is non-trivial. The following web pages ensure that it is not damaged during the latest update:

These web pages should work properly in modern (3 years old) versions of Firefox, Chrome, and Safari.

Further development