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 相关文章推荐
学习YUI.Ext 第四天--对话框Dialog的使用
Mar 10 Javascript
jquery cookie插件代码类
May 26 Javascript
javascript 处理事件绑定的一些兼容写法
Dec 24 Javascript
再谈javascript图片预加载技术(详细演示)
Mar 12 Javascript
from表单多个按钮提交用onclick跳转不同action
Apr 24 Javascript
JavaScript中的跨浏览器事件操作的基本方法整理
May 20 Javascript
javascript获取网页各种高宽及位置的方法总结
Jul 27 Javascript
JS简单实现无缝滚动效果实例
Aug 24 Javascript
详解用node-images 打造简易图片服务器
May 08 Javascript
动态统计当前输入内容的字节、字符数的实例详解
Oct 27 Javascript
vue-awesome-swiper 基于vue实现h5滑动翻页效果【推荐】
Nov 08 Javascript
Ant Design Vue table中列超长显示...并加提示语的实例
Oct 31 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原创论坛
2006/10/09 PHP
PHP基于DOM创建xml文档的方法示例
2017/02/08 PHP
让getElementsByName适应IE和firefox的方法
2007/09/24 Javascript
网络图片延迟加载实现代码 超越jquery控件
2010/03/27 Javascript
Javascript连接Access数据库完整实例
2015/08/03 Javascript
浅析JavaScript访问对象属性和方法及区别
2015/11/16 Javascript
jquery 动态增加删除行的简单实例(推荐)
2016/10/12 Javascript
vue.js实现条件渲染的实例代码
2017/06/22 Javascript
解决iview打包时UglifyJs报错的问题
2018/03/07 Javascript
redux中间件之redux-thunk的具体使用
2018/04/17 Javascript
vue项目webpack中Npm传递参数配置不同域名接口
2018/06/15 Javascript
vue异步axios获取的数据渲染到页面的方法
2018/08/09 Javascript
vue中render函数的使用详解
2018/10/12 Javascript
Net微信网页开发 使用微信JS-SDK获取当前地理位置过程详解
2019/08/26 Javascript
vue 使用微信jssdk,调用微信相册上传图片功能
2020/11/13 Javascript
JavaScript实现网页下拉菜单效果
2020/11/20 Javascript
python网络编程调用recv函数完整接收数据的三种方法
2017/03/31 Python
python3读取MySQL-Front的MYSQL密码
2017/05/03 Python
Python实现查找匹配项作处理后再替换回去的方法
2017/06/10 Python
Python打印“菱形”星号代码方法
2018/02/05 Python
搭建python django虚拟环境完整步骤详解
2019/07/08 Python
详解python列表(list)的使用技巧及高级操作
2019/08/15 Python
使用python绘制cdf的多种实现方法
2020/02/25 Python
基于Python把网站域名解析成ip地址
2020/05/25 Python
Python如何将模块打包并发布
2020/08/30 Python
linux系统下pip升级报错的解决方法
2021/01/31 Python
编码转换,怎样实现将GB2312编码的字符串转换为ISO-8859-1编码的字符串
2014/01/07 面试题
JDK安装目录下有哪些内容
2014/08/25 面试题
C语言中一个结构不能包含指向自己的指针吗
2012/05/25 面试题
.net软件工程师面试题
2015/03/31 面试题
可靠的数据流传输TCP
2016/03/15 面试题
计算机学生的自我评价分享
2014/02/18 职场文书
小学母亲节活动方案
2014/03/14 职场文书
优秀教师先进个人事迹材料
2014/08/31 职场文书
个人四风对照检查材料
2014/09/26 职场文书
北京英语导游词
2015/02/12 职场文书