JS組件系列之JS組件封裝過程詳解
前言:
之前分享了那么多bootstrap組件的使用經(jīng)驗(yàn),這篇文章打算研究下JS組件的擴(kuò)展和封裝,我們來感受下JQuery為我們提供$.Extend的神奇,看看我們怎么自定義自己的組件,比如我們想擴(kuò)展一個(gè)$("#id").MyJsControl({})做我們自己的組件,我們該如何去做呢,別急,我們慢慢來看看過程。
一、擴(kuò)展已經(jīng)存在的組件
1、需求背景
很多時(shí)候,我們使用jquery.ajax的方式向后臺(tái)發(fā)送請求,型如
$.ajax({
type: "post",
url: "/User/Edit",
data: { data: JSON.stringify(postdata) },
success: function (data, status) {
if (status == "success") {
toastr.success('提交數(shù)據(jù)成功');
$("#tb_aaa").bootstrapTable('refresh');
}
},
error: function (e) {
},
complete: function () {
}
});
這種代碼太常見了,這個(gè)時(shí)候我們有這樣一個(gè)需求:在自己調(diào)用ajax請求的時(shí)候,我們不想每次都寫error:function(e){}這種代碼,但是我們又想讓它每次都將ajax的錯(cuò)誤信息輸出到瀏覽器讓用戶能夠看到。怎么辦呢?
2、實(shí)現(xiàn)原理
要想實(shí)現(xiàn)以上效果其實(shí)并不難,我們可以將$.ajax({})封裝一層,在封裝的公共方法里面定義error對應(yīng)的事件即可。確實(shí),這樣能達(dá)到我們的要求,但是并不完美,原因很簡單:1)在jquery的基礎(chǔ)上面再封裝一層,效率不夠高;2)需要改變調(diào)用者的習(xí)慣,每次調(diào)用ajax的時(shí)候需要按照我們定義的方法的規(guī)則來寫,而不能直接用原生的$.ajax({})這種寫法,這是我們不太想看到。
既然如此,那我們?nèi)绾巫龅郊炔环庋b控件,又能達(dá)到以上要求呢?答案就是通過我們的$.extend去擴(kuò)展原生的jquery.ajax。
其實(shí)實(shí)現(xiàn)起來也并不難,通過以下一段代碼就能達(dá)到我們的要求。
(function ($) {
//1.得到$.ajax的對象
var _ajax = $.ajax;
$.ajax = function (options) {
//2.每次調(diào)用發(fā)送ajax請求的時(shí)候定義默認(rèn)的error處理方法
var fn = {
error: function (XMLHttpRequest, textStatus, errorThrown) {
toastr.error(XMLHttpRequest.responseText, '錯(cuò)誤消息', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
},
success: function (data, textStatus) { },
beforeSend: function (XHR) { },
complete: function (XHR, TS) { }
}
//3.如果在調(diào)用的時(shí)候?qū)懥薳rror的處理方法,就不用默認(rèn)的
if (options.error) {
fn.error = options.error;
}
if (options.success) {
fn.success = options.success;
}
if (options.beforeSend) {
fn.beforeSend = options.beforeSend;
}
if (options.complete) {
fn.complete = options.complete;
}
//4.擴(kuò)展原生的$.ajax方法,返回最新的參數(shù)
var _options = $.extend(options, {
error: function (XMLHttpRequest, textStatus, errorThrown) {
fn.error(XMLHttpRequest, textStatus, errorThrown);
},
success: function (data, textStatus) {
fn.success(data, textStatus);
},
beforeSend: function (XHR) {
fn.beforeSend(XHR);
},
complete: function (XHR, TS) {
fn.complete(XHR, TS);
}
});
//5.將最新的參數(shù)傳回ajax對象
_ajax(_options);
};
})(jQuery);
如果沒接觸過jquery里面$.extend這個(gè)方法的童鞋可能看不懂以上是什么意思。好,我們首先來看看jquery API對$.extend()方法是作何解釋的。

什么意思呢?我們來看官方的兩個(gè)例子就知道了
栗子一:
var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
$.extend(settings, options);
結(jié)果:
settings == { validate: true, limit: 5, name: "bar" }
栗子二:
var empty = {};
var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
var settings = $.extend(empty, defaults, options);
結(jié)果:
settings == { validate: true, limit: 5, name: "bar" }
empty == { validate: true, limit: 5, name: "bar" }
以上的兩個(gè)簡單例子就說明extend()方法作用就是合并另個(gè)對象,有相同的則覆蓋,沒有相同的則添加。就是這么簡單。
了解了$.extend()的作用,我們就能大概看懂上面那個(gè)擴(kuò)展jquery.ajax的實(shí)現(xiàn)了吧。主要的步驟分為:
1)定義默認(rèn)的error處理方法。
var fn = {
error: function (XMLHttpRequest, textStatus, errorThrown) {
toastr.error(XMLHttpRequest.responseText, '錯(cuò)誤消息', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
},
success: function (data, textStatus) { },
beforeSend: function (XHR) { },
complete: function (XHR, TS) { }
}
2)判斷用戶在調(diào)用$.ajax({})的時(shí)候是否自定了error:function(){},如果定義過,則使用用戶定義的,反之則用默認(rèn)的error處理方法。
3)使用$.extend()將error默認(rèn)處理方法傳入$.ajax()的參數(shù)中。我們看options參數(shù)時(shí)包含$.ajax()方法里面所有的參數(shù)的,然后用默認(rèn)的fn去擴(kuò)展它即可。
通過以上三步就能夠?qū)崿F(xiàn)對$.ajax()方法里面error默認(rèn)處理方法。這樣擴(kuò)展,對于我們使用者來說完全感覺不到變化,我們?nèi)匀豢梢?.ajax({});這樣去發(fā)送ajax請求,如果沒有特殊情況,不用寫error處理方法。
3、組件擴(kuò)展的意義
使用組件擴(kuò)展,能夠幫助我們在原有組件上面增加一些和我們系統(tǒng)業(yè)務(wù)相關(guān)的處理需求,而在使用時(shí),還是和使用原生組件一樣去調(diào)用,免去了在組件上面再封裝一層的臃腫。
二、擴(kuò)展自己組件
上面通過$.extend()方法擴(kuò)展了$.ajax()的error事件處理方法。下面我們來封裝一個(gè)自己的組件試試,功能很簡單,但比較有說明性。我們就以select這個(gè)組件為例,很多情況下,我們的select里面的option都是需要從數(shù)據(jù)庫里面取數(shù)據(jù)的,所以一般的做法就是發(fā)送一個(gè)ajax請求,然后在success方法里面拼html?,F(xiàn)在我們就封裝一個(gè)select遠(yuǎn)程取數(shù)據(jù)的方法。
1、代碼實(shí)現(xiàn)以及使用示例
先上干貨吧,將寫好的整出來:
(function ($) {
//1.定義jquery的擴(kuò)展方法combobox
$.fn.combobox = function (options, param) {
if (typeof options == 'string') {
return $.fn.combobox.methods[options](this, param);
}
//2.將調(diào)用時(shí)候傳過來的參數(shù)和default參數(shù)合并
options = $.extend({}, $.fn.combobox.defaults, options || {});
//3.添加默認(rèn)值
var target = $(this);
target.attr('valuefield', options.valueField);
target.attr('textfield', options.textField);
target.empty();
var option = $('<option></option>');
option.attr('value', '');
option.text(options.placeholder);
target.append(option);
//4.判斷用戶傳過來的參數(shù)列表里面是否包含數(shù)據(jù)data數(shù)據(jù)集,如果包含,不用發(fā)ajax從后臺(tái)取,否則否送ajax從后臺(tái)取數(shù)據(jù)
if (options.data) {
init(target, options.data);
}
else {
//var param = {};
options.onBeforeLoad.call(target, options.param);
if (!options.url) return;
$.getJSON(options.url, options.param, function (data) {
init(target, data);
});
}
function init(target, data) {
$.each(data, function (i, item) {
var option = $('<option></option>');
option.attr('value', item[options.valueField]);
option.text(item[options.textField]);
target.append(option);
});
options.onLoadSuccess.call(target);
}
target.unbind("change");
target.on("change", function (e) {
if (options.onChange)
return options.onChange(target.val());
});
}
//5.如果傳過來的是字符串,代表調(diào)用方法。
$.fn.combobox.methods = {
getValue: function (jq) {
return jq.val();
},
setValue: function (jq, param) {
jq.val(param);
},
load: function (jq, url) {
$.getJSON(url, function (data) {
jq.empty();
var option = $('<option></option>');
option.attr('value', '');
option.text('請選擇');
jq.append(option);
$.each(data, function (i, item) {
var option = $('<option></option>');
option.attr('value', item[jq.attr('valuefield')]);
option.text(item[jq.attr('textfield')]);
jq.append(option);
});
});
}
};
//6.默認(rèn)參數(shù)列表
$.fn.combobox.defaults = {
url: null,
param: null,
data: null,
valueField: 'value',
textField: 'text',
placeholder: '請選擇',
onBeforeLoad: function (param) { },
onLoadSuccess: function () { },
onChange: function (value) { }
};
})(jQuery);
先來看看我們自定義組件如何使用:
用法一:通過URL遠(yuǎn)程取數(shù)據(jù)并初始化
首先定義一個(gè)空的select
<select id="sel_search_plant" class="form-control"></select>
然后初始化它
$(function(){
$('#sel_search_plant').combobox({
url: '/apiaction/Plant/Find',
valueField: 'TM_PLANT_ID',
textField: 'NAME_C'
});
})
參數(shù)很簡單,就不一一介紹了。很簡單有木有~~
用法二:取值和設(shè)置
var strSelectedValue = $('#sel_search_plant').combobox("getValue");
$('#sel_search_plant').combobox("setValue", "aaa");
其實(shí)對于簡單的select標(biāo)簽,博主覺得這里的getValu和SetValue意義不大,因?yàn)橹苯油ㄟ^$('#sel_search_plant').val()就能解決的事,何必要再封一層。這里僅僅是做演示,試想,如果是封裝成類似select2或者multiselect這種組件,getValue和setValue的意義就有了,你覺得呢?
2、代碼詳細(xì)講解
上面的實(shí)現(xiàn)代碼,如果您一眼就能看懂,證明您是經(jīng)常封組件的大蝦了,下面的就不用看了。如果看不懂,也沒關(guān)系,我們將代碼拆開詳細(xì)看看里面是什么鬼。
(1)首先看看我們最??吹降娜缦聦懛ǎ?/p>
(function ($) {
//....封裝組件邏輯
})(jQuery);
初初看到這種用法,博主也是狂抓,這是什么鬼嘛,四不像啊。使用多了之后才知道原來這就是一個(gè)匿名函數(shù)的形式。將它拆開來看如下:
var fn = function($){
//.....組件封裝邏輯
};
fn(jQuery);
也就是說這種寫法就表示先定義一個(gè)方法,然后立即調(diào)用這個(gè)方法,jQuery相當(dāng)于實(shí)參。打開jquery.js的原文件可以看到,jQuery是這個(gè)文件里面的一個(gè)全局變量。
(2)定義自己的組件的代碼:
$.fn.combobox = function (options, param) {
};
習(xí)慣這種寫法的應(yīng)該知道,這個(gè)就表示向jquery對象添加自定義方法,比如你想使用文章開始的 $("#id").MyJsControl({}) 這種用法,你就可以這樣定義 $.fn.MyJsControl=function(options){} 。
(3) options = $.extend({}, $.fn.combobox.defaults, options || {}); 這一句,看過上文的朋友應(yīng)該還記得extend這么一個(gè)方法吧,怎么樣,又來了你。這句話其實(shí)就沒什么好說的了,合并默認(rèn)參數(shù)和用戶傳進(jìn)來的參數(shù)。
(4)默認(rèn)參數(shù)列表
$.fn.combobox.defaults = {
url: null,
param: null,
data: null,
valueField: 'value',
textField: 'text',
placeholder: '請選擇',
onBeforeLoad: function (param) { },
onLoadSuccess: function () { },
onChange: function (value) { }
};
如果用戶沒有傳參,就用默認(rèn)的參數(shù)列表。如果你夠細(xì)心,你會(huì)發(fā)現(xiàn)博主之前分享的其他bootstrap組件的js文件里面都有這么一個(gè)default參數(shù)列表。我們隨便找兩個(gè):
bootstrap上傳組件

bootstrap table組件


基本都是這么些用法。這樣來看,是否也可以自己封一個(gè)js組件~~
三、總結(jié)
以上就是博主對js組件擴(kuò)展以及封裝用法的認(rèn)識(shí)和總結(jié)。當(dāng)然,都是些比較簡單基礎(chǔ)的封裝,如果想要實(shí)現(xiàn)類似bootstrap table的組件,那還差得很遠(yuǎn)。不過萬丈高樓平地起,只要打好基礎(chǔ),封一個(gè)自己的table組件也不是什么大問題。如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
《javascript設(shè)計(jì)模式》學(xué)習(xí)筆記一:Javascript面向?qū)ο蟪绦蛟O(shè)計(jì)對象成員的定義分析
這篇文章主要介紹了《javascript設(shè)計(jì)模式》學(xué)習(xí)筆記Javascript面向?qū)ο蟪绦蛟O(shè)計(jì)對象成員的定義,結(jié)合實(shí)例形式分析了《javascript設(shè)計(jì)模式》中JavaScript面向?qū)ο蟪绦蛟O(shè)計(jì)的原理、定義、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04
基于JavaScript實(shí)現(xiàn)跳轉(zhuǎn)提示頁面
這篇文章主要介紹了基于JavaScript實(shí)現(xiàn)跳轉(zhuǎn)提示頁面 的相關(guān)資料,需要的朋友可以參考下2016-09-09
JavaScript動(dòng)態(tài)檢測密碼強(qiáng)度原理及實(shí)現(xiàn)方法詳解
這篇文章主要介紹了JavaScript動(dòng)態(tài)檢測密碼強(qiáng)度原理及實(shí)現(xiàn)方法,結(jié)合具體實(shí)例形式詳細(xì)分析了javascript針對輸入字符串密碼強(qiáng)度檢測的原理與相關(guān)判斷操作技巧,需要的朋友可以參考下2019-06-06
使用JavaScript實(shí)現(xiàn)獲取頁面滾動(dòng)位置
這篇文章主要為大家詳細(xì)介紹了在JavaScript中如何獲取水平和垂直的滾動(dòng)位置,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2024-12-12
一文快速弄懂webpack動(dòng)態(tài)import原理
無論你開發(fā)使用的是CommonJS規(guī)范還是ES6模塊規(guī)范,打包后的文件都統(tǒng)一使用webpack自定義的模塊規(guī)范來管理、加載模塊,下面這篇文章主要給大家介紹了關(guān)于webpack動(dòng)態(tài)import原理的相關(guān)資料,需要的朋友可以參考下2022-04-04
JavaScript版DateAdd和DateDiff函數(shù)代碼
VBScript中有兩個(gè)非常好用的日期操作函數(shù):DateAdd用來給日期添加指定時(shí)間間隔,DateDiff用來返回兩個(gè)日期的時(shí)間間隔??上У氖荍avaScript沒有,不過我們可以寫一個(gè)函數(shù)來實(shí)現(xiàn),一樣的,呵呵2012-03-03
原生微信小程序中封裝一個(gè)模擬select下拉框組件代碼示例
這篇文章主要給大家介紹了關(guān)于原生微信小程序中封裝一個(gè)模擬select下拉框組件的相關(guān)資料,文中介紹了如何在小程序中創(chuàng)建和使用自定義組件van-select,包括組件的創(chuàng)建步驟和在頁面中的應(yīng)用方法,需要的朋友可以參考下2024-11-11

