JavaScript Widget Development Best Practices (Part 2: the Setup)
In the former part of the series we had a brief introduction on challenges in developing JavaScript widgets.
In this article, we’re going to continue from where we left: We will start by installing necessary instruments to develop and test our code.
First Things First
Before we start actual coding, we need to install node.js.
You can download and install the most recent version of node.js from http://nodejs.org/. The installation process is pretty straightforward.
Note that this article is not by no means a node.js tutorial. I assume you know enough node.js to follow through. If not, there are excellent tutorials around to get you started on node.js.
Then we’ll install express.js to have a higher-level abstraction on top of node.js and to be easily manage cookies, session state, templates… and the like.
To install express.js:
$ cd /path/to/your/projects/folder/ $ npm install express
While we’re at there let’s install jade template engine too:
$ cd /path/to/your/projects/folder/ $ npm install jade
Create Development Servers
The next thing we are going to do is to create development environments.
We’ll have two server instances:
- http://api.widget.www/ will be our widget provider;
- And http://publisher.www:8080/ will be our publisher website.
We’ll simply add these lines to our /etc/hosts file.
127.0.0.1 api.widget.www 127.0.0.1 publisher.www
Project Folder Structures
Initially, there’s nothing fancy:
Here’s the initial folder structure of our api widget provider wwwroot:
And here’s the initial folder structure of our demo publisher website:
You can view this initial version of the sites at this github history snapshot. We will be adding more files and changing the folder structure as proceed to the next articles of this series.
Widget Server
The node.js server of the widget API website is quite simple:
var express = require('express');
var app = express.createServer();
app.use(express.static(__dirname + '/static'));
app.get('/api/v.0.1/login', function(req, res) {
res.send('hello authentication');
});
app.listen(80);
We simply server everything under the api.widget.wwwroot/static folder as static content.
Let’s test that it works.
:api.widget.wwwroot$ sudo node index.js
And then when we browse to http://api.widget.www/api.v.0.1.js we’ll see that our Widget API JavaScript is served without problem.
Publisher Server
And here’s the publisher‘s node.js server index file:
var express = require('express');
var app = express.createServer();
app.use(express.static(__dirname + '/static'));
// Set path to the views (template) directory
app.set('views', './views');
app.get('/', function(req, res) {
res.render('index.jade');
});
app.listen(8080);
And we have a very simple Jade template to be rendered when requesting the site root (“/”):
# views/index.jade
!!! 5
html(lang='en')
head
meta(charset='utf-8')
title= 'Publisher Website'
link(rel='stylesheet', media='all', href='/css/main.css');
script
(function() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = 'http://api.widget.www/api.v.0.1.js'
var node = document.getElementsByTagName('script')[0];
node.parentNode.insertBefore(script, node);
}());
body(lang='en')
section#Main
h2 Welcome to Publisher Website
If this were a real publisher site, the template folder structure would have been more complicated possibly with a handful of partial templates. Though this basic page is good enough for our initial setup.
Let’s run
:publisher.wwwroot $ sudo node index.js
on publisher.wwwroot.
When we browse http://publisher.www:8080/ we’ll get a very basic welcome page.
Note that we are listening to port 8080 for the publisher website because our api server is already listening to port 80. In a production environment we won’t need ts.his too.
Asynchronous Loading of the Bootsrapper
On the publisher page we include our widget initialization script as follows:
(function() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = 'http://api.widget.www/api.v.0.1.js'
var node = document.getElementsByTagName('script')[0];
node.parentNode.insertBefore(script, node);
}());
This structure is known as async loading pattern.
This usage is better than simply including the script with
<script src="http://api.widget.www/api.v.0.1.js"></script>
because the latter will block downloading of the resources and rendering of the page until it’s fully downloaded and executed.
If you remember from the introduction article, as widget developers we don’t own the publisher’s website. Per contra, we are like thieves wandering in their home. So the less noise we make, and the less trace we leave, the better.
That’s why our widget should initiate itself with minimal impact to the publisher’s performance: If you want to have faster and responsive website, loading third-party resources asynchronously is a best practice to follow.
However keep in mind that the publisher should have the liberty to load your script inline (as in the second example) if she likes. If she knows the consequences of it, and if she has a reason to do so, we shall not prevent her from doing it. As before, we do not own publisher’s DOM. The publisher can do anything (imaginable) and we should be flexible enough to support every possible scenario.
Widget Code
Here comes the fun part
. Let’s see our widget code:
(function(window, isDebugMode) {
'use strict';
var version = 'v.0.1';
function isArray(item) {
return window._wd_o2.isArray(item);
}
function checkForUpdates() {
}
function render() {
}
function execute() {
}
var queue = {
items : [],
push : function(item) {
log('o->queue.push()');
execute(item);
}
};
var processQueue = function() {
var q = null;
if (window._wdq && isArray(window._wdq)) {
q = window._wdq;
while (q.length) {
execute(q.pop());
}
}
processQueue = window._wd_o2.nill;
};
function initialState_ready(state) {
render(state);
processQueue();
window._wdq = queue;
}
function loadInitialState(config, callback) {
config = null;
callback({});
}
function getConfiguration() {
return {};
}
function initialize() {
var config = getConfiguration();
loadInitialState(config, initialState_ready);
}
function getPrerequisites(callback) {
callback();
}
checkForUpdates(version);
getPrerequisites(initialize);
}(this, true));
I’ve removed comments and console.log calls to make the code look cleaner.
The first thing we see is we’re utilizing the module pattern to hide our widget’s private static context and functions from the publisher. We should not pollute global namespace as widget development best practice.
Widget Initialization Flow
Here’s a broad overview of the initialization flow of the widget:
Most of the methods are blank, left to be implemented in the followup articles. However, the initialization flow will more or less remain the same.
In the diagram above, the gray boxes represent async operations (such as loading a beacon, or making a resource request to the server).
Let’s observe each of the blocks one by one:
BootLoader Cache Validation
- We start by running two requests in parallel:
- We check for new versions of our bootloader script and update our browser cache if we find out that our version is obsolete (so that the user will load the new version from cache the next time she visits the publisher site).
This will help us solve caching issues, and enable us serve a faster-loading widget.
- And, in parallel, we start our regular widget initialization flow.
- We check for new versions of our bootloader script and update our browser cache if we find out that our version is obsolete (so that the user will load the new version from cache the next time she visits the publisher site).
Widget Initialization
In our widget initialization flow:
- We get additional resources (such as helper libraries) in the getPrerequsities function.
- After we retrieve all required resources we get widget configuration data from DOM and request initial state data of our widget from the server (this is basically the “Model” data that’ll be used to render the “View” of the widget)
- We render our widget’s initial view with the state data we acquired.
- We process any jobs in the job queue that has been registered by the publisher to be run as soon as widget is available (this job queue works similar to the _gaq of google analytics; you can register jobs, and they will be run in order whenever the widget is able to respond).
- We override the job queue object with an Array-like implementation.
Fear not, if these flows seem a bit complicated. Most of these methods are currently abstract anyway, and we’ll be implementing them all in the following articles. And everything will be crystal-clear. Stay tuned
.
Conclusion
In this article we’ve learned how to set up our environment for JavaScript widget development and testing.
We’ve also had an abstract overview of our widget’s cache-revalidation and initialization flow logics.
In the next sections, we will be filling out some of these empty functions with real code.
Until then, feel free to share your comments and suggestions.




