JavaScript学习笔记(四)——jQuery插件开发与发布
jQuery插件就是以jQuery库为基础衍生出来的库,jQuery插件的好处是封装功能,提高了代码的复用性,加快了开发速度,现在网络上开源的jQuery插件非常多,随着版本的不停迭代越来越稳定好用,在jQuery官网有许多插件:
一、插件开发基础
1.1、$.extend
在jQuery根命名空间下直接调用的方法可以认为是jQuery的静态方法或属性,常常使用$.方法名来调用,使用$.extend这个静态方法可以完成两个功能:
1.1.1、扩展属性或方法给jQuery
比如我们想给jQuery扩展一个用于快速向控制台写入日志的工具方法log,而不需要使用console.log且在没有console.log的浏览器中使用其它的方法替代:
<script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.extend({ log: function(message) { console.log(message); } }); $.log("控制台日志"); </script>
运行结果:
1.1.2、扩展对象
var x={a:1}; var y={a:2,b:3}; var z={b:4,c:5}; //使用第2个及后的对象扩展前面的对象,如果有则覆盖 $.extend(x,y,z); $.log(x.a+","+x.b+","+x.c);
运行结果:
除了可以扩展对象的属性,方法也可以扩展。
1.2、$.fn.extend
$.fn就是jQuery的原型,$.fn等于jQuery.prototype,$是jQuery的别名。$.fn.extend方法的作用是用于扩展jQuery实例对象,也就是我们从页面中获得的jQuery对象。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>extend</title> </head> <body> <button>按钮1</button> <button>按钮2</button> <input type="text" value="username"/><input type="text" value="password"/> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.fn.extend({ show:function(){ $(this).click(function(){ alert($(this).val()||$(this).html()); }); }, log:function(){ console.log($(this).val()||$(this).html()); } }); $("button").show(); $("input[type=text]").log(); </script> </body> </html>
运行结果:
1.3、$.fn
在上面的示例中我们通过$.fn扩展了jQuery的原型,让所有的jQuery实例对象都得到的扩展的方法,其它也可以直接修改jQuery.prototype来实现,$.fn是jQuery.prototype的简写,源代码如下:
示例代码:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>extend</title> </head> <body> <button id="btn1">按钮1</button> <button>按钮2</button> <input type="text" value="username"/><input type="text" value="password"/> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.fn.bgColor=function(color){ this.css("background",color); } jQuery.prototype.color=function(color){ this.css("color",color); } $("button,input").bgColor("lightGreen"); $("button,input").color("red"); </script> </body> </html>
运行结果:
二、插件开发
2.1、jQuery插件开发基本模式
jQuery插件开发的基本模式需要有一个私有作用域,javascript中默认没有块级作用域,一般通过闭包+IIFE模拟达到类似效果,在1.3中的示例是存在问题,因他它直接暴露在根命名空间下,可以使用如下办法解决:
;(function(method) { method(window, window.document, jQuery); }(function(win, doc, $) { console.log("这是一个私有作用域,IIFE"); $.fn.yourPluginName = function(options) { } }));
前置的分号是为了与插件使用前的代码划清界线;两个IIFE表达式的作用是:一个为了立即执行且形成一个私有的块级作用域,另一个是为了将后置的参数前置,方便看到IIFE执行时带的参数。带参数的目的是为了加快查找的速度,提升性能。
插件的命名:
当然一个好的插件应该有一个容易记住且规范的名称,能见名知意且不与别的插件同名冲突,文件的基本命名规范如下:
jQuery.YourPluginName-1.5.js 源代码
jQuery.YourPluginName-1.5.min.js 压缩代码
2.2、获取上下文
插件方法执行范围内可以直接通过this关键字得到上下文,这里的this就是一个jQuery对象,无需使用$(this)将DOM转换成jQuery对象。
; (function(method) { method(window, window.document, jQuery); }(function(win, doc, $) { console.log("这是一个私有的块级作用域,IIFE"); $.fn.myPlugin = function(options) { this.css({ //this是一个jQuery对象 "color": "blue" }); } })); $("button").myPlugin();
运行结果:
上面的示例中是讲$.fn的形式扩展,如果使用$.fn.extend情况还是一样吗?
; (function(method) { method(window, window.document, jQuery); }(function(win, doc, $) { console.log("这是一个私有的块级作用域,IIFE"); $.fn.extend({ myPlugin2: function(options) { this.css({ //this是一个jQuery对象 "background": "lightgreen" }); } }); })); $("button").myPlugin2();
运行结果:
可见$.fn与$.fn.extend两种方法中的this都是指jQuery对象,这也符合this指向调用他的对象的原则。
2.3、第一个jQuery插件
这是一个Hello World示例,完成一个可以变长的元素插件,指定每次增加长度参数,在指定的HTML元素后增加一个加号点击加号可以将元素加宽指定长度。
jQuery.SuperPlus-1.0.js文件内容如下:
; (function(method) { method(window, window.document, jQuery); }(function(win, doc, $) { $.fn.SuperPlus = function(length) { $.each(this, function(index, obj) { $("<span/>").html("+").css("cursor", "pointer").click(function() { $(obj).width($(obj).width() + length); }).insertAfter(obj); }); } }));
HTML页面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>jQuery插件</title> </head> <body> <button>按钮1</button><br /> <button>按钮2</button><br /> <input type="text" value="username" /><br /> <input type="text" value="password" /> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script src="js/SuperPlus/jQuery.SuperPlus-1.0.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $("button").SuperPlus(10); $("input").SuperPlus(50); </script> </body> </html>
运行结果:
插件中使用each的原因是jQuery选择器选择的内容默认就是一个包装集,中间有多个元素,包装集中含有多个DOM元素,each中的元素就不再是jQuery对象而是一个DOM对象。
练习:$("#div1,#div2").superDiv(50,50,3,"blue");在div1与div2中都添加3个长50,宽50的div,设置背景色为蓝色,点击时div消失,添加的div要求横向排列,间隔为宽高的1/10。
$("#div1,#div2").superDiv({width:50,height:50,color:"red",before:function(){},after:function(){}});
2.4、链式编程
几乎在所有基于“类型”的语言中如果调用一个方法后将对象作为方法参数返回则就会形成链式编程,如:
return $.each(this, function(index, obj) { $("<span/>").html("+").css("cursor", "pointer").click(function() { $(obj).width($(obj).width() + length); }).insertAfter(obj); });
上面的示例中当$.each循环完成后返回this对象,返回的仍然是一个jQuery对象,所以可以继续jQuery编程。
$("button").SuperPlus(10).height(26).width(100).css("color","blue");
运行结果:
2.5、参数与默认值
参数是插件对外部提供的接口,灵活的参数会让插件变得使用方便,这里主要从3个方面来讲参数:
2.5.1、默认值
最好为每个参数提供默认值,有缺省的默认值会减少错误,如:
$("input").SuperPlus();
这样没有提供参数,点击时没有任何效果,也没有错误提示,应该给参数增加一个默认值,如:
; (function(method) { method(window, window.document, jQuery); }(function(win, doc, $) { $.fn.SuperPlus = function(length) { length=length||3; return $.each(this, function(index, obj) { $("<span/>").html("+").css("cursor", "pointer").click(function() { $(obj).width($(obj).width() + length); }).insertAfter(obj); }); } }));
length=length||3意思是如果用户没有提供参数则length的值为3。
2.5.2、参数对象
上面的示例中只有一个参数,直接作为方法的参数没有任何问题,但如果参数非常多,且都要默认值,处理就很麻烦,最好的办法是使用参数对象:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>jQuery插件</title> </head> <body> <button>按钮1</button><br /> <button>按钮2</button><br /> <input type="text" value="username" /><br /> <input type="text" value="password" /> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script src="js/SuperPlus/jQuery.SuperPlus-1.0.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $("button").SuperPlus({ length: 100 }); $("input").SuperPlus({ color: "red" }); </script> </body> </html>
插件:
; (function(method) { method(window, window.document, jQuery); }(function(win, doc, $) { $.fn.SuperPlus = function(options) { //默认参数 var setting={ length:3, color:"blue" }; //使用用户的参数覆盖默认参数 $.extend(setting,options); return $.each(this, function(index, obj) { $("<span/>").html("+").css("cursor", "pointer").css("color",setting.color).click(function() { $(obj).width($(obj).width() + setting.length); }).insertAfter(obj); }); } }));
运行结果:
2.5.2、参数类型
参数对象的类型可是属性,方法,对象,数组等多种形式,也可以使用回调方法,比如这里我们要为插件增加一个执行后的事件changeAfter,当目标元素被修改后触发。
修改后的插件:
; (function(method) { method(window, window.document, jQuery); }(function(win, doc, $) { $.fn.SuperPlus = function(options) { //默认参数 var setting={ //每次增加的长度 length:3, //加号的颜色 color:"blue", //回调事件,点击后执行 changeAfter:null }; //使用用户的参数覆盖默认参数 $.extend(setting,options); return $.each(this, function(index, obj) { $("<span/>").html("+").css("cursor", "pointer").css("color",setting.color).click(function() { $(obj).width($(obj).width() + setting.length); //如果用户指定了回调事件 if(setting.changeAfter) { //执行用户带入的方法将当前对象作为参数带出 setting.changeAfter($(obj)); } }).insertAfter(obj); }); } }));
HTML页面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>jQuery插件</title> </head> <body> <button>按钮1</button><br /> <button>按钮2</button><br /> <input type="text" value="username" /><br /> <input type="text" value="password" /> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script src="js/SuperPlus/jQuery.SuperPlus-1.0.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $("button").SuperPlus({ length: 100, changeAfter:function(obj){ //如果长度超过300则变成100 if($(obj).width()>300){ $(obj).width(100); } } }); $("input").SuperPlus({ color: "red" }); </script> </body> </html>
运行结果:
2.6、命名空间与面向对象
给插件正确的命名空间是非常重要要的,这样可以避免和其它插件或代码冲突,可以使用闭包,模块等方法实现。按照jQuery的约定,只使用一个命名空间。
在插件中尽量只使用jQuery.fn下的一个名称,名称越多冲突的可能性就越大,成熟的插件会做冲突处理,就像多个jQuery库共存的道理是一样的。
另外对于复杂的插件尽量将代码封装,不要使用函数式的编程方式,可以通过构造函数,IIFE,原型继承等方法达到目的。
2.7、插件与关联的CSS
如果插件中会使用到大量的CSS样式,则不推荐使用jQuery的方式去添加样式,应该做成一个插件单独使用CSS文件,命名可以参考如下方式:
jQuery.YourPluginName-1.5.css 样式
样式一定要注意不要修改与插件无关的元素,甚至连CSSReset都不应该有,推荐使用一个相对不易冲突的名称所有的样式都在该类样式下,注意ID样式是不允许重复的,因为要考虑一个页面中同时使用多个插件实例的情况。
2.8、压缩与混淆
压缩为是了减小插件的体积。JavaScript的速度分为两种,下载速度和执行速度。要想JavaScript的下载速度快,就需要尽量减少JavaScript文件的大小,另外,把多个JavaScript文件合并成一个也能减少服务器的响应次数而加快网页下载。
可以通过对javascript的变量名称和过程名称进行编码,从而起到混淆JavaScript代码的作用,保护您的劳动成果。
其实高强度压缩就是很好的混淆,如将变量名称都换成a,b,c等没有意义的字符。
2.8.1、在线压缩与混淆
网络中有很多提供在线压缩的工具,如果仅是少量,临时的压缩在线压缩与混淆是非常方便的,如:
可以搜索“javascript在线压缩”就会有很多网站提供在线,免费的服务。
2.8.2、工具压缩与混淆
可以使用GUI工具也可以使用命令行,GUI工具操作非常简单,这里主要讲基于node.js的工具UglifyJS的使用。
UglifyJS是UglifyJS2的前身,是一个Javascript开发的通用的语法分析、代码压缩、代码优化的一个工具包。UglifyJS是基于Nodejs环境开发,支持CommonJS模块系统的任意的Javascript平台。
UglifyJS2的安装:
npm install uglify-js -g
合并压缩:
uglifyjs a.js b.js c.js -o d.js
将a.js、b.js与c.js文件合并后压缩到d.js中
混淆:
在原参数上增加-m可以将变量名称替换成a,b,c等没有意义的变量。
压缩的办法有多个还可以使用IDE中的插件:
命令参数详细:
–source-map [string],生成source map文件。
–source-map-root [string], 指定生成source map的源文件位置。
–source-map-url [string], 指定source map的网站访问地址。
–source-map-include-sources,设置源文件被包含到source map中。
–in-source-map,自定义source map,用于其他工具生成的source map。
–screw-ie8, 用于生成完全兼容IE6-8的代码。
–expr, 解析一个表达式或JSON。
-p, –prefix [string], 跳过原始文件名的前缀部分,用于指定源文件、source map和输出文件的相对路径。
-o, –output [string], 输出到文件。
-b, –beautify [string], 输出带格式化的文件。
-m, –mangle [string], 输出变量名替换后的文件。
-r, –reserved [string], 保留变量名,排除mangle过程。
-c, –compress [string], 输出压缩后的文件。
-d, –define [string], 全局定义。
-e, –enclose [string], 把所有代码合并到一个函数中,并提供一个可配置的参数列表。
–comments [string], 增加注释参数,如@license、@preserve。
–preamble [string], 增加注释描述。
–stats, 显示运行状态。
–acorn, 用Acorn做解析。
–spidermonkey, 解析SpiderMonkey格式的文件,如JSON。
–self, 把UglifyJS2做为依赖库一起打包。
–wrap, 把所有代码合并到一个函数中。
–export-all, 和–wrap一起使用,自动输出到全局环境。
–lint, 显示环境的异常信息。
-v, –verbose, 打印运行日志详细。
-V, –version, 打印版本号。
–noerr, 忽略错误命令行参数。
2.9、发布插件
2.9.1、将插件发布到GitHub上
首先你需要将插件代码放到GitHub上创建一个Service Hook,这样做的目的是你以后更新的插件后,jQuery可以自动去获取新版本的信息然后展示在插件中心的页面上。
先在github上创建一个仓库:
将插件提交到GitHub中:
2)、制作清单文件
然后需要制作一个JSON格式的清单文件,其中包括关于插件的基本信息,具体格式及参数可以在jQuery官网插件发布指南页面了解到,这里提供一个示例文件,是我之前写的一个jQuery插件SlipHover
这里参考jQuery官网的设置指南:http://plugins.jquery.com/docs/publish/
The jQuery Plugins Registry will look in the root level of your repository for any files named *.jquery.json
. You will want to create *yourplugin*.jquery.json
according to the package manifest specification. Use an online JSON verifier such as JSONlint to make sure the file is valid. You are now ready to publish your plugin!
在插件项目的根目录下添加一个名称为“插件名.jquery.json”的清单文件;清单文件可以参考package manifest specification,清单文件是一个json格式的文件,编写好之后可以使用JSONlint验证格式,确保没有错误。
SuperPlus.jquery.json
{ "name": "SuperPlus", "title": "jQuery SuperPlus", "description": "jQuery plugin for HTML Element.", "keywords": [ "super", "plus", "element" ], "version": "1.0.0", "author": { "name": "South IT College class NO1 of UED", "url": "https://github.com/zhangguo5/SuperPlus/blob/master/AUTHORS.txt" }, "maintainers": [ { "name": "South IT College class NO1 of UED", "email": "2676656856@qq.com", "url": "http://best.cnblogs.com" } ], "licenses": [ { "type": "MIT", "url": "https://github.com/jquery/jquery-color/blob/2.1.2/MIT-LICENSE.txt" } ], "bugs": "https://github.com/zhangguo5/SuperPlus/issues", "homepage": "https://github.com/zhangguo5/SuperPlus", "docs": "https://github.com/zhangguo5/SuperPlus", "download": "https://github.com/zhangguo5/SuperPlus/tree/master/dest", "dependencies": { "jquery": ">=1.5" } }
编辑完成清单文件可以在线验证:
2.9.2、GitHub Service Hook
1)、在项目的根目录下,点击右上角的设置“settings”->"Integrations&services"
2)、在Add Service中输入jQuery Plugin
接下来会提示登录,输入密码,登录成功后激活就完成。
搜索:
练习:
请打造一个属于自己的jQuery插件,并发布到github中,写出使用的帮助说明。插件类型可以是:弹出层、分页控件、验证、图片轮播、组织结构图、超级下拉列表、图表、工具库等。
弹出层参考:
http://www.jq22.com/yanshi10917
http://www.jq22.com/jquery-info10177
http://www.jq22.com/jquery-plugins%E5%BC%B9%E5%87%BA%E5%B1%82-1-jq
分页控件:
https://github.com/gbirke/jquery_pagination
简单参考:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>jQuery插件</title> <style type="text/css"> .pager { width: 800px; margin: 0 auto; } .pager .pageItem { display: inline-block; padding: 5px 8px; background: dodgerblue; margin: 3px; } .pager a { color: white; text-decoration: none; } .pager a:hover { background: orangered; } </style> </head> <body> <div id="div1"> </div> <div id="div2"> </div> <div id="div3"> </div> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script src="js/SuperPlus/jQuery.SuperPlus-1.0.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.fn.superPager = function(options) { var settings = { total: 100, record: 10, callback: null }; $.extend(settings, options); var pager = $("<div/>").addClass("pager").appendTo(this); for(var i = 1; i <= Math.ceil(settings.total / settings.record); i++) { $("<a href='#'/>").html(i).addClass("pageItem").click(function(p) { return function() { if(settings.callback) { settings.callback(settings, p); } return false; }; }(i)).appendTo(pager); } return this; } $("#div1").superPager({ callback: function(opt, index) { alert(opt.total + "," + opt.record + "," + index); } }); $("#div2").superPager({ total:300, record:20, callback: function(opt, index) { alert(opt.total + "," + opt.record + "," + index); } }); $("#div3").superPager({ total:10, record:2, callback: function(opt, index) { alert(opt.total + "," + opt.record + "," + index); } }); </script> </body> </html>
验证:
http://www.jq22.com/jquery-plugins%E9%AA%8C%E8%AF%81-1-jq
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>jQuery插件</title> <style type="text/css"> .error { color: red; } </style> </head> <body> <form action="" method="post" id="form1"> <p> 名称:<input type="text" required="required" data-regex="^[\u4e00-\u9fa5]{2,10}$" data-regexmsg="只允许输入2-10位的中文" data-msg="名称必须填写"/> </p> <p>价格:<input type="text" required="required" data-regex="^\d{1,5}$"/></p> <p>详细:<input type="text" required="required" /></p> <input type="submit" value="提交" /> </form> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.fn.superVerify = function() { this.find("[required=required]").each(function(index, obj) { var msg = $(this).data("msg") || "请填写该字段"; var fun=function(){ $(this).next("span.error").remove(); if(!$(this).val()){ $("<span/>").html(msg).addClass("error").insertAfter(this); } } $(this).change(fun).focus(fun).blur(fun); }); this.find("[data-regex]").each(function(index, obj) { var msg = $(obj).data("regexmsg") || "格式错误"; var fun=function(){ $(obj).next("span.error").remove(); var exp=new RegExp($(obj).data("regex")); console.log(exp+","+$(obj).data("regex")); console.log(exp.test($(this).val())); console.log($(this).val()); if($(this).val()&&!exp.test($(this).val())){ $("<span/>").html(msg).addClass("error").insertAfter(this); } } $(this).change(fun).focus(fun).blur(fun); }); return this; } $("#form1").superVerify(); //var reg=new RegExp("^\\d{1,5}$"); //alert(reg.test("123123")); </script> </body> </html>
图片轮播:
比如:
<div id="adv"> <a href="x.html"><img src="p1.jpg"/></a> <a href="y.html"><img src="p2.jpg"/></a> <a href="z.html"><img src="p3.jpg"/></a> </div>
$("#adv").superSwaper({ speed:3000 });
超级下拉列表
$("#select1").superSelect(["中国","美国","日本"]);
$("#select1").superSelect({data:[{"text":"中国","value":"01"},{"text":"美国","value":"02"},{"text":"日本 ","value":"03"}]});
$("#select1").superSelect({url:"service/getAllCountry.do","text":"text","value":"id"});
$("#select1").superSelect({data:"省"});
$("#select1").superSelect({data:"学历"});
$("#select1").superSelect({data:"性别"});
http://www.jq22.com/