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 相关文章推荐
Javascript new Date().valueOf()的作用与时间戳由来详解
Apr 24 Javascript
js实现获取焦点后光标在字符串后
Sep 17 Javascript
JavaScript中的变量定义与储存介绍
Dec 31 Javascript
JS实现IE状态栏文字缩放效果代码
Oct 24 Javascript
JavaScript 定时器 SetTimeout之定时刷新窗口和关闭窗口(代码超简单)
Feb 26 Javascript
修改jquery中dialog的title属性方法(推荐)
Aug 26 Javascript
微信小程序 动态绑定数据及动态事件处理
Mar 14 Javascript
JS设计模式之单例模式(一)
Sep 29 Javascript
JSON.stringify()方法讲解
Jan 31 Javascript
浅析vue-cli3配置webpack-bundle-analyzer插件【推荐】
Oct 23 Javascript
手机浏览器唤起微信分享(JS)
Oct 11 Javascript
JS实现简易日历效果
Jan 25 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中使用array_filter()函数过滤空数组的实现代码
2014/08/19 PHP
php发送短信验证码完成注册功能
2015/11/24 PHP
thinkphp5实现微信扫码支付
2019/12/23 PHP
php框架CI(codeigniter)自动加载与自主创建对象操作实例分析
2020/06/06 PHP
jquery里的each使用方法详解
2010/12/22 Javascript
js获取URL的参数的方法(getQueryString)示例
2013/09/29 Javascript
不同Jquery版本引发的问题解决
2013/10/14 Javascript
判断某个字符在一个字符串中是否存在的js代码
2014/02/28 Javascript
JavaScript格式化日期时间的方法和自定义格式化函数示例
2014/04/04 Javascript
jQuery Masonry瀑布流插件使用方法详解
2017/01/18 Javascript
js实现兼容PC端和移动端滑块拖动选择数字效果
2017/02/16 Javascript
JS实现的点击表头排序功能示例
2017/03/27 Javascript
JavaScript设计模式之装饰者模式定义与应用示例
2018/07/25 Javascript
使用vue-cli webpack 快速搭建项目的代码
2018/11/21 Javascript
什么时候不能在 Node.js 中使用 Lock Files
2019/06/24 Javascript
[01:03:27]Optic vs VGJ.S 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
Python类定义和类继承详解
2015/05/08 Python
Python实现抓取网页生成Excel文件的方法示例
2017/08/05 Python
基于python 微信小程序之获取已存在模板消息列表
2019/08/05 Python
python 一篇文章搞懂装饰器所有用法(建议收藏)
2019/08/23 Python
python实现淘宝购物系统
2019/10/25 Python
Python+OpenCV检测灯光亮点的实现方法
2020/11/02 Python
python 基于PYMYSQL使用MYSQL数据库
2020/12/24 Python
美国时装品牌:Nautica(诺帝卡)
2016/08/28 全球购物
SmartBuyGlasses德国:购买太阳镜和眼镜
2019/08/20 全球购物
Servlet方面面试题
2016/09/28 面试题
创意广告词
2014/03/17 职场文书
乡镇党委书记第三阶段个人整改措施
2014/09/16 职场文书
法学专业毕业实习自我鉴定2014
2014/09/27 职场文书
2014年汽车销售工作总结
2014/12/01 职场文书
2014年林业工作总结
2014/12/05 职场文书
初三学生语文考试作弊检讨书
2014/12/14 职场文书
什么是创业计划书?什么是商业计划书?这里一一解答
2019/07/12 职场文书
python删除csv文件的行列
2021/04/06 Python
Go语言并发编程 sync.Once
2021/10/16 Golang
避坑之 JavaScript 中的toFixed()和正则表达式
2022/04/19 Javascript