一步步学会使用SeaJS 2.0

一步步学会使用SeaJS 2.0

本文分为如下8步,熟悉以后就可以熟练使用SeaJS,今后以后你的生活会变得更加轻松愉悦!
一、SeaJS是什么?
二、下载并检阅SeaJS
三、创建工程和各类目录
四、引入SeaJS库
五、编写本身的代码
六、引入本身的代码
七、压缩合并
八、总结展望


-------------------------------------------------- html


一、SeaJS是什么? 前端


你必定听过前端模块化开发吧?神马,你没听过?我只能说你out了……
你应该知道Java的import吧?神马,你又不知道?那你应该知道CSS中的import吧……
在这里我不想展开说前端模块化的含义和价值,由于这里有一篇好文( https://github.com/seajs/seajs/issues/547),详细说明了前端模块化。

我知道你看到那么长的文章确定会望而却步,也许你是但愿可以快速开始敲代码(程序员的通病……)。不要紧,若是实在读不下去,只要记住模块化要解决的问题便可:命名冲突、文件依赖关系。 node

这两个闹心的问题应该遇到过吧,若是没遇到过……我只能说你太牛X了 git

好了,前端模块化就扯到这里,写过前端的人应该基本都知道JavaScript自身是不支持模块化开发的,因此就有了SeaJS这款神器,为前端从业者提供了一个强大易用的模块化开发工具。




二、下载并检阅SeaJS 程序员


SeaJS如今已是2.0版本啦,到这里下载: https://github.com/seajs/seajs


解压后会看到下列目录: github


其中:
dist —— 压缩好的、用于浏览器端的SeaJS代码
docs —— 文档
src —— 源代码
package.json + Gruntfile.js —— Grunt构建工具所须要的文件,这个在第七步压缩合并会介绍到
其余目录或文件能够暂且无论




三、创建工程和各类目录 算法


准备工做已经完成,咱们终于能够开始进行开发啦!来,跟我走:
a. 创建工程
用你最喜欢的IDE创建工程,名字为HelloSeaJS
b. 准备各类目录
在这里把JavaScript、Image、CSS都放在统一的资源文件(assets)中,建好以后的目录以下:

(我使用了Sublime2.0,在这里强烈推荐) npm


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目录并行存放。 json


至此,工程和文件都已准备完成。




四、引入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

相关文章
相关标签/搜索