RequireJS & SeaJS
在 模塊化開發(fā)
開發(fā)以前,都是直接在頁面上引入 script
標(biāo)簽來引用腳本的,當(dāng)項(xiàng)目變得比較復(fù)雜,就會(huì)帶來很多問題。
JS項(xiàng)目中的依賴只有通過引入JS的順序來確定,項(xiàng)目會(huì)變得復(fù)雜難以維護(hù)。
復(fù)雜的腳本會(huì)暴露很多全局變量, 比如 $,_. ... 。
同步加載的時(shí)候,網(wǎng)頁會(huì)停止渲染,加載時(shí)間越長,網(wǎng)頁失去響應(yīng)的事件就越長。
于是,AMD 規(guī)范就誕生了,AMD 即為異步模塊定義,有效避免同步加載導(dǎo)致頁面的假死現(xiàn)象。RequireJS 是一個(gè) AMD 的實(shí)現(xiàn),后來還有CMD規(guī)范,玉伯的 而SeaJS 是 CMD 的一個(gè)實(shí)現(xiàn),兩者最大的區(qū)別就是
RequireJS 是預(yù)加載,而sea.js 是懶加載,也就是按需加載
什么意思 ??
比如下面的代碼
define(function(require,exports,module){ // do something var mod1 = require('./mod1'); // do something var mod2 = require('./mod2');})
RequireJS 會(huì)全部找到這個(gè)模塊的依賴,并在開始執(zhí)行是就加載全部的依賴,
而SeaJS 則是按需加載,直到遇到 require 才會(huì)加載。
RequireJS 內(nèi)部通過
Function.prototype.toString()
,然后使用正則匹配所有的require 方法,將其轉(zhuǎn)化為
define(['./mod1',./mod2']function(mod1,mod2)
這種方式。
有很多關(guān)于這兩種方式的爭吵,由于我沒有做過較大的項(xiàng)目,所以對(duì)于這兩種方式在正真項(xiàng)目中使用的區(qū)別也不清楚。
回顧 RequireJS
主要接口有兩個(gè): require
& define
,define 是模塊的定義方法,require 是模塊的使用方法。
define 的參數(shù)為 define (id?,deps?,factory)
。第一個(gè)為模塊ID,第2個(gè)為依賴列表,第三個(gè)是工廠方法 。如果不定義ID,則為匿名方法,通常情況,模塊ID 等于模塊在工程的路徑。deps 和 factory 有約定,deps 數(shù)組有多少個(gè)元素,factory 就會(huì)有多少個(gè)形參,形參對(duì)于與依賴模塊工廠函數(shù)執(zhí)行后的返回值。
define ("id",["mod1","mod2"],function(mod1,mod2){ return { /// };})
require 方法和 define 情況一致,其實(shí)在內(nèi)部,define 會(huì)調(diào)用 require 來加載依賴模塊。
記住,真正將 script 節(jié)點(diǎn)插入到頁面中是 require 完成的 。
文件目錄
└─ use-require/ ├─ app/ │ ├─ js/ │ │ ├─ module/ │ │ │ ├─ a.js │ │ │ └─ b.js │ │ ├─ main.js │ └─ lib/ │ │ └─ require.js ├─ index.html └─ readme.md
現(xiàn)在,需要在 index.html 引導(dǎo)整個(gè)程序 。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><body> <script src="./app/lib/require.js" data-main="./app/js/main"> </script></body></html>
引入 requirejs,并通過 data-main
申明 啟動(dòng) js 文件。
注意: data-main 指向的文件所在路徑就是 baseUrl,在這里就是app/js ,
baseUrl 也可以通過require.config 設(shè)置 。如果沒有通過 data-main 屬性指定 baseUrl ,也沒有通過config的方式顯示聲明 baseUrl ,那么 baseUrl 默認(rèn)為加載requirejs的那個(gè)頁面所在的路徑
下面 b 依賴 a ,那么就應(yīng)該寫module/a
,而 main 依賴 b ,寫為module/b
,這個(gè)路徑會(huì)和 baseUrl 拼接,與a ,b 所在的路徑無關(guān) 。有時(shí)候,你可能需要生成一個(gè)相對(duì)于模塊的URL地址。你可以將require
作為一個(gè)依賴注入進(jìn)來,然后調(diào)用require.toUrl()
以生成該URL 。define(["require"], function(require) { var url = require.toUrl("./style.js"); });
有時(shí)候你想避開"baseUrl + paths"的解析過程,而是直接指定加載某一個(gè)目錄下的腳本。此時(shí)可以這樣做:如果一個(gè)module ID符合下述規(guī)則之一,其ID解析會(huì)避開常規(guī)的"baseUrl + paths"配置,而是直接將其加載為一個(gè)相對(duì)于當(dāng)前HTML文檔的腳本:
注意是 相對(duì)于 HTML 文檔,因?yàn)?js腳本是會(huì)被插入到 html 文檔中執(zhí)行的 。
以 ".js" 結(jié)束 。
以 "/" 開始。
包含 URL 協(xié)議, 如 "http:" or "https:"。
一般來說,最好還是使用baseUrl及"paths" config去設(shè)置module ID。它會(huì)給你帶來額外的靈活性,如便于腳本的重命名、重定位等。 同時(shí),為了避免凌亂的配置,最好不要使用多級(jí)嵌套的目錄層次來組織代碼,而是要么將所有的腳本都放置到baseUrl中,要么分置為項(xiàng)目庫/第三方庫的一個(gè)扁平結(jié)構(gòu),如下:
└─ project/ ├─ js/ │ ├─ app/ │ │ └─ sub.js │ ├─ lib/ │ │ ├─ jquery.js │ │ └─ require.js │ └─ app.js ├─ index.html └─ readme.md
下面自然就順利成章
申明模塊a
define(function() { 'use strict'; return { name: "hello , i am a" }})
模塊 b
define(["module/a"], function(a) { 'use strict'; var aName = a.name; var name = "hello i am b"; return { sayHello: function() { console.log(name + " my brother is " + aName); } }})
在模塊b 里面依賴模塊a 。
main.js
require(["module/b"], function(b) { 'use strict'; b.sayHello();})
看看效果
如果使用 require.config()
配置呢?
將上面的目錄復(fù)制一份,命名 use-require-config
,目錄結(jié)構(gòu)完全一致
main.js
requirejs.config({ baseUrl: './app/js/module'})require([".b"], function(b) { 'use strict'; b.sayHello();})
在這里,將配置放在 main.js 里面,等會(huì)兒會(huì)講如果不放在它里面會(huì)又什么問題。
模塊 b 依賴 于 a
define(["./a"], function(a) { 'use strict'; var aName = a.name; var name = "hello i am b"; return { sayHello: function() { console.log(name + " my brother is " + aName); } }})
由于將
baseUrl
設(shè)置為app/js/module
,所以這里依賴a
就可以直接寫a
在這里,我使用./a
, 與直接寫a
一模一樣,還是使用baseUrl+ moduleName
`來尋找模塊路徑。
如果不將 requirejs.config
放在main中,而是另外在引入一個(gè)script 節(jié)點(diǎn)來放置 呢
<body> <script src="./app/lib/require.js" data-main="./app/js/main"> </script> <script> requirejs.config({ baseUrl: './app/js/module' }) </script></body>
在瀏覽器運(yùn)行
發(fā)現(xiàn)報(bào)錯(cuò)了,怎么回事呢?
看它尋找 main.js 的路徑
file:///E:/HFLib/module/code/use-require-config/app/js/module/main.js
是 baseUrl + main.js。 這說明,配置的 baseUrl 覆蓋了為 data-main 配置的路徑
也就是說,一旦使用requirejs.config 來配置 baseUrl,那么所有的路徑都會(huì)以 baseUrl 為基準(zhǔn)。
所以,data-main 應(yīng)改為: ..\main
,注意這里必須要使用反斜杠,正斜杠拼接總是會(huì)出現(xiàn)問題。
通過這樣就可以解決 data-main 和 requirejs.config() 的沖突了 。
paths 和 shim 。
當(dāng)模塊名過長是,require.js 為我們提供了路徑 paths 的方式 。
理想狀況下,每個(gè)加載的腳本都是通過define()來定義的一個(gè)模塊;但有些"瀏覽器全局變量注入"型的傳統(tǒng)/遺留庫并沒有使用define()來定義它們的依賴關(guān)系,你必須為此使用shim config來指明它們的依賴關(guān)系。
這是我的文件目錄
index.html
<body> <script src="./app/lib/require.js" data-main="./app/main"> </script></body>
main.js
requirejs.config({ paths: { jQuery: 'lib/jQuery' }, shim: { 'jQuery': { exports: '$' } }})require(['jQuery'], function($) { 'use strict'; console.log($);})
可以看到,已經(jīng)順利注入了 jQuery 依賴。
require.js 中還有很多配置和用法,我用的就比較少,就簡單介紹到這里。
下來,我要實(shí)現(xiàn)一個(gè)簡化的模塊加載器,類似于 require.js.
實(shí)現(xiàn)一個(gè)自己的模塊加載器
太晚了 。
就在 實(shí)現(xiàn)一個(gè)自己的模塊加載器(二)中實(shí)現(xiàn)一個(gè)類似 require.js 的Demo 吧 。