Much more than just JavaScript

Modern client-side component development

WilMooreIII
moore-components-teaser1

Wil Moore III explores the best options for packaging and sharing reusable web components.

One
of the hotter debates in web development as of late revolves around
the notion of packaging and sharing reusable components. You may
have noticed that many segments of the web development community
have stepped up in order to attempt to solve this problem.

Interestingly, if you hang around in multiple parts of the
community, you’ve probably noticed that there are many solutions
being proposed and developed. Unfortunately, these solutions have
overlapping and incompatible feature sets, leaving us with a manual
integration headache. Don’t get me wrong, there are talented
developers working on this problem and I commend their efforts;
however, virtually all of them miss a critical detail…

A web “component” is much more than just JavaScript

I’m sure there are lots of stories floating around as to which
JavaScript package management solution is the best. Perhaps you’ve
heard of and even use Twitter’s Bower or James Burke’s
Volo or Caolan McMahon’s Jam. As with most of Twitter’s open source
projects, Bower happens to be the most popular of the lot. This is
no surprise given the impressive list of people
behind the project
. For example, @fat (Jacob Thornton), @addyosmani (Addy Osmani), and
@paulirish (Paul
Irish).

About two years ago, I started looking for the “best client-side
script loader”. I even entertained the thought, “perhaps I’ll just
write one myself” (famous last words). About a year ago, I started
digging further into NodeJS…that is when it all came together and I
realized that I should be looking for a client-side “module
loader”. You know, npm for the
browser. Little did I know that there was already Ender and Browserify in existence. Oh wait, what
about that Asset
Pipeline
thing or Assetic? Yes, I
frantically tried most of these solutions; and I even wrote about
my short
list of contenders
.

To a large degree, I continued to “miss the point”. Fortunately,
a very talented lad
by the name of TJ
Holowaychuk
set me straight. He reminded
me
“A [web] ‘component’ is much more than just
JavaScript
… A ‘component’ can be JavaScript, CSS,
images, fonts, and more
.” The moral of the story is,
“Create components, not [only] JavaScript
packages”
.

Write modular CommonJS components

If you are a PHP developer, you’ve probably heard of and have
likely used the esteemed Composer dependency management tool.
The idea is that you “declare” your dependencies and it will handle
sorting out the gory details. This is very similar to node’s
npm [14] and ruby’s bundler. If the concept behind these
tools gives your developer senses the warm and fuzzies, you may be
able to appreciate the idea behind CommonJS and writing modular
components that do one thing
well
.

This is precisely the philosophy of the new tool aptly named
Component. Component is a
client-side package manager and
module loader.
It also ships with a builder tool, which
allows you to aggregate the components used in your application
into a single package for testing and/or deployment. It can be used
to generate new components.

The included “component-builder” is written in JavaScript on top
of NodeJS; however, the philosophy behind the project is that other
communities may want to write the builder portion in the
language of their choice
since not everyone uses node. The
default “component-builder” is simply one implementation of the
“component” specification.

I thought AMD “won” the module loader race already? This sounds
nothing like AMD…what gives?

Hold on a second…let’s makes a few things clear before we move
on. First, we should establish that the “component” loader is
not an AMD module loader. An AMD module loader
(i.e. RequireJS, curl.js) loads a module when it is used (sort of
like PHP’s Auto Loader). AMD loaders load multiple modules at once
asynchronously. In theory, this is very convenient; however, the
downsides quickly start to outweigh the benefits once a project
moves past trivial. Honestly, if your application is indeed
trivial, you are better off punting on the loading debate entirely.
At that point, sprinkling script tags and jQuery snippets around
your pages is probably “good enough”. I personally don’t like to
develop software this way, but hey, who am I to judge you?

“Component” on the other hand, loads modules via a blazingly
fast local map. A component module is loaded when the
NodeJS/CommonJS style “require” function is
applied using a canonical string identifier or an alias (i.e.
model, model-timestamps) as depicted below.

// Module dependencies.
var model      = require('model'),
    timestamps = require('model-timestamps');

// Item model.
module.exports = model('Item')
  .attr('id')
  .attr('title')
  .attr('complete')
      .use(timestamps)

If you are building non-trivial client-side applications, the
ability to work in multiple domain-specific files (modules) is well
worth the investment of executing a build step (BTW, I would
recommend using the
watch(1)
command to automate this). Declaring the
modules you are using at the top of your module makes for
incredibly transparent source code. This increases readability,
testability, and maintainability.

On AMD, compatibility and other module formats

There are many module formats in play today (also referred to as
a transport format); however, “component” uses the CommonJS module format, which is not
directly compatible with the following:

That being said, the CommonJS module format is
well known
to be an excellent source module format from which
other module formats can be generated. In fact, this “translation”
is actually supported by the included “component-build” tool. A
“component” is built as an AMD module so you do not have to wrap
your modules in a “wrapper” function (which you are forced into
with the AMD and YUI formats). With “component” when you are ready
to share your module, you can optionally build a “standalone”
version that is compatible with AMD (and thus Dojo). For legacy’s
sake, a “window” global is also exported.

This “standalone” component or application can be loaded via
YUI’s module loader as well or used with manually placed

<script>
tags (that sounds so barbaric at this point).
If you had previously assumed that AMD “won”, please be aware that
this is not necessarily the case. It is documented that many very
experienced JavaScript developers do not see AMD
as the best solution
and certainly not a panacea.

Nevertheless, AMD is quite popular; however, that is only
because for a long time, there were no compelling alternatives.
This is no longer the case. If you have ever developed a
non-trivial application on top of an AMD module loader, you may
have noticed that it quickly becomes edge-case city. The process is
error prone and is ultimately a bad idea given there is a good
alternative.

Small, focused modules are better for building applications
than monolithic frameworks

The jQuery library takes a fair amount of heat for being
monolithic and non-cohesive. You may be wondering if jQuery
deserves all of this negative attention. jQuery was originally
developed when client-side package/dependency management wasn’t a
reality. At the time, a multiple file library just didn’t make
sense. jQuery made it easy to drop the library on to an HTML page,
select a DOM element, and attach an event handler in an elegant
way. jQuery has done well as a DOM abstraction library with a cute
API. It protects you from certain cross-browser edge cases and API
cruft. On the other hand, that cute DOM library actually sneaks
quite a list of extra concerns into our codebases, all attached to
the same global
$
object (you can optionally give back the
$
and make jQuery non-global, but this is not the
default).

The following list of things jQuery does other than DOM is quite
alarming if you consider the
The Single Responsibility Principle
to be
important:

  • DOM element selection, traversal, creation, and mutation (let’s
    pretend this is just one thing)
  • Events
  • XMLHttpRequest (AJAX)
  • Browser Sniffing
  • Array / Collection Utilities
  • Object manipulation and iteration (i.e. each, extend)
  • Object type inspection
  • JSON functions
  • XML functions
  • String functions
  • Time functions

I count at least 11 different concerns. This problem is not
isolated to jQuery alone. At the time of this writing, the
Underscore.js, MooTools, and Prototype libraries all have huge
non-cohesive APIs (to be fair, the new MooTools is reported to be
cleaning this up). I’ll say it again, this made sense at the time
these libraries were introduced; however, the batteries included
approach to software development seems nice until you start
building non-trivial software.

You may be wondering at this point why this even matters.
Perhaps you only use the AJAX portion of jQuery. The fact that
there are 100+ methods that you are not using isn’t hurting you…or
is it? It is tempting to believe that there is no harm done;
however, consider the fact that those unused functions are
completely unrelated to the task at hand. The only way for a future
developer (in many cases this is yourself) to understand why jQuery
or underscore was included is to read the source code…all of it.
With a module system in place and small, focused components, this
maintenance and complexity problem goes away.

Now, I am not going to get ahead of myself and assume that
you’ve been completely persuaded; however, I will assume that you’d
at least entertain the question “How do I do X if I give up jQuery
(or any other monolithic framework you are using)”? In other words,
armed with only the component package manager, the CommonJS module
format, and the component.io registry, how might I build a date
picker component?

With
component(1)
installed, creating a re-usable date picker
component boils down to the following steps (assuming you’ve
already installed
Component
):

Bootstrapping a new datepicker component

$ npm install -g component
$ component create datepicker

repo (username/project): component/datepicker
description: Datepicker ui component built on component/calendar
does this component have js? y  
does this component have css? y
does this component have html? 

      create : datepicker
      create : datepicker/index.js
      create : datepicker/datepicker.css
      create : datepicker/Makefile
      create : datepicker/Readme.md
      create : datepicker/History.md
      create : datepicker/.gitignore
      create : datepicker/component.json

Adding dependencies

$ cd datepicker
$ component install component/calendar

install : component/calendar@master
    dep : component/range@master
install : component/range@master
    dep : component/jquery@master
install : component/jquery@master
    dep : component/emitter@master
install : component/emitter@master
    sep : component/in-groups-of@master
install : component/in-groups-of@master
  fetch : component/calendar:index.js
  fetch : component/calendar:lib/utils.js
  fetch : component/calendar:lib/template.js
  fetch : component/calendar:lib/calendar.js
  fetch : component/calendar:lib/days.js
  fetch : component/calendar:lib/calendar.css
  fetch : component/range:index.js
  fetch : component/jquery:index.js
  fetch : component/in-groups-of:index.js
  fetch : component/emitter:index.js
complete : component/range
complete : component/in-groups-of
complete : component/emitter
complete : component/jquery
complete : component/calendar

Component Integration

We will create an
example.html
file that will allow us to integrate and
interact with our date picker component.

<!DOCTYPE html>
<html>
  <head>
    <title>Datepicker</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link rel="stylesheet" href="build/build.css">
    <script src="build/build.js"></script>
  </head>
  <body>
    <input type="text" name="date" placeholder="Choose a date">

    <script>
       var picker = require('datepicker');
       var el = document.querySelector('[name=date]');
       picker(el);
    </script>
  </body>
</html>

We will also install a few more “glue” components:

% component install component/{popover,aurora,event}

Next, we will create an
index.js
file. This is the demo’s entry point.

var Calendar = require('calendar')
  , Popover = require('popover')
  , event = require('event')

module.exports = Datepicker;

function Datepicker(el) {
  if (!(this instanceof Datepicker)) return new Datepicker(el);
  this.el = el;
  this.cal = new Calendar;
  this.cal.el.addClass('datepicker-calendar');
  event.bind(el, 'click', this.onclick.bind(this));
}

Datepicker.prototype.onclick = function(e){
  this.cal.on('change', this.onchange.bind(this));
  this.popover = new Popover(this.cal.el);
  this.popover.classname = 'datepicker-popover popover';
  this.popover.show(this.el);
};

Datepicker.prototype.onchange = function(date){
  el.value = date.getFullYear()
    + '/'
    + date.getMonth()
    + '/'
    + date.getDate();

  this.popover.hide();
};

Styling

We will create a
datepicker.css
file that will contain a few overrides so the
popover border is not in the way.

.datepicker-calendar {
  font: 10px "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.datepicker-popover .tip-arrow {
  top: auto;
}
.datepicker-popover .tip-inner {
  border: none;
}

Building our component

The only thing left to do is build the component.

% component build

There are a few more minor details that go into polishing off
your new component but the above steps will get you a working date
picker. For the original Date Picker tutorial check out
this article by the “component” author
.

My precious plugins

The jQuery plugin ecosystem is arguably a major part of what
makes jQuery so popular. There is a plugin for pretty much anything
you can think of. Unfortunately, this is part of the fragmentation
problem. jQuery isn’t the only library around. There is YUI, Dojo,
ExtJS, and many others. With a few minor exceptions, none of these
project’s plugins or modules are portable. For example, one can’t
simply take a YUI module and plug it into ExtJS. Definitely applaud
YUI and Dojo for having real module systems and build tools;
however, everyone does it their own way.

With “component” hopefully we will get to a point where we don’t
need to worry about compatibility. With enough adoption, developers
will be incentivized to build components from plan JavaScript, CSS,
HTML, and more. While still very new, the “component” ecosystem is
coming along nicely. There are already close to 700 components
registered at the component.io registry. For example, suppose you
are building an Evernote-like web application and you would like to
give your users the ability to tag their notes. This is generally
referred to as a “tag-input” component:

% component install component/pillbox

Suppose you miss those CSS selectors, which are extremely
popular with the jQuery crowd… in that case, you may like:

% component install component/zest

You may have gotten the impression that components need to
include JavaScript. That certainly is not the message I want you to
receive. For example, a component could indeed be just CSS (i.e.
Twitter Bootstrap).

That being said, there is nothing wrong with a component that
has no CSS but is a JavaScript-only utility that works in NodeJS
and the browser. There is even a DOM manipulation and traversal
component
 available that will help you ease away from
jQuery.

Build awesome things

I hope that you are at least intrigued enough to want to find
out more. If so, you’ll definitely want to start by reading the
original
component announcement
then watch the “Web Component
Introduction” screencast
and then read the
best practices wiki
. Get inspired to learn JavaScript more
intimately by watching Rebecca Murphey’s excellent JSCONF talk

The jQuery Divide
.

Perhaps you are on the fence, still sprinkling jQuery

document.ready
calls about your pages and using
pseudo-namespaces
(i.e. deeply nested but still global objects)
for “organization”. If this is the case, you might want to give
component a try. If you aren’t sure what to build, there is a
wealth of inspiration out there. For example, people are building
complex content editors, file management tools, mobile apps, music,
games, and even apps that run on your television. Enjoy building
awesome things with component.

Wil Moore III
(wil.moore@wilmoore.com) is a full-stack software craftsman
with a passion for well-crafted software with an aesthetic API. Wil
is a Zend Certified Engineer with PHP 5.3+ and is completely in awe
of and contributes to many open-source projects. He is not shy
about advocating for a development culture of devops,
multi-paradigm programming, test-driven development, peer review,
and mentorship. Wil primarily develops software in JavaScript
(Browser and NodeJS), Ruby, and PHP. Follow Wil on Github or
Twitter @wilmoore.

Author
Comments
comments powered by Disqus