In SeaJS, any JavaScript file should be written in module format, and files and modules are in one-to-one correspondence.
A global function "define" is available to define modules. The signature of this function is:
define(id?, dependencies?, factory);
The unique identifier for the current module. This argument is optional, and if it is not present, the loader will set the module id to the request uri of this module file. When present, the module id MUST be a top-level or absolute id (relative ids are NOT allowed).
The module’s dependency array which is an array of module identifiers. This
argument is optional. If it is not present, the loader will parse the
factory.toString()
to extract the dependency.
** NOTICE: It is highly recommended to ignore the id
and
dependencies
arguments in the define statement. The
optimization tool will generate these arguments automatically in the
deployment phase.
A function that will be executed only once to initialize the module. In addition to a function, the factory argument can also be an object, a string, or any other value, and in those case, the module exports are set to the factory value directly.
Three symbols are available in the function body of factory to every module:
require
, exports
, and module
.
define(function(require, exports, module) { // The module code goes here });
The exports
object is used to export the API of the module.
define(function(require, exports) { // snip... exports.foo = 'bar'; exports.doSomething = function() {}; });
Except adding members to the exports
object, you can also use
return
to provide the API directly.
define(function(require) { // snip... return { foo: 'bar', doSomething: function() {}; }; });
If the return
statement is the only code in the module code,
it can be simplified to:
define({ foo: 'bar', doSomething: function() {}; });
The above authoring format is especially suited for defining JSON data.
** ATTENTION: The following code DOES NOT work!
define(function(require, exports) { // snip... exports = { // WRONG! foo: 'bar', doSomething: function() {}; }; });
The module loader cannot obtain the new value assigned to the
exports
variable. Please use return
or
module.exports
to expose the API in this case.
The require
function is used to access the exported API of
foreign modules.
define(function(require) { var a = require('./a'); a.doSomething(); });
It accepts a single module identifier as parameter.
Keep in mind that you'll need to follow a few simple rules to make sure that static analysis will be able to detect the dependencies within your module in the development phase.
Use this function to load the specified modules asynchronously and execute the optional callback when complete.
define(function(require, exports) { // load one module require.async('./b', function(b) { b.doSomething(); }); // load multiple modules require.async(['./c', './d'], function(c, d) { // do something }); });
Use the internal require()
machinery to look up the location
of a module, but rather than loading the module, just return the resolved
uri.
Use this function to fetch the specified uris asynchronously and execute the optional callback when complete. You can override this function to implement custom fetching method.
Occasionally, we want to add some members to the require
argument in all module factories. In this case, using
require.constructor
is very convenient.
The module object contains metadata about the module. It contains the following members:
The unique identifier for the current module.
require(module.id)
must return this module’s exports object.
define(function(require, exports, module) { console.log(module.id); // http://path/to/this/file.js console.log(require(module.id) === exports); // true });
module.dependencies
is a reference to the module’s dependency
array.
This array is informative only: modification of this array has no effect on the module once the module has been provided to the environment.
The exports
object is created by the module system. Sometimes
this is not acceptable, many want their module to be an instance of some
class. To do this assign the desired export object to
module.exports
.
define(function(require, exports, module) { console.log(module.exports === exports); // true module.exports = new SomeClass(); console.log(module.exports === exports); // false });
Note that assignment to module.exports
must be done
immediately. It cannot be done in any callbacks. This does not work:
x.js:
define(function(require, exports, module) { setTimeout(function() { module.exports = { a: "hello" }; }, 0); });
y.js:
define(function(require, exports, module) { var x = require('./x'); console.log(x.a); // undefined });
Occasionally, we want to add some members to all module
objects. In this case, using module.constructor
is very
convenient.
extend.js:
define(function(require, exports, module) { var Module = module.constructor; Module.prototype.filename = function() { var id = this.id; var parts = id.split('/'); return parts[parts.length - 1]; }; });
a.js:
define(function(require, exports, module) { exports.filename = module.filename(); });