-
Notifications
You must be signed in to change notification settings - Fork 3
Loader
- 在objectjs中,代码的最基本组织形式是 模块 。除了浏览器原生成员,任何代码成员都应该属于某个模块;
- objectjs中的模块只有在 被调用时 才会被执行,因此,它具有沙箱的特性,多次调用过程中,产生的模块实例不是同一个,避免了全局污染;
object.define(id, dependencies?, factory);
当前模块的唯一标识,一般为一个路径;
当前模块的依赖模块,用“,”(逗号)分隔,也可以传递一个数组,如['dom', 'events']
;
objectjs的Loader的创新点在于,在兼容CommonJS形式的模块的同时,支持传统的命名空间式 子模块 ,且两种模块可一起使用。两种形式是:
- CommonJS形式,为一个路径形式,如
a/b/c
,./b
; - ObjectJS形式,为命名空间形式,如
a.b.c
;
两种依赖的区别是:
- CommonJS依赖支持相对依赖,如
./b
则代表当前模块下名为b
的模块; - ObjectJS依赖支持子模块,如依赖名为
a.b.c
的模块时,可通过获取到a
的引用,调用a.b.c
上的成员,同传统的 命名空间 式相同; - ObjectJS依赖的子模块在调用时会自动初始化其父模块;
- 通过
object.define
指定了路径形式的id
的模块,可直接通过ObjectJS的依赖形式获取其引用。
假设已定义3个模块,分别为a
,a/b
,a/b/c
。
示例:
- 定义
a
模块时,可通过./b
依赖获取到a/b
模块的引用; - 可通过
a.b.c
形式的依赖,将a/b
模块的引用放到a
模块的b
成员上,将a/b/c
模块的引用放到a/b
模块的c
成员上; - ObjectJS形式的模块依赖,并不支持相对引用,
./b.c
形式的依赖会找不到模块a/b.c
。
模块构造函数,模块初始化时,会调用此函数并传递3个参数,分别为require、exports和module。
通过require获取依赖模块的引用。
object.define('mymodule', 'ui, ./submodule, ui/slider, ui.popup', function(require, exports) {
var ui = require('ui'); // ui上有popup成员,没有slider成员
var slider = require('ui/slider');
var submodule = require('./submodule'); // 通过相对路径获取 mymodule/submodule 模块
});
用来异步加载模块。
object.define('mymodule', function(require, exports) {
require.async('./b, ./c', function(b, c) {
});
});
在模块构造函数内部,通过exports
提供此模块的成员(同this)。
在模块构造函数内部可通过this.__name__
获取到当前模块的名称。
object.define('mymodule', function(require, exports, module) {
// 模块函数
exports.func = function() {};
// 模块变量,this == exports
this.foo = 1;
// 模块类
exports.MyClass = new Class(function() {});
console.log(this.__name__);
});
同时,也可通过return
返回模块的成员,此时,向exports上添加的任何成员都是无效的。
object.define('mymodule', function(require, exports, module) {
// 通过return返回模块内容
return {
func: function() {},
foo: 1,
MyClass: new Class(function() {})
};
});
objectjs还支持一种一种类似python的模块定义方式(object.add)。
-
object.define兼容SeaJS的模块书写规范,因此也可通过维护脚本兼容nodejs的模块;而add的模块从书写上并不完全兼容;
-
object.define支持通过
require.async
进行异步调用,而object.add不支持; -
通过add定义module的优势在于减少模块名依赖模块的代码量,自动获取依赖模块引用,减少代码量;
-
根据以上区别,建议采用object.define声明模块;
-
两者建立的模块可以互相依赖,并无兼容性问题。
object.add(id, dependencies?, factory);
id和dependencies方法同object.define完全一致。
factory,模块构造函数,模块初始化时,会调用此函数并传递参数,第一个参数为exports,与回调函数中的this指向同一对象,可用于输出模块成员。
依赖的模块会通过函数参数 非重复顺序引用 的形式传递给主体函数。如果依赖模块是个子模块,则属于同一个父模块的多个子模块会放于同一父模块的引用中;
在模块构造函数中通过exports
或this
输出模块成员,同object.define的规则是一样的。
object.add('ui', function(exports) {
this.test = 1;
});
object.add('myobjectmodule', 'ui/slider, ui.popup', function(exports, slider, ui) {
// 属于同一个父模块的多个子模块会放于同一父模块的引用中,子模块在调用时会自动初始化其父模块
console.log(ui); // ==> {test: 1, popup: <Module popup>}
}
将一个已命名的模块直接执行,此时此模块主体函数中获取到的this.__name__
为__main__
而不是模块名
execute方法会产生一个运行时,一个运行时中的每个模块都会有一个且只有一个唯一实例,也就是说,在一个运行时中,一个模块如果被多个模块依赖调用,获取到的实例相同。
object.execute('mymodule'); // ==> __main__
利用此特性可在模块中判断当前__name__
是否为__main__
得知此模块是被依赖调用的还是被直接调用的。
形式同object.add类似,只是没有第一个模块名的参数
object.use就是object.add/define和object.execute的结合,object.use会向系统中注册一个匿名模块,并立刻execute。
object.use('mymodule, dom, event', function(mymodule, dom, event) {
mymodule.func();
console.log(mymodule.foo); // ==> 1
console.log(this.__name__); // ==> __main__
}
objectjs支持动态加载,减少网络请求数。需要在网页中预留需要动态加载的模块信息:
<script type="text/javascript" data-src="http://path/to/js.js" data-module="模块名称"></script>
这样,当object.use找不到需要的module时,会通过页面中的信息进行动态加载。
为了减少网络请求数,多个模块可以放置于同一个js中。data-module
属性可以设置多个模块名:
<script type="text/javascript" data-src="http://path/to/3-modules-in-1.js" data-module="module1 module2 module3"></script>
return返回模块的形式也可以返回一个function,就实现了一个function形式的模块。
object.add('jquery', function() { function jQuery() { // ... } jQuery.ajax = function() { } // ... return jQuery; });
object.use('jquery', function(exports,
系统中有一个内置的sys模块,其中的modules成员保存了此次use产生的运行时中的所有模块实例的引用
object.use('sys, mymodule', function(exports, sys, mymodule) {
// 在这里,sys.modules 保存着所有运行过程中用到的模块的引用
console.log(sys.modules); // ==> {dom: <Module dom>, event: <Module event>, sys: <Module sys>, mymodule: <Module mymodule>}
// 这样就能实现对某个运行时的模块进行修改、扩展的功能
var _submodule = sys['mumodule.submodule'];
if (_submodule) {
_submodule.bar = 2;
}
});
object.execute可在模块中判断当前__name__
是否为__main__
得知此模块是被依赖调用的还是被直接调用的, 因此可以判断直接调用时可自动执行一些初始化方法,实现模块的自初始化,将初始过程隐藏。
object.add('myeditor', function() {
// 一个编辑器
this.listen = function() {
var eles = document.getElementsByClassName('editor');
// ...
// 初始化每个元素成为编辑器
};
// 自初始化
if (this.__name__ == '__main__') {
this.listen();
}
});
用object.use,需要知道myeditor的listen成员并经行调用,才可以在页面中初始化
object.use('myeditor', function(exports, myeditor) {
// object.use生成myeditor实例时this.__name__为myeditor,不会进行自初始化
myeditor.listen();
});
或用object.execute,自动初始化,无需得知其listen成员
object.execute('myeditor'); // 此时mymodule中的this.__name__为__main__
可通过 object._loader.lib
查看当前页面中已经注册的所有模块列表