下載及安裝
要在專案中使用SeaJS,你所有需要做的準備工作就是下載sea.js然後放到你專案的某個位置。
SeaJS專案目前託管在GitHub上,首頁為 https://github.com/seajs/seajs/ 。可以到其git庫的build目錄下載sea.js(已壓縮)或sea-debug.js(未壓縮)。
下載完成後放到專案的對應位置,然後在頁面中透過<script>標籤引入,你就可以使用SeaJS了。 </script>
SeaJS基本開發原則
在討論SeaJS的具體使用前,先介紹SeaJS的模組化理念和開發原則。
使用SeaJS開發JavaScript的基本原則就是:一切皆為模組。引入SeaJS後,編寫JavaScript程式碼就變成了編寫一個又一個模組,SeaJS中模組的概念有點類似於物件導向中的類別——模組可以擁有資料和方法,資料和方法可以定義為公共或私有,公共數據和方法可以供別的模組呼叫。
另外,每個模組應該都定義在一個單獨js檔案中,也就是一個對應一個模組。
下面介紹模組的編寫和呼叫。
模組的定義與編寫
模組定義函數define
SeaJS中使用「define」函數定義一個模組。因為SeaJS的文檔並沒有關於define的完整參考,所以我閱讀了SeaJS原始碼,發現define可以接收三個參數:
/**
* 定義一個模組。
* @param {string=} id 模組 ID。
* @param {Array.|string=} deps 模組相依性。
* @param {function( )|Object}factory 模組工廠函數。
*/
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載入即可。
工廠函數factory解析
工廠函數是模組的主體和重點。在只傳遞一個參數給define時(推薦寫法),這個參數就是工廠函數,此時工廠函數的三個參數分別是:
1.require-模組載入函數,用來記載依賴模組。
2.exports-介面點,將資料或方法定義在其上則將其暴露給外部呼叫。
3.module——模組的元資料。
這三個參數可以根據需要選擇是否需要顯示指定。
下面說一下module。 module是一個對象,儲存了模組的元信息,具體如下:
1.module.id——模組的ID。
2.module.dependencies-一個陣列,儲存了此模組所依賴的所有模組的ID列表。
3.module.exports-與exports指向同一個物件。
三種編寫模組的模式
第一種定義模組的模式是基於exports的模式:
define(function(require, exports, module) {
var a = require('a'); //引入a模組
var b = require('b'); //引入a模組
var b = require('b'); //引入b模組
var data1 = 1; //私人資料
var func1 = function() { //私人方法
var func1 = function() { //私人方法
exports.data2 = 2; //公用資料
exports.func2 = function() { //公用方法
);
上面是一種比較「正宗」的模組定義模式。除了將公共資料和方法附加在exports上,也可以直接傳回一個物件表示模組,如下面的程式碼與上面的程式碼功能相同:
define(function(require) {
var a = require('a'); //引入a模組
var a = require('a'); //引入a模組
b'); //引入b模組
var data1 = 1; //私人資料
var func1 = function() { //私人方法
var func1 = function() { //私人方法
var func1 = function() { //私人方法 }
return {
data2: 2,
turn 'hello';
}
};
});
如果模組定義沒有其它程式碼,只回傳一個對象,還可以有以下簡化寫法:
define({
data: 1,
func: function() {
第三種方法對於定義純JSON資料的模組非常合適。
模組的載入與引用
模組的尋址演算法
上文說過一個模組對應一個js文件,而載入模組時一般都是提供一個字串參數告訴載入函數需要的模組,所以就需要有一套從字串標識到實際模組所在檔案路徑的解析演算法。 SeaJS支援以下標識:
絕對位址-給出js檔案的絕對路徑。
如:
複製程式碼
相對位址-用相對呼叫載入函數所在js檔案的相對位址尋找模組。
例如在http://example/js/b.js 載入
複製程式碼
基址位址-如果載入字串識別既不是絕對路徑也不是以”./”開頭,則相對SeaJS全域配置中的「base」來尋址,這種方法稍後討論。
注意上面在載入模組時都不會使用傳遞後綴名“.js”,SeaJS會自動加上“.js”。但以下三種情況不會加入:
載入css時,如:
複製程式碼
複製程式碼
複製程式碼
程式碼如下:require("http://example/js/a.json#");
根據應用程式場景的不同,SeaJS提供了三個載入模組的API,分別是seajs.use,require和require.async,以下分別介紹。
seajs.use
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也可以改為如下寫法:
TinyApp
="content">
這種寫法會令html更簡潔。
require
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('>require('>require Module'.toLowerCase());這都會造成SeaJS無法進行正確的正規匹配以下載對應的js檔案。
require.async
上文說過SeaJS會在html頁面打開時透過靜態分析一次記載所有需要的js文件,如果想要某個js文件在用到時才下載,可以使用require.async:複製程式碼
程式碼如下:
require.async('/path/to/module/file', function(m) {
//code of callback...});這樣只有在用到這個模組時,對應的js檔案才會被下載,也就實現了JavaScript程式碼的按需加載。
SeaJS的全域設定
SeaJS提供了一個seajs.config方法可以設定全域配置,接收一個表示全域設定的設定物件。使用方法如下:複製程式碼
程式碼如下:
seajs.config({
'path/to/jslib/',
alias: {
'app': 'path/to/app/'
}, . : 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表示是否工作在偵錯模式下。
SeaJS如何與現有JS函式庫搭配使用
要將現有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
*
* http://jquery.org/license
*
> * http://sizzlejs.com/
* Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
May *
* Date: Thu 1
May *
* Date: Thu 122 04:36 2011 -0400
*/
//...
//}}}jQuery原有程式碼結束
return $.noConflict();}) ;
SeaJS專案的打包部署
SeaJS本來整合了一個打包部署工具spm,後來作者為了更KISS一點,將spm拆出了SeaJS而成為了一個單獨的專案。 spm的核心思想是將所有模組的程式碼合併壓縮後併入入口模組,由於SeaJS本身的特性,html不需要做任何改動就可以很方便的在開發環境和生產環境間切換。但由於spm目前並沒有發布正式版本,所以本文不打算詳細介紹,有興趣的朋友可以參考其github專案首頁 https://github.com/seajs/spm/。
其實,由於每個專案所使用的JS合併和壓縮工具不盡相同,所以spm可能並不是完全適合每個專案。在了解了SeaJS原理後,完全可以自己寫一個符合自己專案特徵的合併打包腳本。