Total Pageviews

Monday, 20 January 2014

How to Structure Your Application Using RequireJS?


I've been using RequireJS, a module loading library for JavaScript, for the past few months. Since I started I've learned quite a bit about it. It was a bit rough at first but forms a solid part of my toolkit now. I really find it hard to imagine JS development without it. It's like driving a Porsche without proper wheels... Just as frustrating. :)

Besides providing nifty module syntax it allows you to optimize your project easily. In fact this is something you get for free once you set up your project certain way. Highly useful!

Even though the project documentation is quite vast, it's pretty easy for a beginner to get lost in the detail. In this post I'm going to show you how to structure your application using RequireJS. It's not a "be all and end all" kind of solution. Consider it more as a starting point that you'll need to adapt to suit your needs.

Before getting into actual application structure I'm going to cover some core concepts briefly.

Fundamentals of RequireJS


You can probably live without RequireJS in case you application isn't that big (say ~500 lines of code). Once you reach certain limit, it will simply start to bog you down. That's where RequireJS comes in.

It allows you to separate your source code semantically into modules. It constructs the actual application based on these modules at the build stage.

The next snippet contains a few examples of possible module definitions to give you a better idea of what I'm talking about:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
// module for some app conf, items may contain funcs too
define({
debug: true,
logUndo: false,
});
 
// more complicated def (plugin example)
define({
meta: {
tooltip: 'My awesome tool',
attributes: { // some default attrs
width: 5
}
},
execute: function(opts) {
// do something cool now
}
});
 
// even more complicated def, with dependencies this time
define(['utils/math'], function(math) {
// you can include handy local utils here
 
// ...
 
// and define what to share here
return {
execute: function(opts) {
// do something with the math module
return math.sqrt(123);
}
};
});
view raw module_examples.js hosted with ❤ by GitHub


Now, how to connect these modules together and get the app built?

Project Structure


I have found the following basic structure most useful. It's only very high level structure, no actual code yet:

  • <app name> - Project folder
  • <app name>/readme.md - Just some common info about the project. Dev tips, whatnot.
  • <app name>/normal.build.json - Build profile for the normal build I use for developing. Extension set to json so it parses ok at my IDE (Oracle NetBeans).
  • <app name>/optimized.build.json - Release profile. Same idea here.
  • <app name>/requirejs - RequireJS source needed for building the app. You can find the full source here.
  • <app name>/<app name> - Actual app. I'm going to cover its contents in the next section.

To get the app built, I just execute "requirejs/build/build.sh <build profile>" at the project root. As a result I get a nice "build" folder containing my freshly baked app straight from the oven.

In Windows you might need to do something along "requirejs\build\build.bat <build profile>" instead of the aforementioned unless you are using Cygwin or something similar.

A build profile may look something like this (normal.build.json):

1 2 3 4 5 6 7 8 9 10 11 12
{
appDir: <app name>,
baseUrl: "src",
dir: "build",
optimize: "none",
 
modules: [
{
"name": "main"
}
]
}
view raw normal.build.json hosted with ❤ by GitHub


Note that in case of optimized profile, you may want to tweak the file to suit your preferences. This example file shows various options available. The official documentation has some handy information as well.

Application Structure


We are still missing a vital part, application structure. Let's have a look at that next:

  • <app name>/<app name>/<app name>.html - Markup for the project to get it running at your web browser.
  • <app name>/<app name>/css - CSS files defining what the app looks like visually.
  • <app name>/<app name>/images - Some images in which my CSS files and app UI elements may refer to.
  • <app name>/<app name>/src - Here's the interesting bit. Some actual source code! :)

Note that it's possible, and sometimes preferable, to define multiple html files to test various aspects of your app. You might want to pass it different kind of configuration, use some other style or something.

My basic HTML looks like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>App name goes here</title>
 
<!-- Load some CSS here. -->
<link rel="stylesheet" type="text/css" href="css/main.css">
</head>
<body>
<!-- Optional part. Define some global opts. You can set these easily via PHP or so. -->
<script type="text/javascript">
APP_DEFAULTS = {
width: 640,
height: 480,
color: 'white'
}
</script>
 
<!-- Path to your RequireJS. Note the data-main thingy! -->
<script data-main="src/main" src="src/require.js"></script>
 
<!-- You can load CSS here too if you really need to. -->
<link rel="stylesheet" type="text/css" href="css/other.css">
</body>
</html>
view raw app.html hosted with ❤ by GitHub

As you can see I have defined some global configuration for the application. This can be most useful especially in server environment.

Source Structure


Great! You made it this far. Let's take a look at the source structure briefly next:

  • src/application.js - Scaffolding for our Application. It just runs it and does some initialization.
  • src/main.js - Important part! This will set up some configuration and actually run the application.
  • src/require.js - Our beloved RequireJS. I use a special build of my own containing another favorite lib of mine, RightJS, too.
  • src/utils - A stash for some utility functions and classes used all over the app.
  • src/utils/math.js - Just some math stuff just to show there's stuff inside these folders.
  • src/utils/misc.js - Another one just to give the idea.
  • src/ui - UI related stuff. Widgets and such.
  • src/configuration - Inner configuration of the app. Optional since you can define these at HTML as above. This might be a bit safer from hacking, though. Alternatively you could use just this and ditch HTML based configuration.
  • src/ - Your own "packages" containing your app logic and such. I have specific folders for my app "panels" and "tools" for instance.

I'm going to elaborate on a few critical bits next. Let's have a look at basic main.js next:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
APP = {}; // my global stash, you might do without
 
require({
// some nice aliases for easy imports
// now we can refer to "math" utils
// simply by "utils/math"
paths: {
configuration: 'configuration',
ui: 'ui',
utils: 'utils'
}
},
['application', 'utils/misc'],
function(app, misc) {
// I extend various libs at misc, hence this
misc.initialize();
// booyah, time to run
app.run();
});
view raw main.js hosted with ❤ by GitHub

A basic application.js might look something like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
define(['panels/panels'],
function(panels) {
var Application = new Class({
initialize: function() {
// stub console funcs to allow console.log ie. without errors
this._stubConsole();
 
// inject "app" div to document body
$E('div').set('id', 'app').insertTo(document.body);
 
// this gets the party started
panels.initialize();
},
_stubConsole: function() {
if(!defined(window.console)) {
window.console = {};
}
 
['error', 'log', 'warn'].each(function(name) {
if(!(name in window.console)) {
window.console[name] = function() {};
}
});
}
});
 
// no need to return actual Application, instead
// we can return a nice func instantiating and
// hence running it
// btw you can eliminate the class if you really want to
return {
run: function() {
new Application();
}
};
});
view raw application.js hosted with ❤ by GitHub

The rest of the app just follows the patterns discussed above, except for one little thing. I call it a "package" pattern. This pattern just encapsulates a bunch of separate modules into the same interface. I'm going to show you how to use it next.

Package Pattern


Suppose you have a bunch of widgets each containing a heap of code. You probably really wouldn't want to contain them into the same file. That would just be too much overhead in case you need to use only one of them or so. It also makes maintenance more difficult than it has to be.

Package pattern helps in this problem by splitting up the widgets to separate files and then by providing a common interface that may be used to access them. In this case you might want to structure your source as follows:
  • src/ui/widgets.js - Common interface for widgets.
  • src/ui/widgets - Folder containing our actual widgets.
  • src/ui/widgets/.js - Some widget. You can as many of these as you like.
widgets.js providing the common interface might look something like this:

1 2 3 4 5 6 7 8 9
define(['./widgets/selectors', './widgets/slider', './widgets/button'],
function(selectors, slider, button) {
return {
ToolSelector: selectors.ToolSelector,
OptionSelector: selectors.OptionSelector,
Slider: slider.Slider,
Button: button.Button
};
});
view raw widgets.js hosted with ❤ by GitHub

In this case I chose to provide the interface explicitly. In case you don't need this level of control, you could just merge the contents of module objects and just return that instead.

Conclusion


I hope this post helped you to understand how to use RequireJS in your own application. It's an extremely powerful library that can really boost your development. I heartily recommend at least checking it out!
 
from http://www.nixtu.info/2011/02/how-to-structure-your-application-using.html