jQuery组件封装实战:从$.extend扩展原生方法到自定义UI组件

作者:闭包旅人时间:2026-10-22 03:37:18浏览:361

本文深入解析jQuery组件的扩展与封装技术。首先利用$.extend方法重写$.ajax的错误处理逻辑,实现全局错误提示而不改变原有调用习惯。随后,通过自研combobox组件实例,演示如何基于jQuery.fn定义私有方法、合并默认参数与用户配置,以及处理远程数据加载。文章涵盖匿名函数封装、参数合并及方法扩展等核心概念,帮助开发者掌握构建可复用JS组件的基础流程。

前言:

此前已分享多篇Bootstrap组件的使用心得,本文旨在探讨JavaScript组件的扩展与封装机制。我们将深入体验jQuery提供的$.extend方法的神奇之处,学习如何自定义组件。例如,若希望扩展一个$("#id").MyJsControl({})作为自有组件,具体应如何操作?接下来我们将逐步解析实现过程。

扩展已经存在的组件

一、扩展已经存在的组件

1、需求背景

在实际开发中,我们常通过jquery.ajax向后端发送请求,典型代码形式如


$.ajax({
    type: "post",
    url: "/User/Edit",
    data: { data: JSON.stringify(postdata) },
    success: function (data, status) {
     if (status == "success") {
      toastr.success('提交数据成功');
      $("#tb_aaa").bootstrapTable('refresh');
     }
    },
    error: function (e) {
    },
    complete: function () {
    }
   });

此类代码极为常见。此时,我们面临一个需求:在调用ajax请求时,不希望每次手动编写error:function(e){},但又希望每次ajax发生错误时,都能自动将错误信息输出至浏览器供用户查看。该如何实现?

2、实现原理

要实现上述效果并不复杂,我们可以将$.ajax({})进行封装,在封装的公共方法中定义error对应的事件即可。确实,这样做能达到要求,但并非完美方案,原因有二:1)在jQuery基础上再封装一层,效率不够高;2)需要改变调用者的习惯,每次调用ajax时需遵循我们定义的方法规则,而无法直接使用原生$.ajax({})写法,这并非我们期望的结果。

那么,如何做到既不封装控件,又能实现上述需求呢?答案是通过$.extend去扩展原生的jquery.ajax。

实现过程并不复杂,通过以下一段代码即可达成目标。


(function ($) {
 //1.得到$.ajax的对象
 var _ajax = $.ajax;
 $.ajax = function (options) {
  //2.每次调用发送ajax请求的时候定义默认的error处理方法
  var fn = {
   error: function (XMLHttpRequest, textStatus, errorThrown) {
    toastr.error(XMLHttpRequest.responseText, '错误消息', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
   },
   success: function (data, textStatus) { },
   beforeSend: function (XHR) { },
   complete: function (XHR, TS) { }
  }
  //3.如果在调用的时候写了error的处理方法,就不用默认的
  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.扩展原生的$.ajax方法,返回最新的参数
  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.将最新的参数传回ajax对象
  _ajax(_options);
 };
})(jQuery);

若未接触过jQuery中$.extend方法的读者可能对此感到困惑。首先,我们来看看jQuery API对$.extend()方法的官方解释。

扩展已经存在的组件

具体含义如何?通过官方提供的两个示例即可明了。

示例一:


var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
$.extend(settings, options);

运行结果:


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);

运行结果:


settings == { validate: true, limit: 5, name: "bar" }
empty == { validate: true, limit: 5, name: "bar" }

以上两个简单示例表明,extend()方法的作用是合并两个对象:若存在相同属性则覆盖,不存在则添加。原理十分简单。

理解了$.extend()的作用,我们便能大致看懂前述扩展jquery.ajax的实现逻辑。主要步骤包括:

1)定义默认的error处理方法。


var fn = {
   error: function (XMLHttpRequest, textStatus, errorThrown) {
    toastr.error(XMLHttpRequest.responseText, '错误消息', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
   },
   success: function (data, textStatus) { },
   beforeSend: function (XHR) { },
   complete: function (XHR, TS) { }
  }

2)判断用户在调用$.ajax({})时是否自定义了error:function(){},若已定义则使用用户自定义的方法,否则使用默认的error处理方法。

3)使用$.extend()将error默认处理方法传入$.ajax()的参数中。观察options参数可知,其包含$.ajax()方法内的所有参数,随后用默认函数fn对其进行扩展即可。

通过以上三步,即可实现对$.ajax()方法内error默认处理方法的扩展。这种扩展方式对用户透明,使用者仍可继续使用$.ajax({});发送ajax请求,若无特殊情况,无需编写error处理方法。

3、组件扩展的意义

使用组件扩展,有助于在原有组件基础上增加与系统业务相关的处理逻辑,同时保持调用方式与原生组件一致,避免了在组件上层再封装一层所带来的冗余代码。

扩展自己组件

二、扩展自己组件

前文通过$.extend()方法扩展了$.ajax()的error事件处理方法。接下来,我们尝试封装一个自己的组件。该组件功能简单但具有代表性,以select组件为例。通常情况下,select中的option数据需从数据库获取,常规做法是发送ajax请求,并在success方法中拼接HTML。现在,我们封装一个select远程获取数据的方法。

1、代码实现以及使用示例

首先展示完整代码:


(function ($) {
  //1.定义jquery的扩展方法combobox
 $.fn.combobox = function (options, param) {
  if (typeof options == 'string') {
   return $.fn.combobox.methods[options](this, param);
  }
  //2.将调用时候传过来的参数和default参数合并
  options = $.extend({}, $.fn.combobox.defaults, options || {});
  //3.添加默认值
  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.判断用户传过来的参数列表里面是否包含数据data数据集,如果包含,不用发ajax从后台取,否则否送ajax从后台取数据
  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.如果传过来的是字符串,代表调用方法。
 $.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.默认参数列表
 $.fn.combobox.defaults = {
  url: null,
  param: null,
  data: null,
  valueField: 'value',
  textField: 'text',
  placeholder: '请选择',
  onBeforeLoad: function (param) { },
  onLoadSuccess: function () { },
  onChange: function (value) { }
 };
})(jQuery);

接下来查看自定义组件的使用方式:

用法一:通过URL远程取数据并初始化

首先定义一个空的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'
  });
})

参数设置简单,此处不再逐一介绍。实现过程十分简洁。

用法二:取值和设置


var strSelectedValue = $('#sel_search_plant').combobox("getValue");
$('#sel_search_plant').combobox("setValue", "aaa");

对于简单的select标签,笔者认为getValu和SetValue方法意义不大,因为直接通过$('#sel_search_plant').val()即可解决。此处仅作演示,试想若封装成类似select2或multiselect等复杂组件,getValue和setValue的意义便凸显出来。

2、代码详细讲解

若读者能一眼看懂上述实现代码,说明具备丰富的组件封装经验,下文可略过。若看不懂,不妨将代码拆解,详细剖析其内部逻辑。

(1)首先观察我们常见的如下写法:


(function ($) {
  //....封装组件逻辑
})(jQuery);

初看这种用法令人困惑,不知其意。使用多次后便会明白,这实际上是一种匿名函数的形式。将其拆解如下:


var fn = function($){
  //.....组件封装逻辑
};
fn(jQuery);

这种写法表示先定义一个方法,然后立即调用该方法,jQuery作为实参传入。打开jquery.js源文件可见,jQuery是该文件中的全局变量。

(2)定义自己的组件的代码:


$.fn.combobox = function (options, param) {
};

熟悉这种写法的读者应知,这表示向jQuery对象添加自定义方法。例如,若想使用文章开头的$("#id").MyJsControl({})这种调用方式,便可如此定义 $.fn.MyJsControl=function(options){} 。

(3) options = $.extend({}, $.fn.combobox.defaults, options || {}); 这一句,回顾前文的朋友应记得extend方法。此处再次使用,其含义明确,即合并默认参数与用户传入的参数。

(4)默认参数列表


$.fn.combobox.defaults = {
  url: null,
  param: null,
  data: null,
  valueField: 'value',
  textField: 'text',
  placeholder: '请选择',
  onBeforeLoad: function (param) { },
  onLoadSuccess: function () { },
  onChange: function (value) { }
 };

若用户未传参,则使用默认参数列表。细心的读者可能发现,博主此前分享的其他Bootstrap组件的js文件中均包含此类default参数列表。我们选取两个示例:

bootstrap上传组件

扩展已经存在的组件

bootstrap table组件

扩展已经存在的组件

扩展已经存在的组件

基本均采用此类用法。如此看来,自行封装JS组件并非难事。

三、总结

以上为博主对JS组件扩展及封装用法的认识与总结。所述内容均为较为基础的封装,若要实现如bootstrap table般复杂的组件,仍有较大差距。然而,万丈高楼平地起,打好基础后,封装自己的table组件并非难题。若有任何疑问,请留言,小编会及时回复。在此衷心感谢大家对本站的支持!

作者最新文章