Justin's Words

AMD 和 RequireJS

摘录自:Next Generation JavaScript with AMD and RequireJS

为了解决的问题

1
2
3
<script src="file1.js"></script>
<script src="file2.js"></script>
<script src="file3.js"></script>

异步模块定义 (Asynchronous Module Definition as AMD)

dom/events.js 定义模块

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
define({
addEvent: function (el, evt, fn) {
if (el.addEventListener) {
this.addEvent = function (el, evt, fn) {
el.addEventListener(evt, fn, false);
return el;
};
} else if (el.attachEvent) {
this.addEvent = function (el, evt, fn) {
el.attachEvent("on" + evt, fn);
return el;
};
} else {
this.addEvent = function (el, evt, fn) {
el["on" + evt] = fn;
return el;
};
}
return this.addEvent(el, evt, fn);
},
removeEvent: function (el, evt, fn) {
if (el.removeEventListener) {
this.removeEvent = function (el, evt, fn) {
el.removeEventListener(evt, fn, false);
return el;
};
} else if (el.detachEvent) {
this.removeEvent = function (el, evt, fn) {
el.removeEvent("on" + evt, fn);
return el;
};
} else {
this.removeEvent = function (el, evt, fn) {
el["on" + evt] = fn;
return el;
};
}
return this.removeEvent(el, evt, fn);
}
});

main.js 使用 RequireJS 加载模块,模块不需要后缀

1
2
3
require(["dom/events"], function (event) {

});

main.js 使用模块

1
2
3
4
5
6
require(["dom/events"], function (events) {
var elem = document.getElementById("target");
events.addEvent(elem, "click", function () {
alert("clicked");
});
});

index.html

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>Learn Require.js</title>
<script data-main="js/main" src="js/require.js"></script>
</head>
<body>
<h1 id="target">Learning Require.js</h1>
</body>
</html>

让模块稍微复杂点 dom/events.js

1
2
3
4
5
6
7
define(function () {
var eventObject = { /* the object we had previously */ };

// we will add our sugar methods dynamically in here.

return eventObject;
});

再写个 Array.prototype.forEach 兼容方法 utils/array.js

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
define(function () {
var nativeForEach = function (list, callback, thisArg) {
[].forEach.call(list, callback, thisArg);
};
var customForEach = function (list, callback, thisArg) {
var T, k = 0, o, len, kValue;

if (list == null) {
throw new TypeError(" this is null or not defined");
}

O = Object(list);
len = O.length >>> 0;

if ({}.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}

if (thisArg) {
T = thisArg;
}

while(k < len) {
if (k in O) {
kValue = O[k];
callback.call(T, kValue, O);
}
k++;
}
};

return {
forEach: (function () {
if ({}.toString.call([].forEach) === "[object Function]") {
return nativeForEach;
} else {
return customForEach;
}
}())
};
});

同样使这个模块稍微复杂点

1
2
3
4
5
6
7
define(["utils/array"], function (array) {
var eventObject = { /* the object we had previously */ };

// we will add our sugar methods dynamically in here.

return eventObject;
});

dom/events.js 中使用 utils/array.js,添加依赖关系

1
2
3
4
5
6
7
8
9
10
11
define(["utils/array"], function (array) {
var eventObject = { /* the object we had previously */ };

array.forEach(["click", "mouseover","mouseout", "keypress"], function(evt) {
eventObject[evt] = function (el, fn) {
this.addEvent(el, evt, fn);
};
});

return eventObject;
});

main.js 使用添加了 utils/array.js 依赖的 dom/events.js

1
2
3
event.click(elem, function () {
alert("clicked");
});

模块的名字不指定则为模块默认名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require(["utils/array"], function () {
var arr = [1,2,3];

arrayUtils.forEach(arr, function (item) {
// action here
});
});
```javascript

配置 **RequireJS**

`index.html` 加载文件

```html
<script src="js/require.js"></script>
<script src="js/main.js"></script>

main.js 完整配置

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
requirejs.config({
baseUrl: './js',
paths: {
jquery: 'bower_components/jquery/dist/jquery.min',
underscore: 'bower_components/underscore/underscore-min',
angular: 'bower_components/angular/angular.min',
bootstrap: "bower_components/bootstrap/dist/js/bootstrap.min",
semantic: "bower_components/semantic-ui/dist/semantic.min",
C: 'js/controllers',
S: 'js/services',
F: 'js/filters',
D: 'js/directives'
},
shim: {
jquery: {
exports: '$'
},
underscore: {
exports: '_'
},
angular: {
deps: ['jquery']
},
bootstrap: {
deps: ['jquery']
},
semantic: {
deps: ['jquery']
}
}
});

RequireJS 插件

text plugin 为例

text.js 放在 main.js 同级目录,在 main.js 请求

1
2
3
4
5
6
7
8
9
10
11
12
require(["text!templates/list.html", "libs/underscore"], function (listTpl) {
// Underscore is loaded globally.
var context = {
people : {
"Fredrick" : "Back-end Developer",
"Victoria" : "Front-end Developer",
"Hamilton" : "Designer",
"Georgea" : "Content Strategist"
}
};
document.body.innerHTML = _.template(listTpl)(context);
});

templates/list.html 模板用的是 Rails html 模板语法,因为 underscorejs_.template() 默认这种写法

1
2
3
4
5
<ul>
<% _.each(people, function (job, name) { %>
<li><strong><%= name %></strong>: <%= job %></li>
<% }); %>
</ul>

优化工具

app.build.js

1
2
3
4
5
6
7
8
9
10
({
appDir: "../",
baseUrl: "js",
dir: "../../sample-build",
optimizeCSS: "standard",
paths : {
"underscore" : "libs/underscore-min"
},
modules: [ { name: "main" } ]
})

r.js 默认优化配置,可以选择性改变

1
2
3
4
5
6
7
8
9
10
buildBaseConfig = {
appDir: "",
pragmas: {},
paths: {},
optimize: "uglify",
optimizeCss: "standard.keepLines",
inlineText: true,
isBuild: true,
optimizeAllPluginResources: false
};

开始 build 优化

1
node /path/to/r.js -o app.build.js

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Optimizing (standard) CSS file: /Users/andrew/Sites/sample-build/css/custom.css
Optimizing (standard) CSS file: /Users/andrew/Sites/sample-build/css/main.css

Tracing dependencies for: main
Uglifying file: /Users/andrew/Sites/sample-build/js/app.build.js
Uglifying file: /Users/andrew/Sites/sample-build/js/dom/events.js
Uglifying file: /Users/andrew/Sites/sample-build/js/libs/underscore-min.js
Uglifying file: /Users/andrew/Sites/sample-build/js/main.js
Uglifying file: /Users/andrew/Sites/sample-build/js/require.js
Uglifying file: /Users/andrew/Sites/sample-build/js/text.js
Uglifying file: /Users/andrew/Sites/sample-build/js/utils/array.js

js/main.js
----------------
js/utils/array.js
js/dom/events.js
js/text.js
text!templates/list.html
js/libs/underscore-min.js
js/main.js