前言:在慕課網上跟着視頻《側邊工具欄開發》作了一遍,用到了jquery操做DOM,其中,用requirejs管理模塊依賴,而後自定義了兩個模塊它們都依賴jquery,而且其中一個自定義模塊依賴另外一個,因此要暴露出接口。看完視頻初步認識了一下requirejs,以及模塊化開發的概念,在此作一下總結。感謝慕課網上的老師。javascript
使用模塊化開發的好處:css
有效的防止命名衝突html
聲明不一樣的js文件之間的依賴java
可讓咱們寫出模塊化的代碼,便於複用jquery
這個視頻《側邊工具欄開發》的需求很簡單,就是作一個側邊工具條,windows
固定定位在頁面的某個位置,數組
在沒有把頁面向下滾動時,顯示三個按鈕,瀏覽器
當頁面向下滾動必定距離以後,第四個按鈕出現。微信
點擊這個按鈕,頁面會回到頂部。app
鼠標hover到每一個按鈕上都有一些相應的動畫(CSS3完成這裏不寫)
<div class="toolbar"> <!--第一個微信按鈕--> <a href="javascript:;" class="toolbar-item toolbar-item-weixin"> <span class="toolbar-layer"></span> </a> <!--第二個意見反饋按鈕--> <a href="javascript:;" class="toolbar-item toolbar-feedback"></a> <!--第三個app下載按鈕--> <a href="javascript:;" class="toolbar-item toolbar-item-app"> <span class="toolbar-layer"></span> </a> <!--第四個回到頂部按鈕--> <a id="backTop" href="javascript:;" class="toolbar-item toolbar-item-top"></a> </div>
一些說明:
爲了讓頁面顯示滾動條,須要在這個上述代碼下面加不少行<p></p>
標籤以便撐開頁面顯示滾動條。
CSS部分視頻中老師講了三種方法,而且用到了SASS,感興趣的同窗能夠去看一下,這裏再也不贅述。
其中jquery-3.1.0.js和require.js是在各自官網下載的資源文件。main.js是自定義的入口文件。
在未引入模塊化編寫代碼之前引入js文件是在<body>
標籤以前寫多個<script>
標籤,根據js文件加載的順序,添加js文件。如今根據requirejs
的異步加載的特性,能夠設定一個主入口文件,只用一個<script>
實現其他js文件的加載。
首先是引入requirejs:在HTML
文件的<body>
標籤的以前添加script標籤,
而後引入requirejs
文件,而後用data-main
這個屬性來引入入口文件。(當用requirejs引用文件時,能夠省略js文件的js後綴名,因此此處引入的就是項目目錄中的main.js
)
以下:
<script src="js/require.js" data-main="js/main"></script>
requirejs.config
(爲模塊指定別名,方便模塊的引入
),在入口文件中定義
如這個demo要引入屢次jquery-3.1.0.js
,可是這個名字很長因此能夠在入口文件main.js
中爲它定義一個別名,以下:
//爲jquery模塊定義別名 requirejs.config({ paths:{ jquery:'jquery-3.1.0.js' } });
requirejs()
方法(將寫好的模塊進行引入
)
requirejs()接收兩個參數,第一個參數是一個數組,寫入要引入的模塊的名字。第二個參數是一個回調函數,須要傳遞一個參數,來代替前面所引入的模塊。如:引入jquery模塊
requirejs(['jquery'],function($){ //寫一段代碼驗證jquery是否被正確引入 //將body背景顏色變爲紅色 $('body').css('background-color','red'); });
define()
(利用它定義編寫模塊,而後在相應的地方進行引入。
)define()
接收三個參數,第一個參數是爲本模塊命名的值,能夠不寫,第二個參數表示須要引入的模塊,第三個參數是各依賴項成功加載後所運行的函數,傳入的參數與各個依賴項造成對應的關係。
define( moduleName, //可選,若是此參數不寫,則默認使用本模塊所在文件的文件名 dependencies, //一個數組,此數組包含着此文件所需的各個依賴項目,這個數組中各項對應的是所依賴文件相對於requirejs庫所造成的相對路徑文件名。 function(parameters){ //各依賴項成功加載後所運行的函數 //傳入的參數與dependencies數組中的各個依賴項造成對應關係 } );
如今先對demo中的基本功能進行實現:
目前的目錄結構以下:
首先在HTML中初始化requirejs
:在</body>
標籤以前:<script src="js/require.js" data-main="js/main"></script>
在入口文件main.js中實現基本功能
//1.首先爲jquery模塊定義別名 requirejs.config({ paths: { jquery: 'jquery-3.1.0' } }); //2.而後用requirejs()方法引入jquery模塊實現demo中需求 requirejs(['jquery'],function($){ //爲id值爲backTop的第四個按鈕添加點擊回到頂部事件,當點擊時執行move函數回到頂部 $('#backTop').on('click',move); //監聽一下windows對象的滾動事件, //每次滾動都執行函數checkPosition肯定一下位置,是否到達設定的臨界點,以顯示和隱藏第四個按鈕 $(window).on('scroll',function(){ checkPosition($(window).height()); }); //解決bug:在刷新頁面時也出現第四個按鈕,即頁面加載時就檢查一下滾動位置 checkPosition($(window).height()); //------------------------------分割線------------------------------------------ //move函數的具體實現,加動畫效果 function move(){ $('html, body').animate({ scrollTop:0 },800); } //go函數能夠當即移動到頂部 function go(){ $('html, body').scrollTop(0); } //checkPosition函數的具體實現 function checkPosition(pos){ if($(window).scrollTop() > pos){ $('#backTop').fadeIn(); }else{ $('#backTop').fadeOut(); } } });
分割線以上是執行的代碼,分割線如下是寫的被調用的函數。
上述代碼雖然實現了功能,可是存在如下問題:
move和go函數都是到達頂部的功能,實現的功能很類似,做用若是想在其它地方使用這個功能,就要再進行代碼的複製,不方便功能的複用。因此應該將功能抽象成模塊。
實現功能的功能單一:兩個函數都是到達頂部,這樣即使抽象成模塊也會受到很大的限制,因此能夠進一步將問題抽象成移動滾動條到指定位置。
第一步:建立一個新模塊,用scrollto.js
表示,目前這個demo的目錄結構如圖:
第二步:將功能抽象成模塊,寫入scrollto.js
中
//1.先定義這個模塊,由於要用到jquery,因此還要引入jquery define(['jquery'],function($){ //定義構造函數 function ScrollTo(opts){ this.opts = $.extend({},ScrollTo.DEFAULTS,opts); //實現傳參覆蓋 this.$el = $('html, body'); } //原型添加方法 ScrollTo.prototype.move = function (){ var opts = this.opts; this.$el.animate({ scrollTop:opts.dest },opts.speed); }; ScrollTo.prototype.go = function(){ this.$el.scrollTop(opts.dest); }; //定義默認的參數 ScrollTo.DEFAULTS = { dest:0, speed:800 }; //定義接口 return { ScrollTo:ScrollTo }; });
代碼詳解:
傳遞的參數爲一個對象,用opts表示
用戶沒有傳遞參數時,使用默認的參數,默認參數直接寫在ScrollTo構造函數上,至關於造成一個靜態屬性,而後經過jquery的extend()方法進行原型的擴展
實現用戶傳遞參數用之,不傳遞參數用默認值。jquery的extend()
方法
在原型上添加move和go方法
第三步:在入口文件main.js中引入這個scrollto.js
的模塊
requirejs(['jquery','scrollto'], function($,scrollto){ //爲了使用scrollto模塊,須要實例化一下 var scroll = new scrollto.ScrollTo({ dest:0, speed:2000 }); //點擊回到頂部按鈕回到指定位置功能 $('#backTop').on('click', $.proxy(scroll.move, scroll)); });
上述代碼中有一點須要注意:
在第6行中,若是添加點擊按鈕回到指定位置事件時,這麼寫:$('#backTop').on('click', scroll.move)
;
此時瀏覽器控制檯會報錯:Uncaught TypeError: Cannot read property 'ScrollTo' of undefined
分析緣由是由於,在main.js中調用scrollto.js
模塊中在ScrollTo.prototype.move
原型方法move時,main.js中this指的是ScrollTo的實例,即scrollto,而在語句$('#backTop').on('click', scroll.move)
;中,這個this指代的是id爲backTop的這個按鈕。
解決辦法:用jquery提供的方法,直接將this指向scroll對象。$('#backTop').on('click', $.proxy(scroll.move, scroll))
第四步:一個bug
這時基本功能雖然實現了,點擊底部那個按鈕,傳入設定的返回位置和返回的速度,頁面能夠再次返回頂部指定位置,可是目前還存在一個bug:在點擊底部按鈕回到頂部指定位置時,假如連續屢次點擊這個按鈕,則頁面回到頂部後就沒法再次向下滾動頁面。
bug分析:
假如執行的函數如上面第三步中代碼,速度設置成較慢的速度2000,那麼在返回頂部指定位置時能夠屢次點擊這個按鈕,
這樣每次點擊按鈕事件都要調用move方法執行裏面的動畫,點擊多少次,這個動畫就要執行多少次。
所以在頁面返回頂部後,再次滾動頁面向下會當即執行返回頂部動畫,因此在執行完點擊次數的動畫以前,用戶都沒法向下滾動。(而且很是耗性能)
解決辦法,在滾動條正在運動或者已經到達目的地,就不該該執行動畫。添加判斷。
因此scrollto.js
的代碼能夠改爲以下:
define(['jquery'],function($){ //定義構造函數 function ScrollTo(opts){ this.opts = $.extend({},ScrollTo.DEFAULTS,opts); //實現傳參覆蓋 this.$el = $('html, body'); } //原型添加方法 ScrollTo.prototype.move = function (){ var opts = this.opts; if ($(window).scrollTop() != opts.dest){ //判斷是否到達指定位置 if(!this.$el.is(':animated')){ //判斷是否在運動 this.$el.animate({ scrollTop:opts.dest },opts.speed); } } }; ScrollTo.prototype.go = function(){ var dest = this.opts.dest; if($(window).scrollTop() != dest){ this.$el.scrollTop(dest); } }; //定義默認的參數 ScrollTo.DEFAULTS = { dest:0, speed:800 }; //定義接口 return { ScrollTo:ScrollTo }; });
咱們把返回的功能函數move和go都抽象在了scrollto.js
模塊中,如今還能夠直接把整個返回頂部的功能(包括滾動必定距離後隱藏的按鈕出現,和點擊按鈕以後回到頂部指定位置)
而後在入口文件中只須要引入這個模塊(取名叫backtop.js
),這個back.js
須要依賴上面定義的scrollto.js
模塊。
因此目前的項目目錄以下圖:
第一步:如今來寫backtop.js
模塊
define(['jquery', 'scrollto'], function($, scrollto){ //執行函數部分 function BackTop(el, opts){ this.opts = $.extend({}, BackTop.DEFAULTS, opts); this.$el = $(el); //el是節點 this.scroll = new scrollto.ScrollTo({ dest: 0, speed: this.opts.speed }); this._checkPosition(); //加載時就檢查位置,解決bug if(this.opts.mode == 'move'){ //是move才執行move函數,其餘執行go this.$el.on('click', $.proxy(this._move, this)); }else{ this.$el.on('click', $.proxy(this._go, this)); } $(window).on('scroll', $.proxy(this._checkPOsition, this)); } //定義默認屬性部分 BackTop.DEFAULTS = { mode: 'move', pos: $(window).height(), speed: 800 }; //定義功能函數部分 BackTop.prototype._move = function(){ this.scroll.move(); }; BackTop.prototype._go = function(){ this.scroll.go(); }; BackTop.prototype._checkPosition = function(){ if($(window).scrollTop() > this.opts.pos){ this.$el.fadeIn(); }else{ this.$el.fadeOut(); } }; //暴露模塊接口,返回整個對象 return{ BackTop: BackTop }; });
第二步:scrollto.js保持不變
第三步:寫main.js入口文件
//定義別名 requirejs.config({ paths: { jquery: 'jquery-3.1.0' } }); //調用backtop.js模塊 requirejs(['jquery', 'backtop'], function($, backtop){ //實例化BackTop new backtop.BackTop($('#backtop'),{ mode: 'move', pos:100, speed: 2000 }); });
這個demo中的模塊化是這樣一種思想:
首先把功能函數放在一個模塊中(move和go)
把整個實現功能也抽象成一個模塊,依賴上一個功能函數模塊
最後只須要在入口文件中實例化一下這個最外層的模塊,便可完成一系列功能的調用。
每一個模塊都用面向對象的思想,定義模塊而且暴露接口
默認值的用法可讓調用者拿起就用,能夠不用考慮傳參數。