一步步學會使用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

相關文章
相關標籤/搜索