jquery插件开发

纯js插件

优点是依赖性小,不依赖于jQuery等函数库;缺点是比较繁琐;一般不涉及到Dom树的可使用纯js写插件(如:日期库插件moment.js)

基于jquery编写插件

jQuery插件开发方式主要有三种:

  1. 通过$.extend()来扩展jQuery
  2. 通过$.fn 向jQuery添加新的方法
  3. 通过$.widget()应用jQuery UI的部件工厂方式创建

第一种方式太简单,仅仅是在jQuery命名空间或者理解成jQuery身上添加了一个静态方法而以,通常我们使用第二种方法来进行简单插件开发,说简单是相对于第三种方式。第三种方式是用来开发更高级jQuery部件的,这里不细说。

$.extend()用法

  • 给extend方法传递单个对象的情况下,这个对象会合并到jQuery身上,所以我们就可以在jQuery身上调用新合并对象里包含的方法了$.extend(Object)

    1
    2
    3
    4
    5
    6
    7
    8
    $.extend({
    log: function(str) {
    console.log(str ? str : 'Good!');
    }
    })
    //调用
    $.log(); // Good!
    $.log('Hello!'); // Hello!
  • 当给extend方法传递一个以上的参数时,它会将所有参数对象合并到第一个里。同时,如果对象中有同名属性时,合并的时候后面的会覆盖前面的(经常用于$.fn写法的参数处理上)$.extend(target,object1,[objectN])$.extend([deep],target,object1,[objectN])(deep:如果设为true,则递归合并。target:待修改对象。object1和objectN:待合并到第一个对象的对象)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 用法一
    var defaults = {'name': 'smalle', 'age': 18};
    var options = {'name': 'aezo', 'sex': 'boy'};
    //
    var settings = $.extend({}, defaults, options);
    console.log(settings); // Object {name: "aezo", age: 18, sex: "boy"}

    // 用法二
    var result = $.extend( true, {},
    { name: "John", location: {city: "Boston", county:"USA"} },
    { name: "smalle", last: "Resig", location: {state: "MA", county:"China"} } );
    // deep为true,结果为:{name: "smalle", last: "Resig", location:{city:"Boston", state:"MA", county:"China"}} // name覆盖了,但是county没有覆盖
    // deep为false时则为:{name: "smalle", last: "Resig", location:{city:"Boston", county:"China"}} // state未拷贝
    console.log(result);

$.fn用法

  • 基本格式:

    1
    2
    3
    4
    // $.fn. 后面接为插件名
    $.fn.pluginName = function() {
    //your code goes here
    }
  • this说明:在插件名字定义的这个函数内部 ,this指代的是我们在调用该插件时,用jQuery选择器选中的元素,一般是一个 jQuery类型 的集合。

    1
    2
    3
    4
    $.fn.myPlugin = function() {
    this.css('color', 'red');
    }
    // 如:$('a').myPlugin(); 则 this = $('a')
    • 比如 $(‘a’) 返回的是页面上所有 a 标签的集合,且这个集合已经是 jQuery 包装类型了,也就是说,在对其进行操作的时候可以直接调用jQuery的其他方法而不需要再用美元符号来包装一下。
    • 那么通过调用 jQuery 的 .each() 方法就可以处理合集中的每个元素了,但此刻要注意的是,在 each 方法内部,this 指带的是普通的 DOM 元素了,如果需要调用 jQuery 的方法那就需要用$来重新包装一下。
  • 参数处理:包括默认参数和传入参数,可以使用$.extend()进行处理

  • 支持链式调用:jQuery的一个特性是支持链式调用,选择好DOM元素后可以不断地调用其他方法。要让插件不打破这种链式调用,只需return一下即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $.fn.myPlugin = function() {
    var defaults = {
    'color': 'red',
    'fontSize': '12px'
    };
    var settings = $.extend({}, defaults, options);

    return this.each(function(){
    $(this).css({
    'color': settings.color,
    'fontSize': settings.fontSize
    });
    });
    }
  • 面向对象(方便维护、代码清晰)

    如果将需要的重要变量定义到对象的属性上,函数变成对象的方法,当我们需要的时候通过对象来获取,一来方便管理,二来不会影响外部命名空间,因为所有这些变量名还有方法名都是在对象内部。以后要加新功能新方法,只需向对象添加新变量及方法即可,然后在插件里实例化后即可调用新添加的东西。

  • 命名空间

    • 不仅仅是jQuery插件的开发,我们在写任何JS代码时都应该注意的一点是不要污染全局命名空间。因为随着你代码的增多,如果有意无意在全局范围内定义一些变量的话,最后很难维护,也容易跟别人写的代码有冲突。
    • 比如你在代码中向全局window对象添加了一个变量status用于存放状态,同时页面中引用了另一个别人写的库,也向全局添加了这样一个同名变量,最后的结果肯定不是你想要的。所以不到万不得已,一般我们不会将变量定义成全局的。
    • 一个好的做法是始终用自调用匿名函数包裹你的代码,这样就可以完全放心,安全地将它用于任何地方了,绝对没有冲突。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      ;(function($, window, document, undefined) {
      //定义Beautifier的构造函数
      var Beautifier = function(ele, opt) {
      this.$element = ele,
      this.defaults = {
      'color': 'red',
      'fontSize': '12px',
      'textDecoration': 'none'
      },
      this.options = $.extend({}, this.defaults, opt)
      }
      //定义Beautifier的方法
      Beautifier.prototype = {
      beautify: function() {
      return this.$element.css({
      'color': this.options.color,
      'fontSize': this.options.fontSize,
      'textDecoration': this.options.textDecoration
      });
      }
      }
      //在插件中使用Beautifier对象
      $.fn.myPlugin = function(options) {
      //创建Beautifier的实体
      var beautifier = new Beautifier(this, options);
      //调用其方法
      return beautifier.beautify();
      }
      })(jQuery, window, document);
    • 开头的 ; 防止别人的代码没有以分号结尾,导致我们的代码编译出错

    • 传入系统变量,那么window等系统变量在插件内部就有了一个局部的引用,对访问速度有些许提升
    • 关于参数undefined:为了得到没有被修改的undefined,我们并没有传递这个参数,但却在接收时接收了它,因为实际并没有传,所以undefined那个位置接收到的就是真实的undefined了
  • jQuery选择器

    • 尽量使用Id选择器。jQuery的选择器使用的API都是基于getElementById或getElementsByTagName,因此可以知道 效率最高的是Id选择器,因为jQuery会直接调用getElementById去获取dom,而通过样式选择器获取jQuery对象时往往会使用 getElementsByTagName去获取然后筛选。

    • 样式选择器应该尽量明确指定tagName, 如果开发人员使用样式选择器来获取dom,且这些dom属于同一类型,例如获取所有className为jquery的div,那么我们应该使用的写法 是$('div.jquery')而不是$(‘.jquery’),这样写的好处非常明显,在获取dom时jQuery会获取div然后进行筛选,而不是 获取所有dom再筛选。

    • 避免迭代,很多同学在使用jQuery获取指定上下文中的dom时喜欢使用迭代方式,如$(‘.jquery .child’),获取className为jquery的dom下的所有className为child的节点,其实这样编写代码付出的代价是非常大 的,jQuery会不断的进行深层遍历来获取需要的元素,即使确实需要,我们也应该使用诸如$(selector, context), $('selector1>selector2'), $(selector1).children(selector2), $(selctor1).find(selector2)之类的方式。

插件开发注意事项

  • 变量定义:好的做法是把将要使用的变量名用一个var关键字一并定义在代码开头,变量名间用逗号隔开。原因有二

    • 一是便于理解,同时代码显得整洁且有规律,也方便管理,变量定义与逻辑代码分开;
    • 二是因为JavaScript中所有变量及函数名会自动提升,也称之为JavaScript的Hoist特性,即使你将变量的定义穿插在逻辑代码中,在代码解析运行期间,这些变量的声明还是被提升到了当前作用域最顶端的,所以我们将变量定义在一个作用域的开头是更符合逻辑的一种做法。当然,再次说明这只是一种约定,不是必需的。
  • 变量及函数命名:一般使用驼峰命名法(CamelCase)。对于常量,所有字母采用大写,多个单词用下划线隔开;当变量是jQuery类型时,建议以$开头。

  • 插件文件的命名:.min 表示压缩版;大小写字母敏感;如果基于 jquery 或者 bootstrap 等插件开发的,一般会以jquery. 开头(如:jquery.PluginName.js);

  • 插件的压缩:去掉换行、空格、注释,可以将进场出现的单词用某个字母代替,压缩后文件名一般加上.min

  • 插件相关的样式(或主题)文件:好处:用户可直接看到很好的效果,拿来即用;坏处:对用户局限太大(如果我们插件中使用bootstrap样式,那么用户向去掉这个风格会很麻烦)

  • 插件的国际化

  • 单元测试:一些最常见的JavaScript单元测试工具:QUnit(是jQuery团队开发并使用的单元测试框架)、YUI Test和JSTestDriver

参考文章

ChatGPT开源小程序