最近發現require火了一段時間了,然而好久沒有接觸前端框架了。以下本人學習筆記和實戰,參考了多篇文章,裏面可能有錯誤或不準確,請批評指正^_^。
requirejs存在的意義:保證js加載順序
RequireJS 入口
1.data-main屬性
當你下載RequireJS之後,你要做的第一件事情就是理解RequireJS是怎麼開始工作的。當RequireJS被加載的時候,它會使用data-main屬性去搜尋一個腳本文件(它應該是與使用src加載RequireJS是相同的腳本)。data-main需要給所有的腳本文件設置一個根路徑。根據這個根路徑,RequireJS將會去加載所有相關的模塊。下面的腳本是一個使用data-main例子:
<script src="./require/require.js" data-main="main"></script>
如果是js文件,.js後綴名在配置時一般都可以省略。data-main指的是當前文件下的main.js(這個路徑表明main.js和html頁面在同級目錄)。mainjs配置了所有的需要使用的js腳本,代碼如下:
require.config({
baseUrl: '/requireLearning/script/',
paths: {
jquery:'jquery/jquery',
highchart: 'highchart/js/highcharts'
},
shim: {
highchart: {
deps: ['jquery']
}
}
})
require([
'app'
], function (app) {
});
上面,經測試requirejs.config與require.config效果是一樣的。
Config設置說明
● baseUrl——用於加載模塊的根路徑。
● paths——左側爲別名,在define聲明依賴包時會引用到。右側爲相對與根路徑的相對路徑,瀏覽器requirejs會按照baseUrl + paths來加載腳本。
● shims——理解爲聲明哪個模塊需要依賴其他的模塊,這個例子中聲明highchart需要依賴jquery模塊。
● deps——加載依賴關係數組
baseUrl亦可通過RequireJS config手動設置。如果沒有顯式指定config及data-main,則默認的baseUrl爲包含RequireJS的那個HTML頁面的所屬目錄。
如果一個module ID(requirejs提倡一個模塊一個文件,一個moudule ID是指paths左側內容),如果符合下述規則之一,其ID解析會避開常規的”baseUrl + paths”配置,而是直接將其加載爲一個相對於當前HTML文檔的腳本:
● 以 “.js” 結束.
● 以 “/” 開始.
● 包含 URL 協議, 如 “http:” or “https:”.
shim和deps是什麼?
shim爲那些沒有使用define()來聲明依賴關係、設置模塊的”瀏覽器全局變量注入”型腳本做依賴和導出配置。比如Jquery,由於是異步加載,$和Jquery會依然導出,但是Jquery和它的插件加載順序不能保證,因此可以用shim保證依賴,保證加載順序;
(個人理解)一篇博客上說,jquery $不會被導出到全局裏面是錯誤的,jquery源碼有一句
..}(typeof window !== "undefined" ? window : this, function( window, noGlobal )..
...
// Expose jQuery and $ identifiers, even in
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
說明是會被導出的!只不過是加載順序不能保證~因此要保證jquery比它的插件先加載。
require config後,執行require函數,這是這個整個程序聲明我要加載app作爲程序入口(你可以自己加載多個,看需求)。
2.define全局函數的使用(定義模塊)
通常模塊定義函數會把前面的數組中的依賴模塊按順序做爲參數接收。例如,下面是一個簡單的模塊定義(app.js 緊接着上面的main.js):
define([
'highchart'
],function () {
console.log("test")
}
)
定義了一個模塊,模塊聲明highchart依賴,在加載app.js時會加載highchart,然後執行console.log函數
注意:在define 的function裏參數是否與difine依賴完全相對應是可選的。如果依賴項已經把自己的功能對象導出到全局,就不需要在function裏寫對應參數,寫在function裏的參數依賴項需要依賴的的模塊返回它的api,否則會調用出錯!highchart 路徑在main.js裏已經定義過。
你可能會看到一些define()中包含了一個模塊名稱作爲首個參數:
//Explicitly defines the "foo/title" module:
define("foo/title",
["my/cart", "my/inventory"],
function(cart, inventory) {
//Define foo/title object in here.
}
);
這些常由優化工具生成。你也可以自己顯式指定模塊名稱,但這使模塊更不具備移植性——就是說若你將文件移動到其他目錄下,你就得重命名。一般最好避免對模塊硬編碼,而是交給優化工具去生成。優化工具需要生成模塊名以將多個模塊打成一個包,加快到瀏覽器的載人速度。。
define()中的相對模塊名: 爲了可以在define()內部使用諸如require(“./relative/name”)的調用以正確解析相對名稱,記得將”require”本身作爲一個依賴注入到模塊中:
define(["require"], function(require) {
var mod = require("./relative/name");
});
經過測試上述的語法報錯,require is not a function
define(["require"], function() {
var mod = require("./relative/name");
});
此語法通過,requirejs裏把require API輸出到全局,require是一個全局函數。
requirejs要求一個模塊對應一個文件,但是在使用插件時會發現,插件文件裏把多個模塊都集成到一個js文件裏了。此時即使模塊返回了它的API,用define加載的整個文件也並不能獲得它的api對象。
舉個例子ACE一個代碼編輯器插件:在ace.js裏面定義了多個模塊例如:
define("ace/ext/error_marker",。。。。
define("ace/ace",["require",
那麼就不能以一個文件來表示一個模塊,因此加載完後,也不能根據module ID來調用某個模塊的api:
define([
'angular',
'ace'
], function (ace) {
var module = angular.module('controllers');
module.controller('contractDetailController', ['$scope', '$window',function ($scope,$window) {
s.editorDetail= ace.edit(el);
s.editorDetail.getSession().setMode("ace/mode/java");
}
]);
});
直接調用ace edit函數會出錯,因爲ace並不是ace的一個api。可以用兩種方式調用:
define([
'angular',
'ace'
], function () {
var module = angular.module('controllers');
module.controller('contractDetailController', ['$scope', '$window',function ($scope,$window) {
s.editorDetail=$window. ace.edit(el);
s.editorDetail.getSession().setMode("ace/mode/java");
}
]);
});
一種用require獲得模塊,此時並不破壞異步加載,ace.js 已經加載過 了:
define([
'angular',
'ace',
"require"
], function () {
var module = angular.module('controllers');
module.controller('contractDetailController', ['$scope', '$window',function ($scope,$window) {
s.editorDetail=require("ace/ace").edit(el);
s.editorDetail.getSession().setMode("ace/mode/java");
}
]);
});
3.使用require函數
在RequireJS中另外一個非常有用的函數是require函數。require函數用於加載模塊依賴但並不會創建一個模塊。例如:下面就是使用require定義了能夠使用jQuery的一個函數。
require(['jquery'], function ($) {
//jQuery was loaded and can be used now
});
循環依賴(摘)
I如果你定義了一個循環依賴(a依賴b,b同時依賴a),則在這種情形下當b的模塊函數被調用的時候,它會得到一個undefined的a。b可以在模塊已經定義好後用require()方法再獲取(記得將require作爲依賴注入進來):
//Inside b.js:
define(["require", "a"],
function(require, a) {
//"a" in this case will be null if a also asked for b,
//a circular dependency.
return function(title) {
return require("a").doSomething();
}
}
);
模塊(Modules)在它們自己的上下文中進行評估,並且擁有全局變量exports以供模塊使用。變量exports只是個普通的JavaScript對象(plain old JavaScript object),甚至你也可以往它上面附加內容,與我們上面展示的命名空間對象類似。爲了訪問某個模塊,你要調用全局函數require,並指明你請求的包的標示符(identifier for the package)。然後評估該模塊,並且無論返回什麼都會附加到exports上。此模塊將會緩存起來,以便後來的require函數調用來使用。
// calculator.js
exports.add = function(n1, n2) {
};
// app.js
var calculator = require('./calculator');
calculator.add(2, 2);
參考文章:
http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_definition.html
http://kb.cnblogs.com/page/132461/
https://www.oschina.net/translate/getting-started-with-the-requirejs-library
http://www.requirejs.cn/docs/api.html#data-main