起因

如果大家平时做过一些前端开发方面的工作,一定会有这样的体会:页面需要某种效果或者插件的时候,我们一般会有两种选择:

1、上网查找相关的JS插件,学习其用法

2、自己造轮子,开发插件。

寻找存在的插件

第一种做法,上网查找JS插件

这种方式如果是有刚好符合项目需求的插件,那是非常幸运的事了。但是我相信大部分情况下,我们找到的插件会有下面的几个问题:

(1)UI定制:很多插件提供的UI和我们的项目设计风格完全不搭,可能写好的html和css不符合插件使用的方式,结果我们还要去修改html和css来适应插件的用法。

(2)学习成本:如果是比较复杂的插件,存在着一个学习成本的问题,要去学习这个插件怎么使用。

(3)插件不符合需求:我们找到的插件并不完全保证符合我们项目的需求,这个时候你可能要去修改它的代码来支持项目需求,这也是可能存在的问题

(4)插件功能太大而全:假设你的项目需要一个简单的轮播插件,结果找到一个很牛逼的轮播插件,各种酷炫的效果,而且刚好也能使用,但是这个插件的体积和一个js库的体积差不多,而如果自己写效果的话,其实只要几十行代码就可以搞定了,这时候引入这个插件是否太过多余了。

这是使用js插件可能存在的一些问题,当然具体情况具体分析,我也并非不使用已经写好的js插件,毕竟有些插件是经过了时间考验的,使用起来更有益于项目的进行。如果是下面几种情况,我会考虑使用已经存在的js插件:

(1)复杂的功能:比如文件上传,批量上传,进度显示等等,比如HTML编辑器

(2)项目工期紧急,对性能要求不高的场景

(3)js插件刚好符合项目的需求

自己造轮子

第二种做法,自己造轮子开发插件

自己写插件主要有下面几个问题:

(1)开发插件需要时间,可能拖延项目工期,如果工期紧急不建议选用这种方式

(2)自己造的轮子未必有现有轮子的好用,要考虑到队友是否适用

(3)需要比较高的开发水平

如果平时项目不紧急的话,我会考虑自己造轮子,主要有几个好处:

(1)完全符合项目需求,这一条是显而易见的,因为完全针对项目来开发的插件

(2)知根知底,容易修改,插件完全是自己开发的,项目有什么需求变化完全可以灵活应对

(3)轻量级,因为我们不像其他开源插件要应对那么多种需求,所以我们自己的轮子也只需要符合自己的车,不需要很多变化,相对来说,变化少功能少,代码也会少。

(4)对个人能力是一个很大的锻炼,不要重复造轮子这是在程序员中广为流传的一句话,这也成为很多人偷懒的一个借口,但是我们不应该以此为借口来阻碍自己的前进的脚步。造过轮子的同学应该深有体会,造过一个轮子,远比你使用别人写的100个插件的收获还要多,我们的轮子可以不在项目中使用,但这是一种效率极高的学习方式,强烈推荐。

如何开发一个轻量级的适用性强的插件

怎么开发一个适应性强的且轻量的插件呢?所谓适用性强,简单地说就是有几点:

1、对UI限制越少越好,最好没有

2、不提供太多功能,只提供简单的api,让使用者易于扩展

我们举个例子,假设我们要开发一个jQuery分页插件,关于jQuery插件开发教程,请参考 jQuery插件开发 。

确定需求

确定需求是开发插件的第一步。要开发一个轻量级的分页插件,我们还是用从插件最基本的需求开始说起,分页插件最基本的需求是什么呢,无非就是页码显示,页码之间的切换,所以,我们的插件要围绕着这基本需求开始,而暂时不要考虑其他可能存在的需求。

确定插件html和css

确定好插件的需求后,第二步就是插件的UI,也就是html和css。

假设基本的ui如下:

看到上面的基本ui,不知道大家会想到什么样的html结构。对于我们开发人员来说,html和css要越简单越好,所以最基本的html结构无非就是a标签和span标签的混合,有的同学可能会想到使用ul,li标签,但这其实增加的复杂度,得不偿失。我们编写html代码如下:

<div class="pager"><span class="flip noPage">上一页</span><span class="curPage">1</span><a page="1" href="javascript:;">2</a><a page="2" href="javascript:;">3</a><a page="3" href="javascript:;">4</a><span>...</span> <a href="javascript:;" page="8">9</a> <a page="1" href="javascript:;" class="flip">下一页</a></div>

这是最基本html代码结构,包含了分页插件的容器div.pager,当前页span.curPage,其他页码a标签,上一页,下一页等按钮。

接着是css代码,主要当前页标签,其他页面标签,上一页下一页,鼠标悬停在按钮上等几种样式,编写如下:

.pager { display: inline-block; font: 12 px/21px "宋体"; margin-top: 20px; }.pager a, .pager .flip, .pager .curPage { border: 1px solid #e3e3e3; display: inline-block; height: 22px; line-height: 22px; text-align: center; }.pager a { background: none repeat scroll 0 0 #fff; color: #010101; text-decoration: none; width: 26px; }.pager a:hover { background: none repeat scroll 0 0 #f1f1f1; }.pager .noPage { color: #a4a4a4; }.pager .curPage { background: none repeat scroll 0 0 #49abde; color: #ffffff; width: 26px; }.pager .flip { width: 56px; }

编写js代码

写好基本的html和css,接下来最关键的就是js代码了。首先我们搭建好jQuery插件开发的基本形式:

; (function ($, window, document, undefined) {"use strict";var defaults = { pageIndex: 0, pageSize: 6, itemCount: 50, maxButtonCount: 7, prevText: "上一页", nextText: "下一页", buildPageUrl: null, onPageChanged: null }; 

 $.fn.pager = function (options) { options = $.extend(defaults, options || {}); }})(jQuery, window, document);

这里主要提供了一些可选参数的默认值,比如页码默认为0,每页数量6条等等。

接着我们来考虑一下分页插件的思路:

1、设置当前页码为0,表示第一页

2、生成分页插件的html代码

3、修改页码,再生成html代码

基于这个思路,我们编写代码如下:

; (function ($, window, document, undefined) {"use strict";var defaults = { pageIndex: 0, pageSize: 6, itemCount: 50, maxButtonCount: 7, prevText: "上一页", nextText: "下一页", buildPageUrl: null, onPageChanged: null };

function Pager($ele, options) {this.$ele = $ele;this.options = options = $.extend(defaults, options || {});this.init(); } Pager.prototype = { constructor: Pager, init: function () {this.renderHtml();this.bindEvent(); }, renderHtml: function () {var options = this.options;

 options.pageCount = Math.ceil(options.itemCount / options.pageSize);var html = [];

//生成上一页的按钮 if (options.pageIndex > 0) { html.push('<a page="' + (options.pageIndex - 1) + '" href="' + this.buildPageUrl(options.pageIndex + 1) + '" class="flip">' + options.prevText + '</a>'); } else { html.push('<span class="flip noPage">' + options.prevText + '</span>'); }

 //这里是关键//临时的起始页码中间页码,当页码数量大于显示的最大按钮数时使用 var tempStartIndex = options.pageIndex - Math.floor(options.maxButtonCount / 2) + 1;

 //计算终止页码,通过max计算一排按钮中的第一个按钮的页码,然后计算出页数量var endIndex = Math.min(options.pageCount, Math.max(0, tempStartIndex) + options.maxButtonCount) - 1;var startIndex = Math.max(0, endIndex - options.maxButtonCount + 1);

// 第一页 if (startIndex > 0) { html.push("<a href='" + this.buildPageUrl(0) + "' page='" + 0 + "'>1</a> "); html.push("<span>...</span>"); }

 //生成页码按钮for (var i = startIndex; i <= endIndex; i++) {if (options.pageIndex == i) { html.push('<span class="curPage">' + (i + 1) + '</span>'); } else { html.push('<a page="' + i + '" href="' + this.buildPageUrl(options.pageIndex + 1) + '">' + (i + 1) + '</a>'); } }

// 最后一页 if (endIndex < options.pageCount - 1) { html.push("<span>...</span> "); html.push("<a href='" + this.buildPageUrl(options.pageCount - 1) + "' page='" + (options.pageCount - 1) + "'>" + options.pageCount + "</a> "); }

 //生成下一页的按钮if (options.pageIndex < options.pageCount - 1) { html.push('<a page="' + (options.pageIndex + 1) + '" href="' + this.buildPageUrl(options.pageIndex + 1) + '" class="flip">' + options.nextText + '</a>'); } else { html.push('<span class="flip noPage">' + options.nextText + '</span>'); }

this.$ele.html(html.join("")); }, bindEvent: function () {var that = this;that.$ele.on("click", "a", function () {that.options.pageIndex = parseInt($(this).attr("page"), 10);that.renderHtml();that.options.onPageChanged && that.options.onPageChange(that.options.pageIndex); }) }, buildPageUrl: function () {if ($.isFunction(this.options.buildPageUrl)) {return this.options.buildPageUrl(pageIndex); }return "javascript:;"; }  };

 $.fn.pager = function (options) { options = $.extend(defaults, options || {});

return new Pager($(this), options); }

})(jQuery, window, document);

这段代码中有两个关键点要记住:

(1)html代码的生成,由于页码可能太多,需要隐藏部分页码,所以我们要生成一个省略号表示被隐藏的页码,通过maxButtonCount来表示最多的页码按钮

(2)事件绑定,每次页码改变都会重新生成html,我们采用事件代理的方式,提高了性能,也不用重复绑定事件

这样一个最基本的分页插件已经可以了。

但是这样足够了吗?

假设我们需要支持输入页码直接跳转的功能,那该如何呢,是否需要修改原有的html结构和css?前面我们说到,开发一个插件要围绕最基本的需求开始,那对于这些潜在的可能存在的需求又该如何处理呢。

我的解决方案是这样的,提供简单的api,不提供UI,完全由用户自定义。

我们在上面的代码增加三个api:getPageIndex,setPageIndex和setItemCount,分别表示获取当前索引,设置当前索引,设置项目总数量。代码如下:

getPageIndex: function () {return this.options.pageIndex;},setPageIndex: function (pageIndex) {this.options.pageIndex = pageIndex;this.renderHtml();},setItemCount: function (itemCount) {this.options.pageIndex = 0;this.options.itemCount = itemCount;this.renderHtml();}

完整版代码请查看 jquery.page.js

提供了这三个api,假设用户需要跳转页码的功能,可以直接使用setPageIndex方法来跳转,UI完全由用户自定义,插件本身只专注基本功能,不干涉其它。

大家可以查看DEMO

整个插件的代码已经放在我的github上,有兴趣的同学可以点击查看 github

总结

最后,我整理一下我开发一些js插件的思路:

1、专注最基本的需求本身,暂时不考虑可能潜在的需求

2、尽量不提供或少提供UI,减少对使用者的限制

3、考虑可能潜在的需求,提供api,潜在的需求完全由用户自定义

这是我在编写js插件时,考虑如何轻量化并且适用性强的一些想法,欢迎大家交流!

转载于:https://www.cnblogs.com/carr-css/p/6187008.html

js插件开发的一些感想和心得-引狼狼的蓝胖子相关推荐

  1. [js插件开发教程]一步步开发一个可以定制配置的隔行变色小插件

    隔行变色功能,不用js,直接用css伪类就可以做,这个实例可以作为js插件开发很好的入门级实例.本文实现的隔行变色包括以下功能: 1,支持2种常用结构共存( div元素 和 表格类型 ) 2,一个页面 ...

  2. [js插件开发教程]定制一个手风琴插件(accordion)

    本文带来一个垂直方向的手风琴插件开发,可以定制的功能如下: contentClass : 'panel', //面板样式navClass : 'nav', //导航样式activeClass : 'a ...

  3. autojs入门视频教程,Auto.js Pro安卓全分辨率免ROOT引流脚本开发零基础到进阶教程,飞云脚本学院2019年全力打造高清品质,不容错过的年度大作

    课程大纲(更新中-) 基础课程 [已完成]01安装Visual Studio Code(时长9分33秒)在线观看 [已完成]02安装Auto.js Pro(时长5分48秒)在线观看 [已完成]03解读 ...

  4. ae文件怎么变成html,AE转JS动画,lottie.js和bodymovin的简易使用心得

    PS:描述不清或者描述错误请见谅... AE动画转换JS动画使用了lottie.js库 制作流程: 1.设计师使用PS或者AI制作静态设计稿: 2.设计师对设计稿图层进行分类 3.导入AE制作 4.A ...

  5. Vue.js 插件开发详解

    前言 随着 Vue.js 越来越火,Vue.js 的相关插件也在不断的被贡献出来,数不胜数.比如官方推荐的 vue-router.vuex 等,都是非常优秀的插件.但是我们更多的人还只停留在使用的阶段 ...

  6. JS在与lua的交互心得

    最近在写一个项目,前端技术使用的是Vue,在与lua的交互过程,是通过一个公共JS,前端调用公共js的方法给lua发送命令,lua接到命令,去执行一些方法,然后又通过回调返回到了前端,由于是第一次写这 ...

  7. 用node.js 搭建的博客程序心得(node.js实战读书笔记1)

    学习node已经有一段时间了,之前把了不起的node.js看完了,基本算了解了一些node的基本的用法还有一些概念了,然后就开始看第二本node.js实战,第一章就是搭建一个博客程序.但是不得不吐槽一 ...

  8. 关于grumble.js气泡插件的使用小心得

    最近根据项目需要需要使用到一个气泡提示框,在网上寻找了一下 找到grumble.js这个简单实用的东西,但是使用上感觉跟项目有点冲突,就是加载的时候 我们的主模块先隐藏了,grumble.js又是绝对 ...

  9. 计算机应用技术新生入学感想,入学心得体会范文

    荀子曰:学不可以已.人的一生就是一个不断学习不断自我完善的过程.入学是人生必经的一件事.下面是学习啦带来的入学的心得体会,仅供参考. 入学心得体会一: 高一新生入学教育 大家下午好!首先,我代表高一( ...

最新文章

  1. MySQL中同一时候存在创建和上次更新时间戳字段解决方法浅析
  2. 3.2.1 造成误差的原因分析
  3. VS VC 读取 INI文件
  4. LeetCode 801. 使序列递增的最小交换次数(动态规划)
  5. 求特殊方程的正整数解
  6. Oracle中日期和时间字段的日常使用
  7. 不用 SWIG,Go 使用 C++ 代码的方式
  8. 吴恩达机器学习学习笔记第六章:机器学习中的线性代数操作python3版(含numpy、panda库的使用)
  9. linux ftp mysql_linux搭建ftp服务——未连接mysql数据库的做法
  10. java mvc mvvm_从MVC到MVVM(为什么要用vue)
  11. mysql 线程内存 回收_【MySQL】InnoDB后台线程与内存缓存池
  12. unity package 包下载不下来
  13. 双向三相交错并联DC-DC变换器
  14. 【无标题】2022电工(技师)操作证考试题及在线模拟考试
  15. 受力分析软件_基于非线性分析的建筑结构设计与优化
  16. 谷梁科技多元化一卡通系统应用
  17. js 拖拽上传文件及文件夹
  18. 如何用c语言程序预测身高,测身高的C语言程序.doc
  19. Android studio编程常用控件
  20. 《关键对话——掌握关键对话》读书笔记(二)

热门文章

  1. Value of type java.lang.String cannot be conver...
  2. azul zing_微软和Azul将OpenJDK提升到Azure云中
  3. Objective-C分类 (catagory)
  4. 全球网络波动实时监控系统--网动仪
  5. 翁恺java考试卷_翁恺java期末考试题
  6. 美团一点都不美,我再也不想给美团打工了
  7. Tensorflow2.* 加载和预处理数据之用 tf.data 加载 Numpy数据(2)
  8. 在MT终端模拟器中使用Termux命令
  9. vue中prop验证、类型检查及注意事项
  10. docker MySQL8