jQuery 插件開發模式
jQuery 的插件開發模式主要有三種:
- 通過$.extend()來擴展jQuery
- 通過$.fn向jQuery添加新的方法
- 通過$.widget()應用jQuery UI 的部件工廠方式創建
這里我們選用第二種:
$.fn.myplugin = functin() {
// plugin code
}
因為這種方法是加在jQuery對象上,可以在jQuery選擇器選擇元素后直接調用:
$('body').myplugin();
若對其他兩種方法有興趣,請自行查看jQuery 官方文檔。
一個非常簡單的例子
改變元素的背景顏色插件:
$.fn.tinyPlugin = function(){
this.css('background-color','#fff'); // 這里的this是jQuery對象,而不是原生的js對象
}
這樣寫的話,顏色不能使用時定義,修改一下:
$.fn.tinyPlugin = function(bg){
this.css('background-color',bg);
}
以上是一個jQuery插件的基本結構。但編寫更復雜的jQuery插件,借助面向對象的思想可以使插件具有更好的擴展性、可維護性。
面向對象開發插件
大體思路如下:
1.創建myplugin構造函數
2.定義myplugin的方法
3.添加到jQuery對象中
基本結構
首先將代碼放到一個自執行的匿名函數中,防止全局對象污染:
根據上面的步驟,基本結構大致如下:
// 定義構造函數
function myplugin($element,options){
// ...
}
// 添加方法
myplugin.prototype = {
method1: function(){
// ...
},
method2: function(){
// ...
}
};
// 添加到jQuery對象中
$.fn.myplugin = function(options){
new myplugin(this,options);
}
后面將在這個結構上進行完善:
放到立即執行的匿名函數內部
放到匿名函數內部的主要作用是隔離作用域,避免變量污染,而自執行可以返回需要的函數或對象,而不需要每次通過條件判斷為處理,只需要在首次加載的時候賦值給某個變量。更詳細的信息參見知乎上的 這個問題。
;(function($){
// ...
})(jQuery);
留意到上面,函數前還加了分號,這是由於自執行函數前省略分號的表達式時,某些情況下會報錯,可以看 這里。
在匿名函數里面還傳入了jQuery對象,這樣做的好處是美元符號 $
在內部是一個私有變量,僅代表jQuery,可以防止和其他使用 $
的庫沖突。
有的自執行函數還將window對象、undefined對象傳入,具體作用可以看 這里。
默認參數
options是提供給使用者定義的參數。一個好的插件,應該提供必要的參數給使用者,並且提供好默認的參數,因為很多情況下不是每個參數都需要自定義。之前在項目中寫了一個加載更多 LoadMore
插件,就以這個插件為例:
function LoadMore($element,options){
this.$element = $element;
this._defaults = {
loadingSelector: ".zc_loading", //加載中的提示
overSelector: ".zc_loaded", // 加載完畢的加載
}
this.options = $.extend({},this._defaults,options||{}); // 合並參數
}
初始化方法
一般插件都會有一個 init()
方法,調用插件時做一些初始化操作:
function myplugin($element,options){
this.init();
}
// 添加方法
myplugin.prototype = {
init: function(){
// ...
}
};
防止多次初始化
為了防止用戶多次在同一個元素上調用插件,有必要加一些已初始化的判斷。通過在該元素上用 data()
方法緩存插件的實例對象,每次實例化時判斷該緩存對象即可。
$.fn.myplugin = function(options){
if (!$.data(this, 'myplugin')) {
$.data(this, 'myplugin', new myplugin(this, options));
}
}
遍歷所有元素
$
選擇器返回的有可能是多個元素,因此需要返回的所有元素添加插件,直接使用jQuery自帶的 each()
方法:
$.fn.myplugin = function(options){
this.each(function(){
if (!$.data(this, 'myplugin')) {
$.data(this, 'myplugin', new myplugin(this, options));
}
});
}
注意這里的this
是jQuery對象,而不是原生JS對象。所以可以直接使用this.each()
。
保持鏈式操作
由於jQuery有個鏈式操作的特性,在每個方法調用后都會返回該對象,因此只需要 return
該對象即可保持鏈式操作:
$.fn.myplugin = function(options){
return this.each(function(){
// ...
});
}
給插件添加方法
插件不僅有可配置的參數,很多時候還需要調用方法。比如說“加載更多”插件,可能需要知道現在加載到第幾頁,或者是否已經全部加載完畢。此時就需要調用方法來獲得結果。
在前面我們知道是在 prototype
定義方法:
LoadMore.prototype = {
init: function(){},
// 重新加載
reload: function(){},
// 開始加載
startLoad: function(){},
// 本次加載完成
loadOver: function(){},
// 全部加載完畢
allLoadOver: function(){},
// 插入內容
insert: function(content){}
}
方法名可以通過options參數傳入,只需在內部做字符串判斷:
$.fn.LoadMore = function(options,para){
var instance;
instance = $.data(this,'LoadMore');
if(!instance){
instance = new LoadMore(this,options);
$.data(this,'LoadMore',instance);
}
// 如果是字符串則調用方法
if($.type(options) === 'string') {
return instance[options](para);
}
return this; // 保持鏈式
}
因此我們調用方法可以像下面這樣:
$('#list').LoadMore('startLoad');
但是,當調用的方法需要傳參怎么辦?很簡單,那就再加一個參數 para
:
$.fn.LoadMore = function(options,para){
// ...
// 如果是字符串則調用方法
if($.type(options) === 'string') {
return instance[options](para);
}
return this; // 保持鏈式
}
調用帶參數的方法:
$('#list').LoadMore('insert','這是待插入的內容');
這里如果要使用回調函數,和上面也是一樣的道理,只需在內部加個函數判斷。不詳述。若有更多的參數,請使用對象 {}
。
配置參數中添加事件
在寫 LoadMore
插件的過程中,還發現需要使用事件,比如說滾動到某個位置時,執行某些操作。也很簡單,事件我們可以直接寫在配置參數 options 中:
this._defaults = {
onScroll: null, // scroll中
onScrollBottom: null // scroll到底部
}
然后在內部 init()
函數中調用前判斷:
init: function(){
var that = this,
$win = $(window),
$doc = $(document);
$win.on('scroll',function(){
(typeof that.options['onScroll'] == 'function') && that.options['onScroll']();
if($win.scrollTop() + $win.height() >= $doc.height()-10){
(typeof that.options['onScrollBottom'] == 'function') && that.options['onScrollBottom']();
}
});
}
兼容AMD規范
現在流行模塊化開發,AMD是require.js在推廣過程中的規范化產出,AMD說明見 這里。
下面的代碼可以使你的插件兼容AMD規范,可以在require.js中直接調用,當然,沒有使用require.js直接 <script>
引入也是可以的。
// amd support
(function(factory){
// amd support
if(typeof define === 'function' && define.amd){
define(['jquery'],factory);
}else {
factory(jQuery);
}
}(function($){
var exports = {};
// 這里寫插件代碼
return exports;
}));
上面的前一段是判斷是否有define函數,有則調用define方法定義一個模塊,否則直接執行。后一段的 exports
對象是模塊的返回對象,模塊對外的接口。關於AMD規范的具體信息,可參考阮一峰的 這篇文章。
結語
上面所寫的就是寫一個插件的大致流程,由於能力有限,還有很多要完善的地方。想要了解更多,這里推薦一篇文章 深入理解jQuery插件開發。另外,這里提供一個gitHub上面的某位大神寫的插件 SlipHover供參考。