JavaScript模塊化之使用requireJS按需加載
模塊加載器的概念可能稍微接觸過(guò)前端開(kāi)發(fā)的童鞋都不會(huì)陌生,通過(guò)模塊加載器可以有效的解決這些問(wèn)題:
- JS文件的依賴關(guān)系。
- 通過(guò)異步加載優(yōu)化script標(biāo)簽引起的阻塞問(wèn)題
- 可以簡(jiǎn)單的以文件為單位將功能模塊化并實(shí)現(xiàn)復(fù)用
主流的JS模塊加載器有requireJS,SeaJS等,加載器之間可能會(huì)因?yàn)樽裱囊?guī)范不同有微妙的差別,從純用戶的角度出發(fā),之所以選requireJS而不是SeaJS主要是因?yàn)椋?/p>
功能實(shí)現(xiàn)上兩者相差無(wú)幾,沒(méi)有明顯的性能差異或重大問(wèn)題。
文檔豐富程度上,requireJS遠(yuǎn)遠(yuǎn)好于SeaJS,就拿最簡(jiǎn)單的加載jQuery和jQuery插件這回事,雖然兩者的實(shí)現(xiàn)方法相差無(wú)幾,但requireJS就有可以直接拿來(lái)用的Demo,SeaJS還要讀文檔自己慢慢折騰。一些問(wèn)題的解決上,requireJS為關(guān)鍵詞也更容易找到答案。
requireJS 加載jQuery + jQuery插件
可能對(duì)于一般Web App來(lái)說(shuō),引入jQuery及相關(guān)插件的概率是最大的,requireJS也親切的給出了相應(yīng)的解決方案及動(dòng)態(tài)加載jQuery及插件的文檔及實(shí)例代碼。
在最新的jQuery1.9.X中,jQuery已經(jīng)在最后直接將自己注冊(cè)為一個(gè)AMD模塊,即是說(shuō)可以直接被requireJS作為模塊加載。如果是加載舊版的jQuery有兩種方法:
1. 讓jQuery先于requireJS加載
2. 對(duì)jQuery代碼稍做一點(diǎn)處理,在jQuery代碼包裹一句:
define(["jquery"], function($) { // $ is guaranteed to be jQuery now */ });
requireJS的示例中,直接將requireJS與jQuery合并為一個(gè)文件,如果是采用jQuery作為核心庫(kù)的話推薦這種做法。
同樣對(duì)于jQuery插件來(lái)說(shuō)也有兩種方法
1. 在插件外包裹代碼
define(["jquery"], function($){ // Put here the plugin code. });
2. 在使用reuqireJS代碼加載前注冊(cè)插件(比如在main.js)中
requirejs.config({ "shim": { "jquery-cookie" : ["jquery"] } });
requireJS加載第三方類庫(kù)
在實(shí)例的App中還用到了jQuery以外的第三方類庫(kù),如果類庫(kù)不是一個(gè)標(biāo)準(zhǔn)的AMD模塊而又不想更改這些類庫(kù)的代碼,同樣需要提前進(jìn)行定義:
require.config({ paths: { 'underscore': 'vendor/underscore' }, shim: { underscore: { exports: '_' } } });
CSS文件的模塊化處理
在requireJS中,模塊的概念僅限于JS文件,如果需要加載圖片、JSON等非JS文件,requireJS實(shí)現(xiàn)了一系列加載插件。
但是遺憾的是requireJS官方?jīng)]有對(duì)CSS進(jìn)行模塊化處理,而我們?cè)趯?shí)際項(xiàng)目中卻往往能遇到一些場(chǎng)景,比如一個(gè)輪播的圖片展示欄,比如高級(jí)編輯器等等。幾乎所有的富UI組件都會(huì)由JS與CSS兩部分構(gòu)成,而CSS之間也存在著模塊的概念以及依賴關(guān)系。
為了更好的與requireJS整合,這里采用require-css來(lái)解決CSS的模塊化與依賴問(wèn)題。
require-css是一個(gè)requireJS插件,下載后將css.js與normalize.js放于main.js同級(jí)即可默認(rèn)被加載,比如在我們的項(xiàng)目中需要加載jQuery Mobile的css文件,那么可以直接這樣調(diào)用:
require(['jquery', 'css!../css/jquery.mobile-1.3.0.min.css'], function($) { });
不過(guò)由于這個(gè)CSS本質(zhì)上是屬于jQuery Mobile模塊的一部分,更好的做法是將這個(gè)CSS文件的定義放在jQuery Mobile的依賴關(guān)系中,最終我們的requireJS定義部分為:
require.config({ paths: { 'jquerymobile': 'vendor/jquery.mobile-1.3.0', 'jstorage' : 'vendor/jstorage', 'underscore': 'vendor/underscore' }, shim: { jquerymobile : { deps: [ 'css!../css/jquery.mobile-1.3.0.min.css' ] }, underscore: { exports: '_' } } });
在使用模塊時(shí),只需要:
require(['jquery', 'underscore', 'jquerymobile', 'jstorage'], function($, _) { });
jQuery Mobile的CSS文件就會(huì)被自動(dòng)加載,這樣CSS與JS就被整合為一個(gè)模塊了。同理其他有復(fù)雜依賴關(guān)系的模塊也可以做類似處理,requireJS會(huì)解決依賴關(guān)系的邏輯。
數(shù)據(jù)源的加載與等待
Web App一般都會(huì)動(dòng)態(tài)加載后端的數(shù)據(jù),數(shù)據(jù)格式一般可以是JSON、JSONP也可以直接是一個(gè)JS變量。這里以JS變量為例:
var restaurants = [ { "name": "KFC" }, { "name": "7-11" }, { "name": "成都小吃" } ]
載入這段數(shù)據(jù):
$.getScript('data/restaurants.json', function(e){ var data = window.restaurants; alert(data[0].name); //KFC });
單一的數(shù)據(jù)源確實(shí)很簡(jiǎn)單,但是往往一個(gè)應(yīng)用中會(huì)有多個(gè)數(shù)據(jù)源,比如在這個(gè)實(shí)例App中UI就需要載入用戶信息、餐廳信息、訂餐信息三種數(shù)據(jù)后才能工作。如果僅僅靠多層嵌套回調(diào)函數(shù)的話,可能代碼的耦合就非常重了。
為了解決多個(gè)數(shù)據(jù)加載的問(wèn)題,我習(xí)慣的解決方法是構(gòu)造一個(gè)dataReady事件響應(yīng)機(jī)制。
var foodOrder = { //數(shù)據(jù)載入后要執(zhí)行的函數(shù)暫存在這里 dataReadyFunc : [] //數(shù)據(jù)源URL及載入狀態(tài) , dataSource : [ { url : 'data/restaurants.json', ready : false, data : null }, { url : 'data/users.json', ready : false, data : null }, { url : 'data/foods.json', ready : false, data : null } ] //檢查數(shù)據(jù)源是否全部載入完畢 , isReady : function(){ var isReady = true; for(var key in this.dataSource){ if(this.dataSource[key].ready !== true){ isReady = false; } } return isReady; } //數(shù)據(jù)源全部加載完畢,則逐一運(yùn)行dataReadyFunc中存放的函數(shù) , callReady : function(){ if(true === this.isReady()){ for(var key in this.dataReadyFunc){ this.dataReadyFunc[key](); } } } //供外部調(diào)用,會(huì)將外部輸入的函數(shù)暫存在dataReadyFunc中 , dataReady : function(func){ if (typeof func !== 'function') { return false; } this.dataReadyFunc.push(func); } , init : function(){ var self = this; var _initElement = function(key, url){ $.getScript(url, function(e){ //每次載入數(shù)據(jù)后,將數(shù)據(jù)存放于dataSource中,將ready狀態(tài)置為true,并調(diào)用callReady self.dataSource[key].data = window[key]; self.dataSource[key].ready = true; self.callReady(); }); } for(var key in this.dataSource){ _initElement(key, this.dataSource[key].url); } } }
用法為:
foodOrder.dataReady(function(){ alert(1); }); foodOrder.init();
dataReady內(nèi)的alert將會(huì)在所有數(shù)據(jù)載入完畢后開(kāi)始執(zhí)行。
這段處理的邏輯并不復(fù)雜,將所有要執(zhí)行的方法通過(guò)dataReady暫存起來(lái),等待數(shù)據(jù)全部加載完畢后再執(zhí)行,更加復(fù)雜的場(chǎng)景此方法仍然通用。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
js實(shí)現(xiàn)電燈開(kāi)關(guān)效果
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)電燈開(kāi)關(guān)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01ES6中Array.copyWithin()函數(shù)的用法實(shí)例詳解
ES6為Array增加了copyWithin函數(shù),用于操作當(dāng)前數(shù)組自身,用來(lái)把某些個(gè)位置的元素復(fù)制并覆蓋到其他位置上去。下面重點(diǎn)給大家介紹ES6中Array.copyWithin()函數(shù)的用法,需要的朋友參考下2017-09-09JS基于設(shè)計(jì)模式中的單例模式(Singleton)實(shí)現(xiàn)封裝對(duì)數(shù)據(jù)增刪改查功能
這篇文章主要介紹了JS基于設(shè)計(jì)模式中的單例模式(Singleton)實(shí)現(xiàn)封裝對(duì)數(shù)據(jù)增刪改查功能.結(jié)合實(shí)例形式分析了javascript基于單例模式結(jié)合ajax針對(duì)數(shù)據(jù)庫(kù)進(jìn)行增刪改查的相關(guān)操作技巧,需要的朋友可以參考下2018-02-02