requireJS的基本用法
引言
目前專案中的前端架構採用的是RequireJS+AngularJS,小編在工作之餘對這個前端框架比較感興趣,在開始的
時候對這個框架不是很懂,因為裡面有很多平臺自己封裝的東西,所以在理解起來不是很容易,經過這一段時間在項
目中的運用有了一定的理解,下面先來總結總結一下其中的一個技術點——RequireJS的基本用法。
基本介紹
RequireJS是一個JavaScript檔案或者模組的載入器。它可以提高JavaScript檔案的載入速度,避免不必要的
堵塞。它針對於在瀏覽器環境中使用做過專門的優化,但它也可以在其他的JavaScript環境中使用,像Node.js一樣
可以在伺服器上執行。說了這麼多廢話,其實它為我們提供了兩種思路解決大量載入js檔案帶來的問題:
1、模組化組織JS
2、非同步載入js檔案
為什麼需要用RequireJS?
當我們業務比較負載時候我們有時候需要在一個頁面中引入大量的js檔案,之前的ITOO中會見到一個頁面引入
了很多的js檔案,原先我們的寫法是這樣的:
<script src="js/app/a.js"></script> <script src="js/app/b.js"></script> <script src="js/app/c.js"></script>
這樣寫的有很多的缺點:
1、頁面在載入的時候從上往下開始載入和渲染的,當頁面有很多分散的JS檔案的時候,頁面會先載入和解析
頭部的js檔案(同步載入),此時頁面渲染就被堵塞了,如果這些js檔案請求的數量比較多,那麼網頁失去響應的時
間就會加長。
2、如果js檔案之間是有相互依賴關係的,那麼我們的js檔案引入的順序需要我們一定要注意,依賴性大的檔案
一定在最後引入,但是當依賴關係非常複雜的時候,程式碼的編寫和維護就非常複雜了。
然上面引入JS時候,對於第1點:首先:我們可以放在底部去載入,把所有JS放在</body>之前去,這樣就會解決
了遊覽器堵塞的問題,其次我們可以把所有的JS檔案打包成一個JS檔案,但是依賴性(也就是順序)我們還是沒有辦法
解決掉,所以我們引入了requireJS(優點:1、實現JS檔案的非同步載入,避免網頁被堵塞。2、管理模組之間的依賴
性,便於程式碼的編寫和維護。)。
requireJS的基本語法及用法
1、在官網上下載requireJS包,在我們頁面的頭部引入require.js檔案,基本用法如下:
<head>
<meta charset="UTF-8">
<title>RequirJS測試</title>
<script src="RequireJS/require.js" defer async="true" data-main="RequireJS/app.js"></script>
</head>
sync屬性表明檔案需要非同步載入,IE不支援這個屬性,只支援defer,所以上面把這2個屬性都加上。接下來,看
看requireJS啟動載入指令碼的初始化方式,requireJS支援屬性 data-main 這個屬性來載入初始化的JS檔案,其中
app.js檔案也即是我們的入口(主)檔案,如果我們的app.js檔案的內容為空的話,載入順序如下:
定義模組檔案
首先看一個Demo的目錄結構(webstorm開發):
RequireJS編寫模組不同於其他指令碼檔案,它良好的使用define來定義一個作用域避免全域性空間汙染,它可以顯
示出其依賴關係,並以函式(定義此模組的那個函式)引數的形式將這些依賴進行注入。大家如果對JS中的閉包比較熟
悉的話,這個東西非常的好理解,因為在我看來它就是解決js閉包解決的問題。
b.js中我們寫一個簡單例子:
/**
* Created by zhenghao on 2016/7/4.
*/
define(function(){
var add = function(x,y) {
return x + y;
};
return {
add : add
}
});
在入口檔案app.js中寫入一下內容:
/**
* Created by zhenghao on 2016/7/4.
*/
require(['app/b'], function (m){
console.log(m.add(1,3));
});
我們會在瀏覽器的console介面看到輸入4,說明我們呼叫成功了,我們來看一下載入順序:
當然我們也可以編寫簡單的鍵值對直接返回一個物件,從而解決全域性變數的問題,如下:
a.js檔案的內容:
/**
* Created by zhenghao on 2016/7/4.
*/
define(function () {
return {
color: "black",
size: "unisize"
}
});
app.js初始化檔案:
require(['app/a'], function (m){
console.log(m);
});
執行結果:
直接返回一個物件,通過使用上面的方法我們可以想到可以解決全域性變數概念,比如全域性變數全部使用define函式
包圍,什麼時候需要全域性變數的話,直接require([‘XX’],function(XX){})這樣呼叫下,同時所有的JS都是非同步
的,並不會堵塞載入。
requireJS配置項介紹
1、baseUrl:指定本地模組的基準目錄,即本地模組的路徑是相對於那個目錄的,該屬性通常有requireJS載入
時的data-main屬性指定。比如如下程式碼
<script src="RequireJS/require.js" defer async="true" data-main="RequireJS/app.js"></script>
app.js檔案的內容:
requirejs.config({
baseUrl: 'RequireJS/app'
});
requirejs(['a','b','c'],function(a,b,c){
});
我們在瀏覽器中看到如下請求路徑:
如上可以看到,View.html和RequireJS是同一個目錄下的,都是放在requireJS資料夾裡面的,所以定義
baseUrl:’RequireJS/app’ 會自動解析成 requireJSService/RequireJS/app/ 所以
requirejs([‘a’,’b’,’c’])的話,會自動到requireJSService/RequireJS/app/目錄下去查詢a.js,b.js,
c.js.找到了就可以加載出來.
如果未顯示設定baseUrl,則預設值是載入require.js的html所處的位置,如果使用了data-main屬性的話,則該
路徑變成了baseUrl.如下程式碼:
<span style="font-size:18px;"><script src="RequireJS/require.js" defer async="true" data-main="RequireJS/app.js"></script></span>
app.js檔案的內容:
requirejs(['a','b','c'],function(a,b,c){
});
如上顯示:預設情況下是從data-main檔案入口去載入RequireJS/app.js程式碼的,但是現在app.js中並沒有設定
config配置項,所以使用requirejs([‘a’,’b’,’c’],function(a,b,c))的時候會繼續載入RequireJS下面的
a.js,b.js,c.js,如果找到就載入,沒有找到就顯示404 not found,如上所示。
2、path:paths是對映那些不直接放在baseUrl指定的目錄下的檔案,設定paths的起始位置是相對於baseUrl
的,除非該path設定是以”/”開頭或含有URL協議(http://或者https://).
app.js檔案的內容如下:
requirejs.config({
baseUrl: 'RequireJS/lib',
paths: {
app: '../app'
}
});
requirejs(['app/a'],function(a){
});
可以看到paths是相對於baseUrl配置項生成的,baseUrl:’RequireJS/lib’下的所有js檔案,但是paths下的
app:’../app’是相對於js/lib下設定的,’..’的解析到js目錄下,然後就解析成RequireJS/app下,再
require([‘app/a’]),就解析到RequireJS/app/a.js了。
當我們把baseUrl註釋掉的話,a.js可定就找不到了,此時載入的路徑如下:
requirejs.config({
//baseUrl: 'RequireJS/lib',
paths: {
app: '../app'
}
});
requirejs(['app/a'],function(a){
})
3、skim引數:理論上,require.js載入的模組,必須是按照AMD規範、用define()函式定義的模組。但是實際
上,雖然已經有一部分流行的函式庫(比如jQuery)符合AMD規範,更多的庫並不符合,而skim解決了使用非AMD方式
定義的模組(如jquery外掛)及其載入順序,為那些沒有使用define()來宣告依賴關係,設定模組的”瀏覽器全域性變數
注入”型指令碼做依賴和匯出配置。
app.js中的程式碼如下:
require.config({
baseUrl: 'RequireJS/lib',
paths: {
'app': '../app',
'temp':'../app/depBase',
'jquery':'../lib/jquery-1.7.1',
'a':'../app/a'
},
shim: {
'temp': { exports:'_',deps: ['jquery'] }
}
});
require(['temp','a'],function(base){
console.log(base);
});
檔案載入順序及載入結果
require.config()接受一個配置物件,這個物件除了有前面說過的paths屬性之外,還有一個shim屬性,專門用
來配置不相容的模組。具體來說,每個模組要定義(1)exports值(輸出的變數名),表明這個模組外部呼叫時的名
稱;(2)deps陣列,表明該模組的依賴性。
4、map引數:對於給定的模組字首,使用一個不同的模組ID來載入該模組。這個手段在大型專案中是非常重要
的,比如我們不同的模組需要載入不同版本的jquery檔案,這就需要我們用到這個map來給我們解決了,比如我們
a.js檔案需要依賴jquery-1.7.1,而我們的b.js檔案需要依賴jquery-1.8.0。請看下面例子:
app.js檔案中的程式碼:
requirejs.config({
map: {
'app/a': {
'jquery': 'RequireJS/lib/jquery-1.7.1.js'
},
'app/b': {
'jquery': 'RequireJS/lib/jquery-1.8.0.min.js'
}
}
});
/*require(['app/a'],function(jq){
});*/
require(['app/b'],function(jq){
});
a.js檔案和b.js檔案中的內容如下:
define(function (require, exports, module) {
var a = require(['jquery']);
});
define(function (require, exports, module) {
var b = require(['jquery']);
});
我們只加載b.js檔案的時候的檔案載入結果:
如果我們只加載a.js檔案,那麼我們會看到載入的是jquery-1.7.1.js檔案。
5.config引數:這個和我們c#中的配置檔案一樣,我們一般將易變的或者application級別的資訊配置在
config裡面,同樣這個東西在requireJS中也是支援的,但是這就需要我們提供一種手段將裡面的配置的資訊傳遞到
每一個模組中,基於requirejs.config()的config配置項來實現。要獲取這些資訊的模組可以載入特殊的依
賴 ”moudle” ,並呼叫module.config().
app.js檔案中的程式碼:
requirejs.config({
config: {
'app/c': {
name: '鄭浩'
},
'app/d': {
age: 18
}
}
});
require(['app/c'],function(c){
console.log(c);
});
require(['app/d'],function(d){
console.log(d);
});
c.js檔案和d.js檔案中的程式碼如下:
define(function (require, exports, module) {
//其值是'鄭浩'
var size = module.config().name;
return size;
});
<span style="font-family:SimSun;font-size:18px;">define(['module'], function (module) {
var color = module.config().age;
return color;
});</span>
最後贈送
其實require和我們之前過的ajax或者$http有類似地方就是在回撥函式上面,當我們在加載出錯的時候會走第二個
回撥函式,程式碼如下:
require(['b'], function ($) {
//Do something with $ here
}, function (err) {
alert(err)
});
小結
以上就是小編對requireJS的一些理解和總結,其中的例子都是對現在專案中一個縮影,上面講解的一些屬性和
引數都在我們專案中出現過,也是我們在使用requireJS經常用到的一些基本知識,裡面有一些東西還不是很明白,
需要在專案中繼續學習和研究,希望對讀者有些幫助