jQuery插件开发的五种形态小结


Posted in Javascript onMarch 04, 2015

关于jQuery插件的开发自己也做了少许研究,自己也写过多个插件,在自己的团队了也分享过一次关于插件的课。开始的时候整觉的很复杂的代码,现在再次看的时候就清晰了许多。这里我把我自己总结出来的东西分享出来,帮助那些和我一样曾经遇到过同样问题的人。

我要做什么
我想要得到的javascript 插件应该会有以下几个特征

代码相对独立
链式操作
插件可配置
有可操作的方法,插件的生命周期可控制
配置可被缓存
可扩展
无冲突处理
事件代理,动态初始化

* 以下的代码均假设存在 jQuery

插件的第一形态

面对这种情况,通常我们会通过定义function的方式来实现。

function pluginName($selector){

    $.each($selector, function () {

        $(this).css("background-color", "#ccc");

        // to do something...

    });

}

// pluginName(document.getElementsByClassName("demo"));

因为我谈的是jQuery插件开发,那么我现在把这段代码扩展到jQuery上,代码如下:

// IIFE(立即调用函数表达式);  [参考 http://suqing.iteye.com/blog/1981591/]

;(function ($) {

    // 扩展这个方法到jQuery.

    // $.extend() 是吧方法扩展到 $ 对象上,和 $.fn.extend 不同。 扩展到 $.fn.xxx 上后,

    // 调用的时候就可以是 $(selector).xxx()

    $.fn.extend({

        // 插件名字

        pluginName: function () {

            // 遍历匹配元素的集合

            // 注意这里有个"return",作用是把处理后的对象返回,实现链式操作

            return this.each(function () {

                // 在这里编写相应的代码进行处理

            });

        }

    });

// 传递jQuery到内层作用域去, 如果window,document用的多的话, 也可以在这里传进去.

// })(jQuery, window, document, undefined);

})(jQuery, undefined);

// 调用方式 $(".selector").pluginName().otherMethod();

但是还差的远,目前只解决了两个问题

代码相对独立
链式操作
插件可配置
有可操作的方法,插件的生命周期可控制
配置可被缓存
可扩展
无冲突处理
事件代理,动态初始化

插件的第二形态

现在来给插件添加参数支持。代码如下

;(function($){

    $.fn.pluginName = function(options) {

        // 合并参数,通过“extend”合并默认参数和自定义参数

        var args = $.extend({}, $.fn.pluginName.defaults, options);

        return this.each(function() {

            console.log(args.text);

            // to do something...

        });

    };

    // 默认参数

    $.fn.pluginName.defaults = {

        text : "hello"

    };

})(jQuery);

// $(".selector").pluginName({

//     text : "hello world!"

// });

添加参数支持还比较容易些,又解决一问题

代码相对独立
链式操作
插件可配置
有可操作的方法,插件的生命周期可控制
配置可被缓存
可扩展
无冲突处理
事件代理,动态初始化

插件的第三形态

现在来添加方法的支持,我前面所提到的生命周期可控制,意思差不多,例如添加reInit,destory等方法来控制插件。

;(function($){

    $.fn.pluginName = function (method) {

        // 如果第一个参数是字符串, 就查找是否存在该方法, 找到就调用; 如果是object对象, 就调用init方法;.

        if (methods[method]) {

            // 如果存在该方法就调用该方法

            // apply 是吧 obj.method(arg1, arg2, arg3) 转换成 method(obj, [arg1, arg2, arg3]) 的过程.

            // Array.prototype.slice.call(arguments, 1) 是把方法的参数转换成数组.

            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));

        } else if (typeof method === 'object' || !method) {

            // 如果传进来的参数是"{...}", 就认为是初始化操作.

            return methods.init.apply(this, arguments);

        } else {

            $.error('Method ' + method + ' does not exist on jQuery.pluginName');

        }

    };

    // 不把方法扩展在 $.fn.pluginName 上. 在闭包内建个"methods"来保存方法, 类似共有方法.

    var methods = {

        /**

         * 初始化方法

         * @param _options

         * @return {*}

         */

        init : function (_options) {

            return this.each(function () {

                var $this = $(this);

                var args = $.extend({}, $.fn.pluginName.defaults, _options);

                // ...

            })

        },

        publicMethod : function(){

            private_methods.demoMethod();

        }

    };

    // 私有方法

    function private_methods = {

        demoMethod : function(){}

    }

    // 默认参数

    $.fn.pluginName.defaults = {

    };

})(jQuery);

// 调用方式

// $("div").pluginName({...});  // 初始化

// $("div").pluginName("publicMethod");  // 调用方法

又解决一问题

代码相对独立
链式操作
插件可配置
有可操作的方法,插件的生命周期可控制
配置可被缓存
可扩展
无冲突处理
事件代理,动态初始化

插件的第四形态

第三形态的插件修改就已经可以应对大多数插件的需求了。精益求精嘛,继续升级。
第四形态的插件是照帮司徒正美的《javascript框架设计》的代码。加了点面向对象的知识。

(function ($) {

    var Plugin = function (element, options) {

        this.element = element;

        this.options = options;

    };

    Plugin.prototype = {

        create: function () {

            console.log(this.element);

            console.log(this.options);

        }

    };

    $.fn.pluginName = function (options) {

        // 合并参数

        return this.each(function () {

            // 在这里编写相应的代码进行处理

            var ui = $._data(this, "pluginName");

            // 如果该元素没有初始化过(可能是新添加的元素), 就初始化它.

            if (!ui) {

                var opts = $.extend(true, {}, $.fn.pluginName.defaults, typeof options === "object" ? options : {});

                ui = new Plugin(this, opts);

                // 缓存插件

                $._data(this, "pluginName", ui);

            }

            // 调用方法

            if (typeof options === "string" && typeof ui[options] == "function") {

                // 执行插件的方法

                ui[options].apply(ui, args);

            }

        });

    };

    $.fn.pluginName.defaults = {};

})(jQuery);

// 调用的方式和之前一样。

这里特别要提下缓存这个东西,插件用多了,觉的这个真的是好东西。
在传统面向对象的插件开发中,至少会声明个变量保存它,但是我到目前写的jQuery插件中都没有,用起来很麻烦。自从把初始化后的插件缓存起来后,方便了许多。通过代码$("#target").data("pluginName")就可以取到对象了。 来看看还有什么问题没有解决

代码相对独立
链式操作
插件可配置
有可操作的方法,插件的生命周期可控制
配置可被缓存
可扩展
无冲突处理
事件代理,动态初始化

插件的第五形态

看了上面的代码是否脑子有点晕了,如果是,休息片刻,稍后回来,下面的代码更精彩。 最后一个方案算是比较全面的了。方案来自Bootstrap,下面代码以 Bootstrap 的 button 插件为例.

!function ($) {

    // ecma262v5 的新东西, 强制使用严谨的代码编写.

    "use strict";

    // BUTTON PUBLIC CLASS DEFINITION

    // ==============================

    var Button = function (element, options) {

        this.$element = $(element);

        this.options = $.extend({}, Button.DEFAULTS, options);

    };

    Button.DEFAULTS = {

        loadingText: 'loading...'

    };

    Button.prototype.setState = function (state) {

        // ...

    };

    Button.prototype.toggle = function () {

        // ...

    };

    // BUTTON PLUGIN DEFINITION

    // ========================

    var old = $.fn.button; // 这里的 $.fn.button 有可能是之前已经有定义过的插件,在这里做无冲突处理使用。

    $.fn.button = function (option) {

        return this.each(function () {

            var $this = $(this);

            // 判断是否初始化过的依据

            var data = $this.data('bs.button');

            var options = typeof option == 'object' && option;

            // 如果没有初始化过, 就初始化它

            if (!data) $this.data('bs.button', (data = new Button(this, options)));

            if (option == 'toggle') data.toggle();

            else if (option) data.setState(option)

        })

    };

    // ① 暴露类名, 可以通过这个为插件做自定义扩展

    $.fn.button.Constructor = Button;

    // 扩展的方式

    // 设置 : $.fn.button.Constructor.newMethod = function(){}

    // 使用 : $btn.button("newMethod");

    // ② 无冲突处理

    $.fn.button.noConflict = function () {

        $.fn.button = old;

        return this

    };

    // ③ 事件代理, 智能初始化

    $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {

        var $btn = $(e.target);

        // 查找要初始化的对象

        if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn');

        // 直接调用方法, 如果没有初始化, 内部会先进行初始化

        $btn.button('toggle');

        e.preventDefault();

    });

}(jQuery);

来看看还有什么问题没有解决

代码相对独立
链式操作
插件可配置
有可操作的方法,插件的生命周期可控制
配置可被缓存
可扩展
无冲突处理
事件代理,动态初始化

补充

现在的插件都要求灵活性要高,比如希望插件可以同时适配jQuery和Zepto,又或者需要支持AMD或者CMD规范。

支持jQuery和Zepto

if (window.jQuery || window.Zepto) {

  (function ($) {

      // plugin code...

  })(window.jQuery || window.Zepto);

}

中间件支持,node

if (typeof(module) !== 'undefined')

{

  module.exports = pluginName;

}

requirejs(AMD) support

if (typeof define === 'function' && define.amd) {

  define([], function () {

      'use strict';

      return pluginName;

  });

}

seajs(CMD) support

if (typeof define === 'function') {

  define([], function () {

      'use strict';

      return pluginName;

  });

}

呼~,问题都解决了,代码若有看不懂的地方可以多看看。后面的几个看不懂也没有关系,在实际的开发中,前面几个够用了。要强调下,并不是越高级的写法越好,要看自己项目的需求合理的选择。

好了,今天的总结就先到这里了,如果大家有更好的插件开发方式,还请告知一下。希望大家能够喜欢本文。

Javascript 相关文章推荐
JS 时间显示效果代码
Aug 23 Javascript
jQuery实现类似淘宝购物车全选状态示例
Jun 26 Javascript
showModelDialog弹出文件下载窗口的使用示例
Nov 19 Javascript
ExtJS4中的requires使用方法示例介绍
Dec 03 Javascript
wap浏览自动跳转到wap页面的js代码
May 17 Javascript
javascript闭包的理解
Apr 01 Javascript
老生常谈 js中this的指向
Jun 30 Javascript
基于JavaScript实现在新的tab页打开url
Aug 04 Javascript
利用JavaScript对中文(汉字)进行排序实例详解
Jun 18 Javascript
bootstrap Table的一些小操作
Nov 01 Javascript
element-ui的回调函数Events的用法详解
Oct 16 Javascript
vue 组件之间事件触发($emit)与event Bus($on)的用法说明
Jul 28 Javascript
深入理解JavaScript系列(36):设计模式之中介者模式详解
Mar 04 #Javascript
百度UEditor编辑器如何关闭抓取远程图片功能
Mar 03 #Javascript
jQuery实现复选框成对选择及对应取消的方法
Mar 03 #Javascript
js实现文本框中输入文字页面中div层同步获取文本框内容的方法
Mar 03 #Javascript
JS实现文字放大效果的方法
Mar 03 #Javascript
jQuery实现的感应鼠标悬停图片色彩渐显效果
Mar 03 #Javascript
js给网页加上背景音乐及选择音效的方法
Mar 03 #Javascript
You might like
php中convert_uuencode()与convert_uuencode函数用法实例
2014/11/22 PHP
php使用标签替换的方式生成静态页面
2015/05/21 PHP
WordPress网站性能优化指南
2015/11/18 PHP
PHP实现图片不变型裁剪及图片按比例裁剪的方法
2016/01/14 PHP
Yii开启片段缓存的方法
2016/03/28 PHP
支付宝服务窗API接口开发php版本
2016/07/20 PHP
PHP制作登录异常ip检测功能的实例代码
2016/11/16 PHP
php原生导出excel文件的两种方法(推荐)
2016/11/19 PHP
Docker搭建自己的PHP开发环境
2018/02/24 PHP
PHP使用PDO创建MySQL数据库、表及插入多条数据操作示例
2019/05/30 PHP
js使用函数绑定技术改变事件处理程序的作用域
2011/12/26 Javascript
通过action传过来的值在option获取进行验证的方法
2013/11/14 Javascript
怎么通过onclick事件获取js函数返回值(代码少)
2015/07/28 Javascript
【JS+CSS3】实现带预览图幻灯片效果的示例代码
2016/03/17 Javascript
jQuery 限制输入字符串长度
2016/06/20 Javascript
jQuery调用Webservice传递json数组的方法
2016/08/06 Javascript
Javascript动画效果(1)
2016/10/11 Javascript
angular4中引入echarts的方法示例
2019/01/29 Javascript
ES6中Symbol、Set和Map用法详解
2019/08/20 Javascript
关于vue.js中实现方法内某些代码延时执行
2019/11/14 Javascript
vue3.0中使用element的完整步骤
2021/03/04 Vue.js
python 生成目录树及显示文件大小的代码
2009/07/23 Python
Python随机生成彩票号码的方法
2015/03/05 Python
python中urllib.unquote乱码的原因与解决方法
2017/04/24 Python
Python操作SQLite数据库的方法详解
2017/06/16 Python
Python语言生成水仙花数代码示例
2017/12/18 Python
浅谈python实现Google翻译PDF,解决换行的问题
2018/11/28 Python
Django 1.10以上版本 url 配置注意事项详解
2019/08/05 Python
Python流程控制 if else实现解析
2019/09/02 Python
Python object类中的特殊方法代码讲解
2020/03/06 Python
python 线程的五个状态
2020/09/22 Python
用pushplus+python监控亚马逊到货动态推送微信
2021/01/29 Python
产品推广策划方案
2014/05/10 职场文书
初中国旗下的演讲稿
2014/08/28 职场文书
2014年教务处工作总结
2014/12/03 职场文书
假如给我三天光明读书笔记
2015/06/26 职场文书