爲什麼會想到用這個
最近寫webapp因爲需要引入很多不同的js插件,所以部分頁面甚至引用了10-15行js文件。。有些順序依賴很難維護。所以嘗試使用require.js實現模塊化,管理模塊依賴,減少頁面直接引用面積,實現按需加載。瞭解了一下相關的AMD規範,嘗試寫一個demo理解require.js的配置方式、使用方式。
關於AMD規範
簡單描述就是異步加載模塊,並在某個模塊加載前先加載當前模塊依賴的其他模塊。
AMD使用define定義模塊,require引入模塊。
詳細解釋可以參考https://www.jianshu.com/p/9b44a1fa8a96
舉個栗子:
define(['依賴模塊1','依賴模塊2'],function(){ return {模塊3對象}});
require(['模塊3對象'],function(){ do something });
前期準備工作
demo結構如圖
目前我有三個js文件,分別爲module_1.js/module_2.js/module_3.js ,在文件夾modules內
爲了測試用,所以每個js都只輸出log。 在test.html文件中引用這3個模塊
原有引用方式
普通的引入方式:
運行一下test.html
這樣的問題也很明顯,如果需要加載的js文件多,需要寫一堆引用。
如果依賴比較多,module2如果依賴module1,則必須在module2之前先引用module1. 引用的js一多,很難維護和管理。
使用require.js模塊加載方式
1.require.js介紹
RequireJS 其實就是一個JavaScript模塊加載器。對符合AMD規範的js文件實現模塊化加載。對不符合AMD規範的提供配置參數,依然可以作爲模塊引入。RequireJS是AMD規範的一個具體實現。
require.js下載地址:http://requirejs.org/docs/release/2.3.5/comments/require.js
2.require.js基本使用
require.js的使用方式是function (name, deps, callback) ,但是name參數大部分時候不需要單獨去定義,因爲name也是一個路徑的字符串,一旦變更js路徑,不僅僅是引用模塊處要變更,連define定義的地方也要把模塊名變更。所以可以省略掉name參數,這樣require.js會自動幫我們處理。
定義模塊:define(['依賴模塊1','依賴模塊2'],function(){ return {模塊3對象}});
引入模塊: require(['模塊3對象'],function(){ //do something });
(1)我們把之前的module1 module2修改成符合AMD規範的模塊格式
(2)在test.html引入require.js, 並按AMD規範引入module1,module2,調用其中的test函數。
(3) 在瀏覽器運行 test.html
在瀏覽器f12->sources 可以看到模塊module_1 module_2已經被加載進來了。
f12-console看一下 也成功調用了兩個模塊內的ready()函數
3.require.config
上邊直接使用require(),是直接引用模塊所在的路徑引入模塊的。存在一些問題
· 對於不符合AMD規範定義的對象直接引用會找不到模塊報錯
· 在實際應用中,如果項目路徑很長,可讀性賊差
· 在擴展方面,如果變更一個js路徑或者js文件名,模塊名因爲是直接引入路徑,也需要變更。
所以可以使用require.config配置解決上述問題.
require.config提供一個模塊id 對於js路徑的映射。然後引入的時候可以直接通過定義的模塊id進行引用。使模塊名字更符合定義,修改也可以直接修改config,不需要動require的引入代碼。可以通過“shim”配置描述,把不符合AMD規範定義的模塊作爲模塊導出。
module_3是一個沒有根據規範定義的匿名函數,只有一句輸出。
test.html,使用require.config在paths配置module_3的模塊名和路徑,shim配置module_3的描述信息和導出模塊名(具體解釋在代碼註釋中)。
//test.html
<script type="text/javascript" src="require.js"></script>
<script type="text/javascript">
require.config({
//baseUrl是一個統一的根路徑,如果沒有需要可以不配
//baseUrl:"",
//paths配置直接模塊名對應路徑即可
paths:{
"mod1":"modules/module_1",
"mod2":'modules/module_2',
"mod3":'modules/module_3'
},
//shim配置不符合AMD規範的模塊
shim:{
"mod3":{
deps:[],//依賴的模塊
exports:'mod3'//導出的模塊名
}
}
});
require(['mod1', 'mod2','mod3'], function (mod1, mod2,mod3) {
//把module_1,module_2作爲參數引入進來,就是mod1和mod2
mod1.ready();
mod2.ready();
})
</script>
頁面執行
已經把module_3加載了。看一下console控制檯輸出
module_3的匿名函數也執行了。
4.data-main
在第3步中使用require.config完成對不符合規範的模塊加載,以及定義模塊名和路徑的映射。但是每個頁面都去配置一個require.config難免太過於麻煩。如果必須這樣的話,這個工作量甚至會讓我想放棄使用require.js。
很容易想到的解決方案是我們寫一個公共的配置文件config.js,來配置項目內所用到的模塊信息。
但是require是異步加載,如何保證先跑這個config.js呢? require.js提供了一個data-main,可以保證最先加載data-main指定的js文件。引入require.js的時候 加一個data-main="配置js的路徑"即可。
(1)創建config.js 。命名隨意,只要和data-main對得上就可以。
//config.js
(function(){
require.config({
//baseUrl是一個統一的根路徑,如果沒有需要可以不配
//baseUrl:"",
//paths配置直接模塊名對應路徑即可
paths:{
"mod1":"modules/module_1",
"mod2":'modules/module_2',
"mod3":'modules/module_3'
},
//shim配置不符合AMD規範的模塊
shim:{
"mod3":{
deps:[],//依賴的模塊
exports:'mod3'//導出的模塊名
}
}
});
}());
(2) test.html頁面引入require.js時候添加data-main,指定主模塊。
<!-- data-main聲明主模塊路徑,先加載主模塊-->
<script type="text/javascript" src="require.js" data-main="config.js"></script>
<script type="text/javascript">
//最外層還是要先像普通引入模塊一樣,引入主模塊,加載配置
require(['config'], function () {
//然後再根據配置文件中定義的模塊,按需要引入。
require(['mod1', 'mod2', 'mod3'], function (mod1, mod2, mod3) {
mod1.ready();
mod2.ready();
})
});
</script>
(3)運行test.html
可以看到config.js和引用的3個模塊都加載進來了。
有一個困惑,就是不嵌套外層require(['config'])的話會報錯,也就是require.js是把主模塊作爲一個模塊輸出的,我想引用config內配置的模塊,就必須套一層引用config模塊。看起來有些彆扭。但是能力有限沒找到好的解決方案。如果有的話歡迎告訴一下。
總結
本文中展示的代碼demo在https://github.com/tennyxx/cx.require.demo
使用requirejs在本文的demo中感覺並不如直接引入方便,但是在具體的開發環境中可能有N多需要引入的東西,比如基於mui的webapp開發,需要先引入mui.js,然後一系列的時間組件、選擇插件等等依賴mui.js.這種就需要要求一定的順序,並且數量很多。 使用require.js很好地解決了此類問題。
自己有寫一個簡單的webapp的開發結構,各種公共的配置文件使用了require.js實現模塊化異步按需加載。有興趣可以參考歡迎提一些意見。https://github.com/tennyxx/cx.frame.webapp