一步步学会使用SeaJS 2.0
本文分为如下8步,熟悉以后就可以熟练使用SeaJS,今后以后你的生活会变得更加轻松愉悦!
javascript
一、SeaJS是什么?css
二、下载并检阅SeaJShtml
三、创建工程和各类目录前端
四、引入SeaJS库java
五、编写本身的代码node
六、引入本身的代码jquery
七、压缩合并git
八、总结展望程序员
--------------------------------------------------github
一、SeaJS是什么?
你必定听过前端模块化开发吧?神马,你没听过?我只能说你out了……
你应该知道Java的import吧?神马,你又不知道?那你应该知道CSS中的import吧……
在这里我不想展开说前端模块化的含义和价值,由于这里有一篇好文(https://github.com/seajs/seajs/issues/547),详细说明了前端模块化。
我知道你看到那么长的文章确定会望而却步,也许你是但愿可以快速开始敲代码(程序员的通病……)。不要紧,若是实在读不下去,只要记住模块化要解决的问题便可:命名冲突、文件依赖关系。
这两个闹心的问题应该遇到过吧,若是没遇到过……我只能说你太牛X了
好了,前端模块化就扯到这里,写过前端的人应该基本都知道JavaScript自身是不支持模块化开发的,因此就有了SeaJS这款神器,为前端从业者提供了一个强大易用的模块化开发工具。
二、下载并检阅SeaJS
SeaJS如今已是2.0版本啦,到这里下载:https://github.com/seajs/seajs
解压后会看到下列目录:
其中:
dist —— 压缩好的、用于浏览器端的SeaJS代码
docs —— 文档
src —— 源代码
package.json + Gruntfile.js —— Grunt构建工具所须要的文件,这个在第七步压缩合并会介绍到
其余目录或文件能够暂且无论
三、创建工程和各类目录
准备工做已经完成,咱们终于能够开始进行开发啦!来,跟我走:
a. 创建工程
用你最喜欢的IDE创建工程,名字为HelloSeaJS
b. 准备各类目录
在这里把JavaScript、Image、CSS都放在统一的资源文件(assets)中,建好以后的目录以下:
(我使用了Sublime2.0,在这里强烈推荐)
c. 把刚刚下好的seajs/dist中的文件都放在scripts/seajs目录下
注意:SeaJS会根据自身的URI来决定URL base,而SeaJS在加载其余模块的时候会根据这个URL base来计算路径。SeaJS会忽略掉seajs、seajs/2.0.0/seajs这两种目录,照上述的目录结构,此处的URL base就是HelloSeaJS/assets/scripts,这样其余模块就能够与seajs目录并行存放。
至此,工程和文件都已准备完成。
四、引入SeaJS库
与引入其余js库并没有太大区别:
<script src="assets/scripts/seajs/sea.js" id="seajsnode"></script>
你可能注意到,这里加上了id="seajsnode",缘由以下:
a. SeaJS加载自身的script标签的其余属性(如data-config、data-main)等来实现不一样的功能
b. SeaJS内部经过document.getElementById("seajsnode")来获取这个script标签(其实SeaJS内部还有一种方式,不过另外一种方式的效率比较低,因此不推荐,若是有兴趣,能够看一下源码https://github.com/seajs/seajs/blob/master/src/util-path.js)
五、编写本身的代码
这里做为示范,只作了一个很是简单的效果,点击查看:http://liuda101.github.io/HelloSeaJS/
在编写本身代码的时候,要时刻记住”模块化“,而操做起来也很是简单,由于在SeaJS中一个文件就是一个模块。
下面是代码逻辑的模块application.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
define(
function
(require,exports,module){
var
util = {};
var
colorRange = [
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
];
util.randomColor =
function
(){
return
'#'
+
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)];
};
var
helloSeaJS = document.getElementById(
'hello-seajs'
);
helloSeaJS.style.color = util.randomColor();
window.setInterval(
function
(){
helloSeaJS.style.color = util.randomColor();
},1500);
});
|
咱们看到,全部代码都放在define(function(require,exports,module){});函数体里面。
define是SeaJS定义的一个全局函数,用来定义一个模块。
至于require,exports,module都是什么,能够暂且无论,到此,咱们的代码已经完成,很简单吧。嗯,花个几十秒钟,看一下代码。
……
看完以后,你会说,这算什么啊!这就完了么?
不要怪我,为了简单易懂,咱们就按照”一步步“的节奏慢慢来。
随着代码的增多,你确定会遇到util愈来愈多的状况。很好,这样看来,咱们就有了两个模块:util模块和application模块。SeaJS中,文件即模块,因此固然要将其分为两个文件。先看util.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
define(
function
(require,exports,module){
var
util = {};
var
colorRange = [
'0'
,
'1'
,
'2'
,
'3'
,
'4'
,
'5'
,
'6'
,
'7'
,
'8'
,
'9'
,
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
];
util.randomColor =
function
(){
return
'#'
+
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)] +
colorRange[Math.floor(Math.random() * 16)];
};
module.exports = util;
});
|
除了define以外,咱们看到module.exports = util;这一句比较特殊。这句是在说,我util模块向外暴露的接口就这些,其余全部的东西都是我内部用的,尔等就无需担忧了,我会照顾好的。
再看application.js:
1
2
3
4
5
6
7
8
9
10
|
define(
function
(require,exports,module){
var
util = require(
'./util'
);
var
helloSeaJS = document.getElementById(
'hello-seajs'
);
helloSeaJS.style.color = util.randomColor();
window.setInterval(
function
(){
helloSeaJS.style.color = util.randomColor();
},1500);
});
|
咱们看到var util = require('./util');这句比较特殊。这句就是在说,我application模块因为业务须要,想请util模块来帮忙,因此把util给require进来。
至此,咱们经历了一个模块到两个模块的转变,在往后漫长的日子中,咱们的模块也许会愈来愈多,不过不用担忧,有了SeaJS提供的define、require、module.exports,咱们均可以方便的应对。
六、引入本身的代码
你看到这个小标题,你可能会极力的鄙视我,这等工做还须要你来示范?因而,你啪啪啪啪,在引入SeaJS的script标签后引入了util.js和application.js:
<script src="assets/scripts/application/util.js"></script>
<script src="assets/scripts/application/application.js"></script>
而后你不停的F5……
你看不到效果吧?这就是这个小节存在的理由。
SeaJS提供了模块化的能力,前面咱们已经看到了SeaJS定义模块、引用模块的方法,而这里就要用到SeaJS加载并启动模块的两种方式:
a、使用data-main
为<script src="assets/scripts/seajs/sea.js" id="seajsnode"></script>添加data-main="application/application"属性便可:
<script src="assets/scripts/seajs/sea.js" id="seajsnode" data-main="application/application"></script>
SeaJS会根据data-main指定的模块来做为整个应用的入口模块。SeaJS找到这个模块以后,就会加载执行这个模块对应的文件。
那么,SeaJS又是怎么找到这个文件呢?也就是说,这个模块对应的加载路径是多少?
“算法”是:SeaJS_URL_base + data-main
如上文,该例子的SeaJS_URL_base是HelloSeaJS/assets/scripts/
那么,加载路径就是HelloSeaJS/assets/scripts/application/application.js(SeaJS会自动加上.js后缀)
b、使用seajs.use
在<script src="assets/scripts/seajs/sea.js" id="seajsnode">后面加上:
<script> seajs.use("application/application"); </script>
其实这两种效果在这个例子中是同样的,data-main一般用在只有一个入口的状况,use能够用在多个入口的状况,具体用法,看这里:https://github.com/seajs/seajs/issues/260
若是你对你的程序有彻底的控制权,建议使用data-main的方式,这样整个页面就只有一段script标签!做为一名前端开发人员,我不得不惊叹:干净、完美!
不管使用哪一种方式,跟着我一块儿F5一下!
在打开Chrome的debug工具,查看Network这个tab:
咱们看到,SeaJS已经帮咱们加载好了application.js和util.js,舒服吧~
嗯,我第一次试用SeaJS的时候,到这里也感到了无比的舒心
七、压缩合并
正当我伸伸懒腰,打算上个厕所的时候,忽然想到一件事情:若是模块愈来愈多,那么多文件都要分开加载?那岂不严重影响性能!?(啥,你不知道为啥?)
要压缩合并JavaScript呀!因而,我强忍住那股液体,开始用YUICompressor来压缩,并手动合并了两个文件。
这里就不展现结果了,由于很蛋疼,彻底对不住我刚才忍住液体的勇气!结果固然是,失败。
为何会失败呢?本身想了想,同时打开压缩后的代码一看,才发现缘由:
压缩后以后,require变量变成了a变量。SeaJS是经过require字面来判断模块之间的依赖关系的,因此,require变量不能被简化。
嗯,SeaJS已经替咱们想到了这个问题,因而咱们就采用SeaJS提供的方式来合并压缩吧(固然你也能够本身用别的方式压缩)。
SeaJS在2.0以前,是采用SPM做为压缩合并工具的,到了2.0,改成Grunt.js,SPM变为包管理工具,相似NPM(不知道NPM?Google一下吧)
自动化不只是科技带给社会的便利,也是Grunt带给前端的瑞士军刀。使用Grunt,能够很方便的定制各类任务,如压缩、合并等。使用Grunt以前,须要安装node环境和grunt工具,Google一下,十分钟后回来。
……
Grunt最核心的就两个部分,package.json、Gruntfile.js。
a. package.json
Grunt把一个项目/目录视为一个npm模块,package.json就是用来描述这个模块的信息,包括name、version、author等等。
这里强调一下,Grunt既然将该目录视为一个模块,那么该模块固然能够依赖其余模块。
咱们看本示例的:
1
2
3
4
5
6
7
8
9
10
11
12
|
{
"name" : "HelloSeaJS",
"version" : "1.0.0",
"author" : "Qifeng Liu",
"devDependencies" : {
"grunt" : "0.4.1",
"grunt-cmd-transport" : "0.1.1",
"grunt-cmd-concat" : "0.1.0",
"grunt-contrib-uglify" : "0.2.0",
"grunt-contrib-clean" : "0.4.0"
}
}
|
devDependencies就是用来描述自身所依赖的模块
其中:
grunt模块用来跑Gruntfile.js中定义的任务
grunt-cmd-transport模块用来对SeaJS定义的模块进行依赖提取等任务
grunt-cmd-concat模块用来对文件进行合并
grunt-contrib-uglify模块用来压缩JavaScript
grunt-contrib-clean模块用来清除临时目录
b. Gruntfile.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
42
43
|
module.exports =
function
(grunt){
grunt.initConfig({
transport : {
options : {
format :
'application/dist/{{filename}}'
//生成的id的格式
},
application : {
files : {
'.build'
: [
'application.js'
,
'util.js'
]
//将application.js、util.js合而且提取依赖,生成id,以后放在.build目录下
}
}
},
concat : {
main : {
options : {
relative :
true
},
files : {
'dist/application.js'
: [
'.build/application.js'
],
// 合并.build/application.js文件到dist/application.js中
'dist/application-debug.js'
: [
'.build/application-debug.js'
]
}
}
},
uglify : {
main : {
files : {
'dist/application.js'
: [
'dist/application.js'
]
//对dist/application.js进行压缩,以后存入dist/application.js文件
}
}
},
clean : {
build : [
'.build'
]
//清除.build文件
}
});
grunt.loadNpmTasks(
'grunt-cmd-transport'
);
grunt.loadNpmTasks(
'grunt-cmd-concat'
);
grunt.loadNpmTasks(
'grunt-contrib-uglify'
);
grunt.loadNpmTasks(
'grunt-contrib-clean'
);
grunt.registerTask(
'build'
,[
'transport'
,
'concat'
,
'uglify'
,
'clean'
])
};
|
定义好两个文件以后,就能够进入到application目录下,首先运行:
npm install
该命令会下载好package.json中依赖的模块
而后运行
grunt build
该命令会运行grunt.registerTask方法中指定的任务
不出差错的话,会在application目录下生成一个dist目录,里面包含了合并但没压缩的application-debug.js和合而且压缩好的application.js。
修改index.html的
<script src="assets/scripts/seajs/sea.js" id="seajsnode" data-main="application/application"></script>
为
<script src="assets/scripts/seajs/sea.js" id="seajsnode" data-main="application/dist/application"></script>
大功告成!
八、总结展望
SeaJS秉着简单实用的原则,API设计职责单一,使用起来驾轻就熟。
SeaJS为前端提供了模块化能力,能够简单优雅的解决命名冲突、依赖关系等常见且棘手的问题。经过使用SeaJS,个人生活质量一下次上升好几个档次。
固然,除了本文介绍的,SeaJS还有一些其余功能,相信只要你入了门,确定可以迅速掌握,这里给个连接http://seajs.org/docs/#docs
其实,许多语言自身已经有了模块化的功能,如Java的import,C++的#include等,可是因为各类缘由,JavaScript自身并无这个功能。虽然借助于SeaJS能够很方便的进行模块化开发了,但总以为这个能力应该是语言自身的。好在,下个版本的JavaScript貌似在计划引入模块化的概念,让咱们拭目以待吧!
最后,感谢SeaJS做者玉伯。
PS,本文参考了SeaJS提供的使用范例https://github.com/seajs/examples/tree/master/static/hello
SeaJS是一个遵循CommonJS规范的JavaScript模块加载框架,能够实现JavaScript的模块化开发及加载机制。与jQuery等JavaScript框架不一样,SeaJS不会扩展封装语言特性,而只是实现JavaScript的模块化及按模块加载。SeaJS的主要目的是令JavaScript开发模块化并能够轻松愉悦进行加载,将前端工程师从繁重的JavaScript文件及对象依赖处理中解放出来,能够专一于代码自己的逻辑。SeaJS能够与jQuery这类框架完美集成。使用SeaJS能够提升JavaScript代码的可读性和清晰度,解决目前JavaScript编程中广泛存在的依赖关系混乱和代码纠缠等问题,方便代码的编写和维护。
SeaJS的做者是淘宝前端工程师玉伯。
SeaJS自己遵循KISS(Keep It Simple, Stupid)理念进行开发,其自己仅有个位数的API,所以学习起来毫无压力。在学习SeaJS的过程当中,到处能感觉到KISS原则的精髓——仅作一件事,作好一件事。
本文首先经过一个例子直观对比传统JavaScript编程和使用SeaJS的模块化JavaScript编程,而后详细讨论SeaJS的使用方法,最后给出一些与SeaJS相关的资料。
假设咱们如今正在开发一个Web应用TinyApp,咱们决定在TinyApp中使用jQuery框架。TinyApp的首页会用到module1.js,module1.js依赖module2.js和module3.js,同时module3.js依赖module4.js。
使用传统的开发方法,各个js文件代码以下:
//module1.jsvar module1 = { run: function() { return $.merge(['module1'], $.merge(module2.run(), module3.run())); }} //module2.jsvar module2 = { run: function() { return ['module2']; }} //module3.jsvar module3 = { run: function() { return $.merge(['module3'], module4.run()); }} //module4.jsvar module4 = { run: function() { return ['module4']; }}
此时index.html须要引用module1.js及其全部下层依赖(注意顺序):
<!DOCTYPE HTML><html lang="zh-CN"><head> <meta charset="UTF-8"> <title>TinyApp</title> <script src="./jquery-min.js"></script> <script src="./module4.js"></script> <script src="./module2.js"></script> <script src="./module3.js"></script> <script src="./module1.js"></script></head><body> <p class="content"></p> <script> $('.content').html(module1.run()); </script></body></html>
随着项目的进行,js文件会愈来愈多,依赖关系也会愈来愈复杂,使得js代码和html里的script列表每每变得难以维护。
下面看看如何使用SeaJS实现相同的功能。
首先是index.html:
<!DOCTYPE HTML><html lang="zh-CN"><head> <meta charset="UTF-8"> <title>TinyApp</title></head><body> <p class="content"></p> <script src="./sea.js"></script> <script> seajs.use('./init', function(init) { init.initPage(); }); </script></body></html>
能够看到html页面再也不须要引入全部依赖的js文件,而只是引入一个sea.js,sea.js会处理全部依赖,加载相应的js文件,加载策略能够选择在渲染页面时一次性加载全部js文件,也能够按需加载(用到时才加载响应js),具体加载策略使用方法下文讨论。
index.html加载了init模块,并使用此模块的initPage方法初始化页面数据,这里先不讨论代码细节。
下面看一下模块化后JavaScript的写法:
//jquery.jsdefine(function(require, exports, module) = { //原jquery.js代码... module.exports = $.noConflict(true);}); //init.jsdefine(function(require, exports, module) = { var $ = require('jquery'); var m1 = require('module1'); exports.initPage = function() { $('.content').html(m1.run()); }}); //module1.jsdefine(function(require, exports, module) = { var $ = require('jquery'); var m2 = require('module2'); var m3 = require('module3'); exports.run = function() { return $.merge(['module1'], $.merge(m2.run(), m3.run())); }}); //module2.jsdefine(function(require, exports, module) = { exports.run = function() { return ['module2']; }}); //module3.jsdefine(function(require, exports, module) = { var $ = require('jquery'); var m4 = require('module4'); exports.run = function() { return $.merge(['module3'], m4.run()); }}); //module4.jsdefine(function(require, exports, module) = { exports.run = function() { return ['module4']; }});
乍看之下代码彷佛变多变复杂了,这是由于这个例子太简单,若是是大型项目,SeaJS代码的优点就会显现出来。不过从这里咱们仍是能窥探到一些SeaJS的特性:
一是html页面不用再维护冗长的script标签列表,只要引入一个sea.js便可。
二是js代码以模块进行组织,各个模块经过require引入本身依赖的模块,代码清晰明了。
经过这个例子朋友们应该对SeaJS有了一个直观的印象,下面本文具体讨论SeaJS的使用。
要在项目中使用SeaJS,你全部须要作的准备工做就是下载sea.js而后放到你项目的某个位置。
SeaJS项目目前托管在GitHub上,主页为 https://github.com/seajs/seajs/ 。能够到其git库的build目录下下载sea.js(已压缩)或sea-debug.js(未压缩)。
下载完成后放到项目的相应位置,而后在页面中经过<script>标签引入,你就可使用SeaJS了。
在讨论SeaJS的具体使用前,先介绍一下SeaJS的模块化理念和开发原则。
使用SeaJS开发JavaScript的基本原则就是:一切皆为模块。引入SeaJS后,编写JavaScript代码就变成了编写一个又一个模块,SeaJS中模块的概念有点相似于面向对象中的类——模块能够拥有数据和方法,数据和方法能够定义为公共或私有,公共数据和方法能够供别的模块调用。
另外,每一个模块应该都定义在一个单独js文件中,即一个对应一个模块。
下面介绍模块的编写和调用。
SeaJS中使用“define”函数定义一个模块。由于SeaJS的文档并无关于define的完整参考,因此我阅读了SeaJS源代码,发现define能够接收三个参数:
/*** Defines a module.* @param {string=} id The module id.* @param {Array.|string=} deps The module dependencies.* @param {function()|Object} factory The module factory function.*/fn.define = function(id, deps, factory) { //code of function…}
上面是我从SeaJS源码中摘录出来的,define能够接收的参数分别是模块ID,依赖模块数组及工厂函数。我阅读源代码后发现define对于不一样参数个数的解析规则以下:
若是只有一个参数,则赋值给factory。
若是有两个参数,第二个赋值给factory;第一个若是是array则赋值给deps,不然赋值给id。
若是有三个参数,则分别赋值给id,deps和factory。
可是,包括SeaJS的官方示例在内几乎全部用到define的地方都只传递一个工厂函数进去,相似与以下代码:
define(function(require, exports, module) { //code of the module...});
我的建议遵循SeaJS官方示例的标准,用一个参数的define定义模块。那么id和deps会怎么处理呢?
id是一个模块的标识字符串,define只有一个参数时,id会被默认赋值为此js文件的绝对路径。如example.com下的a.js文件中使用define定义模块,则这个模块的ID会赋值为 http://example.com/a.js ,没有特别的必要建议不要传入id。deps通常也不须要传入,须要用到的模块用require加载便可。
工厂函数是模块的主体和重点。在只传递一个参数给define时(推荐写法),这个参数就是工厂函数,此时工厂函数的三个参数分别是:
require——模块加载函数,用于记载依赖模块。
exports——接口点,将数据或方法定义在其上则将其暴露给外部调用。
module——模块的元数据。
这三个参数能够根据须要选择是否须要显示指定。
下面说一下module。module是一个对象,存储了模块的元信息,具体以下:
module.id——模块的ID。
module.dependencies——一个数组,存储了此模块依赖的全部模块的ID列表。
module.exports——与exports指向同一个对象。
第一种定义模块的模式是基于exports的模式:
define(function(require, exports, module) { var a = require('a'); //引入a模块 var b = require('b'); //引入b模块 var data1 = 1; //私有数据 var func1 = function() { //私有方法 return a.run(data1); } exports.data2 = 2; //公共数据 exports.func2 = function() { //公共方法 return 'hello'; }});
上面是一种比较“正宗”的模块定义模式。除了将公共数据和方法附加在exports上,也能够直接返回一个对象表示模块,以下面的代码与上面的代码功能相同:
define(function(require) { var a = require('a'); //引入a模块 var b = require('b'); //引入b模块 var data1 = 1; //私有数据 var func1 = function() { //私有方法 return a.run(data1); } return { data2: 2, func2: function() { return 'hello'; } };});
若是模块定义没有其它代码,只返回一个对象,还能够有以下简化写法:
define({ data: 1, func: function() { return 'hello'; }});
第三种方法对于定义纯JSON数据的模块很是合适。
上文说过一个模块对应一个js文件,而载入模块时通常都是提供一个字符串参数告诉载入函数须要的模块,因此就须要有一套从字符串标识到实际模块所在文件路径的解析算法。SeaJS支持以下标识:
绝对地址——给出js文件的绝对路径。
如
require("http://example/js/a");
就表明载入 http://example/js/a.js 。
相对地址——用相对调用载入函数所在js文件的相对地址寻找模块。
例如在 http://example/js/b.js 中载入
require("./c");
则载入 http://example/js/c.js 。
基址地址——若是载入字符串标识既不是绝对路径也不是以”./”开头,则相对SeaJS全局配置中的“base”来寻址,这种方法稍后讨论。
注意上面在载入模块时都不用传递后缀名“.js”,SeaJS会自动添加“.js”。可是下面三种状况下不会添加:
载入css时,如
require("./module1-style.css");
路径中含有”?”时,如
require(<a href="http://example/js/a.json?cb=func">http://example/js/a.json?cb=func</a>);
路径以”#”结尾时,如
require("http://example/js/a.json#");
根据应用场景的不一样,SeaJS提供了三个载入模块的API,分别是seajs.use,require和require.async,下面分别介绍。
seajs.use主要用于载入入口模块。入口模块至关于C程序的main函数,同时也是整个模块依赖树的根。上面在TinyApp小例子中,init就是入口模块。seajs.use用法以下:
//单一模式seajs.use('./a'); //回调模式seajs.use('./a', function(a) { a.run();}); //多模块模式seajs.use(['./a', './b'], function(a, b) { a.run(); b.run();});
通常seajs.use只用在页面载入入口模块,SeaJS会顺着入口模块解析全部依赖模块并将它们加载。若是入口模块只有一个,也能够经过给引入sea.js的script标签加入”data-main”属性来省略seajs.use,例如,上面TinyApp的index.html也能够改成以下写法:
<!DOCTYPE HTML><html lang="zh-CN"><head> <meta charset="UTF-8"> <title>TinyApp</title></head><body> <p class="content"></p> <script src="./sea.js" data-main="./init"></script></body></html>
这种写法会令html更加简洁。
require是SeaJS主要的模块加载方法,当在一个模块中须要用到其它模块时通常用require加载:
var m = require('/path/to/module/file');
这里简要介绍一下SeaJS的自动加载机制。上文说过,使用SeaJS后html只要包含sea.js便可,那么其它js文件是如何加载进来的呢?SeaJS会首先下载入口模块,而后顺着入口模块使用正则表达式匹配代码中全部的require,再根据require中的文件路径标识下载相应的js文件,对下载来的js文件再迭代进行相似操做。整个过程相似图的遍历操做(由于可能存在交叉循环依赖因此整个依赖数据结构是一个图而不是树)。
明白了上面这一点,下面的规则就很好理解了:
传给require的路径标识必须是字符串字面量,不能是表达式,以下面使用require的方法是错误的:
require('module' + '1'); require('Module'.toLowerCase());
这都会形成SeaJS没法进行正确的正则匹配如下载相应的js文件。
上文说过SeaJS会在html页面打开时经过静态分析一次性记载全部须要的js文件,若是想要某个js文件在用到时才下载,可使用require.async:
require.async('/path/to/module/file', function(m) { //code of callback...});
这样只有在用到这个模块时,对应的js文件才会被下载,也就实现了JavaScript代码的按需加载。
SeaJS提供了一个seajs.config方法能够设置全局配置,接收一个表示全局配置的配置对象。具体使用方法以下:
seajs.config({ base: 'path/to/jslib/', alias: { 'app': 'path/to/app/' }, charset: 'utf-8', timeout: 20000, debug: false});
其中base表示基址寻址时的基址路径。例如base设置为 http://example.com/js/3-party/ ,则
var $ = require('jquery');
会载入 http://example.com/js/3-party/jquery.js 。
alias能够对较长的经常使用路径设置缩写。
charset表示下载js时script标签的charset属性。
timeout表示下载文件的最大时长,以毫秒为单位。
debug表示是否工做在调试模式下。
要将现有JS库如jQuery与SeaJS一块儿使用,只需根据SeaJS的的模块定义规则对现有库进行一个封装。例如,下面是对jQuery的封装方法:
define(function() { //{{{jQuery原有代码开始/*! * jQuery JavaScript Library v1.6.1 * http://jquery.com/ * * Copyright 2011, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * Includes Sizzle.js * http://sizzlejs.com/ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * * Date: Thu May 12 15:04:36 2011 -0400 *///...//}}}jQuery原有代码结束 return $.noConflict();});
SeaJS原本集成了一个打包部署工具spm,后来做者为了更KISS一点,将spm拆出了SeaJS而成为了一个单独的项目。spm的核心思想是将全部模块的代码都合并压缩后并入入口模块,因为SeaJS自己的特性,html不须要作任何改动就能够很方便的在开发环境和生产环境间切换。可是因为spm目前并无发布正式版本,因此本文不打算详细介绍,有兴趣的朋友能够参看其github项目主页 https://github.com/seajs/spm/。
其实,因为每一个项目所用的JS合并和压缩工具不尽相同,因此spm可能并非彻底适合每一个项目。在了解了SeaJS原理后,彻底能够本身写一个符合本身项目特征的合并打包脚本。
上文说了那么多,知识点比较分散,因此最后我打算用一个完整的SeaJS例子把这些知识点串起来,方便朋友们概括回顾。这个例子包含以下文件:
index.html——主页面。
sea.js——SeaJS脚本。
init.js——init模块,入口模块,依赖data、jquery、style三个模块。由主页面载入。
data.js——data模块,纯json数据模块,由init载入。
jquery.js——jquery模块,对 jQuery库的模块化封装,由init载入。
style.css——CSS样式表,做为style模块由init载入。
sea.js和jquery.js的代码属于库代码,就不赘述,这里只给出本身编写的文件的代码。
html:
<!DOCTYPE HTML><html lang="zh-CN"><head> <meta charset="UTF-8"> <title></title></head><body><div id="content"> <p class="author"></p> <p class="blog"><a href="#">Blog</a></p></div> <script src="./sea.js" data-main="./init"></script></body></html>
javascript:
//init.jsdefine(function(require, exports, module) { var $ = require('./jquery'); var data = require('./data'); var css = require('./style.css'); $('.author').html(data.author); $('.blog').attr('href', data.blog);}); //data.jsdefine({ author: 'ZhangYang', blog: 'http://blog.codinglabs.org'});
css:
.author{color:red;font-size:10pt;}.blog{font-size:10pt;}
运行效果以下: