That is my exact question too! I also must use an older jQuery, but also more "traditional" javascript libraries. What is the best technique to do that? (I may edit your question to make it more broad if you don't mind.) Here is what I learned.
RequireJS author, James Burke, explained the advantages of the combined RequireJS + jQuery file. You get two things.
A module, jquery
, is available, and it's the jQuery object. This is safe:
// My module depends on jQuery but what if $ was overwritten?
define(["jquery"], function($) {
// $ is guaranteed to be jQuery now */
})
jQuery is already loaded before any require()
or define()
stuff. All modules are guaranteed that jQuery is ready. You don't even need the require/order.js
plugin since jQuery was basically hard-coded to load first.
To me, #2 isn't very helpful. Most real applications have many .js
files that must load in the right order—sad but true. As soon as you need Sammy or Underscore.js, the combined RequireJS+jQuery file doesn't help.
My solution is to write simple RequireJS wrappers that load my traditional scripts using the "order" plugin.
Example
Suppose my app has these components (by dependency).
- My app, greatapp
- greatapp depends on a custom jquery (old version I must use)
- greatapp depends on my_sammy (SammyJS plus all its plugins I must use). These must be in order
- my_sammy depends on jquery (SammyJS is a jQuery plugin)
- my_sammy depends on sammy.js
- my_sammy depends on sammy.json.js
- my_sammy depends on sammy.storage.js
- my_sammy depends on sammy.mustache.js
In my mind, everything above that ends with .js
is a "traditional" script. Everything without .js
is a RequireJS plugin. The key is: high-level stuff (greatapp, my_sammy) are modules, and at deeper levels, it falls back to traditional .js
files.
Booting
It all starts with a booter telling RequireJS how to start.
<html>
<head>
<script data-main="js/boot.js" src="js/require.js"></script>
</head>
</html>
In js/boot.js
I put only the config and how to start the application.
require( // The "paths" maps module names to actual places to fetch the file.
// I made modules with simple names (jquery, sammy) that will do the hard work.
{ paths: { jquery: "require_jquery"
, sammy : "require_sammy"
}
}
// Next is the root module to run, which depends on everything else.
, [ "greatapp" ]
// Finally, start my app in whatever way it uses.
, function(greatapp) { greatapp.start(); }
);
Main Application
In greatapp.js
I have a normal looking module.
define(["jquery", "sammy"], function($, Sammy) {
// At this point, jQuery and SammyJS are loaded successfully.
// By depending on "jquery", the "require_jquery.js" file will run; same for sammy.
// Those require_* files also pass jQuery and Sammy to here, so no more globals!
var start = function() {
$(document).ready(function() {
$("body").html("Hello world!");
})
}
return {"start":start};
}
RequireJS module wrappers around traditional files
require_jquery.js
:
define(["/custom/path/to/my/jquery.js?1.4.2"], function() {
// Raw jQuery does not return anything, so return it explicitly here.
return jQuery;
})
require_sammy.js
:
// These must be in order, so use the "order!" plugin.
define([ "order!jquery"
, "order!/path/to/custom/sammy/sammy-0.6.2-min.js"
, "order!/path/to/custom/sammy/plugins/sammy.json-0.6.2-min.js"
, "order!/path/to/custom/sammy/plugins/sammy.storage-0.6.2-min.js"
, "order!/path/to/custom/sammy/plugins/sammy.mustache-0.6.2-min.js"
]
, function($) {
// Raw sammy does not return anything, so return it explicitly here.
return $.sammy;
}
);