读RequireJS — Module

Module 即模块,来看下RequireJS怎么定义 Module :

1. 简单的名值对

?
// Inside file my/shirt.js:
define({
     color: "black" ,
     size: "unisize"
});

2. 定义成一个Function

?
// my/shirt.js now does setup work
// before returning its module definition.
define( function () {
     // Do setup work here
 
     return {
         color: "black" ,
         size: "unisize"
     }
});

3. 如果 Module 需要依赖别的模块,可以写成这样:

?
// my/shirt.js now has some dependencies, a cart and inventory
// module in the same directory as shirt.js
define([ "./cart" , "./inventory" ], function (cart, inventory) {
         // return an object to define the "my/shirt" module.
         return {
             color: "blue" ,
             size: "large" ,
             addToCart: function () {
                 inventory.decrement( this );
                 cart.add( this );
             }
         }
     }
);

4. 隐式依赖Function

?
define( function (require) {
     var mod = require( "./relative/name" );
});

  

上面这四种比较常用,RequireJS 中的 Module 还有其他的形式,具体可参阅官方文档。

上面四种方式都用到了define(),接下来我们就来看下define的源码:

?
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
  * The function that handles definitions of modules. Differs from
  * require() in that a string for the module should be the first argument,
  * and the function to execute after dependencies are loaded should
  * return a value to define the module corresponding to the first argument's
  * name.
  */
define = function (name, deps, callback) {
     var node, context;
 
     // 可以是匿名模块
     if ( typeof name !== 'string') {
         // 调整参数
         callback = deps;
         deps = name;
         name = null ;
     }
 
     // 如果模块没有依赖
     if (!isArray(deps)) {
         callback = deps;
         deps = [];
     }
 
     // 如果是一个没有传入依赖的Function,即例4所示
     if (!deps.length && isFunction(callback)) {
         // 通过 callback.toString() 找依赖
         // 为了避免 require(xx) 写在注释中
         // 这里先把注释都去掉,再把require的东东加入deps数组
         //
         // 很变态的一点就是如果callback里写了require
         // 则 callback 必须有形参,不然会被无视
         if (callback.length) {
             callback
                 .toString()
                 .replace(commentRegExp, "" )
                 .replace(cjsRequireRegExp, function (match, dep) {
                     deps.push(dep);
                 });
 
             //May be a CommonJS thing even without require calls, but still
             //could use exports, and module. Avoid doing exports and module
             //work though if it just needs require.
             //REQUIRES the function to expect the CommonJS variables in the
             //order listed below.
             // 可能某些模块即使没有调用require,但却仍然要用到exports和module
             // 如果模块仅仅需要require,就应该避免exports和module的开销
             // 注意:依赖的顺序应该确保像下面这样
             //
             // 这里我把中英文都给出了,因为对于这句我一直很疑惑
             // 1个形参 需要 require
             // 大于1个形参 需要 require exports module
             // 1和非1有那么大差别么,请高手指点一下,这句实在很无解
             deps = (callback.length === 1 ? [ "require" ] : [ "require" , "exports" , "module" ]).concat(deps);
         }
     }
 
     // 应用场景:IE6-8,并且是定义一个匿名模块
     // 个人觉得这个判断没有意义,请看下面的注释
     if (useInteractive) {
         node = currentlyAddingScript || getInteractiveScript();
         if (node) {
             if (!name) {
                 name = node.getAttribute( "data-requiremodule" );
             }
             context = contexts[node.getAttribute( "data-requirecontext" )];
         }
     }
 
     // 真正定义模块的操作放到script onload函数中执行,有两个好处:
     // 1.如果一个文件定义了多个模块,这样可以防止过早的分析依赖, 而是等拿到所有依赖之后, 统一进行处理
     // 2.如果是定义匿名模块, define执行中我们是不知道 moduleName的, 但在onload函数中却可以知道
     //
     // 这里有个问题,即下面这行代码表示有context就加入context.defQueue,没有就加入globalDefQueue
     // 很明显,context有值的情况仅在IE6-8会出现,
     // 而在onload时,会把globalDefQueue里的东西全部加入context.defQueue,所以加入谁无所谓
     // 而且在onload函数中所有浏览器都可以拿到moduleName,所以下面这句和上面那个if判断都失去意义了
     // 可以写成 globalDefQueue.push([name, deps, callback]);
     (context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
 
     return undefined;
};

define() 有三个参数,不传 name 参数表示为匿名模块,匿名只是写法上的匿名,RequireJS 可以通过文件路径确定 moduleName,所以最好别传 name 参数,这样文件目录即使有变动,也不会影响模块的正常使用。

 

这里 callback 参数命名很不好,一般都会叫做factory,取名 callback 完全不知所云。  

所谓的 define 是干嘛的呢 ?

这个函数用于定义模块,可具名,也可匿名,可有依赖,也可无依赖,factory可以是一个function,也可以是一个object,define 就是为当前的模块绑定一个 factory,当我们要用这个模块的时候,再通过 factory 把它造出来。当然,factory是个object,就不需要再造了。

可以看到模块不是单独存在的,一般都会依赖别的模块,我们知道,在web开发中, 如果要加载一个资源,都需要在回调函数中进行处理,因为加载是异步的。

而 CommonJS 想实现同步加载的概念,所以每次加载一个模块,会首先把依赖分析出来,加载完依赖之后,再去执行factory,这样倒也算是一种模拟同步了。

你可能感兴趣的:(requirejs)