seajs是what?
先看段代碼:
1 var loder = {}; 2 var define = loder.define = function(id,deps,factory){ 3 loader[id] = factory; 4 };
上述代碼干了什么?這就是一個最簡單的加載器,但離實際應用還有很大差距,需要添加很多功能,seajs就是一個成熟的方案。
當一個頁面集合了越來越多的js,這些js由不同的小組維護而且js包含了眾多的組件,於是相同的函數可能命名沖突,為了解決命名沖突,大家可能會加上命名空間,記憶命名空間是一個負擔,另外各種組件之間的引用,造成了復雜的依賴的關系,如何管理好依賴文件的加載問題也是個煩惱的問題。
seajs很好地解決了命名空間,依賴等問題。簡單的說seajs是模塊加載器,它將命名空間、組件解釋為模塊。按照seajs規則編寫的代碼符合CMD規范,使用它有兩個好處:二是讓模塊定義變得簡單,二是讓模塊發布變得簡單。它是如何做到的這兩方面的?
- 首先模塊定義的方式,模塊源代碼中是這樣編寫模塊的define(factory),非常簡單,但是如果直接合並合並后多個define就會造成不知道如何引用具體的模塊,於是涉及到第二個部分,如何讓模塊發布過程變得簡單。
- 模塊提取過程是,將源代碼轉換成seajs能正確解釋的代碼,即define(id,deps,factory)需要借助於grunt-cmd-transport或spm-build工具。
讓這兩個方面變得簡單的秘訣在於seajs采用了路徑即模塊標識,於是在提取依賴過程中,根據模塊標識找到依賴模塊的文件將其合並,並且將模塊定義轉換為這種形式define(id,deps,factory)。上線后的代碼中define(id,deps,factory)的id和deps模塊標識用的是路徑,根據路徑即模塊標識,就可以找到相應的完整模塊標識。
模塊標識
seajs的模塊定義方式非常simple,主要的難點在於不同項目有不同的部署要求,因此會因為模塊標識理解不夠造成進入各種坑。下面詳細分析下模塊標識!
模塊標識命名規則:
- 一個模塊標識由斜線(
/
)分隔的多項組成。 - 每一項必須是小駝峰字符串、
.
或..
。 - 模塊標識可以不包含文件后綴名,比如
.js
。 - 模塊標識可以是 相對 或 頂級 標識。如果第一項是
.
或..
,則該模塊標識是相對標識
解釋一下其中的幾種概念:
- 相對路徑,以
.
或..開頭
頂級路徑,不以
.或..及
斜線(/
)開頭- 普通路徑,除相對和頂級路徑外的,比如/(根路徑)開頭的,
"http://"
、"https://"
、"file:///"
等協議標識開頭的 - 模塊命名空間是seajs所在文件的根路徑即所謂的base路徑,去除了
seajs/x.y.z
字串,也可以指定seajs.config({base:});
模塊依賴提取過程如何解析?
- 只提取相對標識
- 相對標識相對
require
所在模塊的標識來解析
上線后模塊標識解析規則?
- 頂級標識始終相對
base
基礎路徑解析。(頂級標識由字符串開頭) - 絕對路徑和根路徑,即普通路徑,始終相對當前頁面解析,跟我們平時用的其他js和css路徑一樣,比如當前頁面是www.simple.com/user/index.html ,路徑為/js/hello.js,它解析后的地址為www.simple.com/js/hello.js。
模塊定義中require
和require.async
的相對路徑相對當前模塊路徑來解析。
如果我們能理解其模塊標識解析設計的出發點,那么就可以輕易的理解這些而不用記憶這么多:
- 關注度分離。書寫模塊的時候我們是不用指定模塊id的,require的模塊時候只要填入依賴模塊的相對路徑,於是我們只要關注代碼的書寫而不是依賴,打包后工具會自動幫我們處理好模塊id。
- 盡量與瀏覽器的解析規則一致。上線后在瀏覽器中的代碼,模塊路徑的解析規則應該於平時用的css、js這些加載路徑規則一樣,普通路徑和相對路徑的都是相對當前頁面的。
示例
目錄結構如下:
www
--app --blog index.html --sea-modules --seajs
--2.2.0
sea.js
--blog
--user
--1.0.0
main.js
--static
--user
--src
a.js
b.js
main.js
--dist
main.js
package.json
Makefile
// /www/static/user/src/a.js define(function(require,exports,module){ module.exports = function(){ // .......................... }; });
// /www/static/user/src/b.js define(function(require,exports,module){ module.exports = function(){ // .......................... }; });
// /www/static/user/src/main.js define(function(require,exports,module){
var a = require('./a"); var b = require('./b");
// ..............
});
// /www/static/user/package.json { family:"blog", name:"user", version:"1.0.0", spm:{ output:["main.js"] } }
// /www/static/user/Makefile
build: @spm build deploy: @rm -rf ../../sea-modules/blog/user @mkdir ../../sea-modules/blog/user @mkdir ../../sea-modules/blog/user/1.0.0 @cp dist/*.* ../../sea-modules/blog/user/1.0.0 @echo @echo " deploy to seajs-modules/blog/user/1.0.0" @echo
使用spm-build構建項目:
cd /www/static/user ; spm-build --encoding gbk
會在/www/static/user/dist 下生成 main.js 和 main-debug.js,main.js大概是這樣的
// /www/static/user/dist/main.js
define("blog/user/1.0.0/main",["./a","./b"],function(require){ var a = require('./a"); var b = require('./b"); }); define("blog/user/1.0.0/a",[],function(require,exports,module){ // ....................... }); define("blog/user/1.0.0/b",[],function(require,exports,module){ // ....................... });
然后運行make deploy
會將 ../dist/main 部署到 /www/sea-modules/blog/user/1.0.0/main.js
在頁面中如何加載模塊呢?
<!-- www.expample.com/app/blog/index.html --> <script src="/sea-modules/seajs/2.4.0/sea.js" id="seajson"></script> <script> seajs.config({charset:"gbk"}); seajs.use("blog/user/1.0.0/main"); </script>