起因
如果大家平常都做過一些前端開發方面的工作,一定會有這樣的體會:頁面需要某種效果或插件的時候,我們通常會有兩種選擇:
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如下:
Seeing the basic UI above, I don’t know what kind of html structure you will think of. For us developers, html and css should be as simple as possible, so the most basic html structure is nothing more than a mixture of a tag and span tag. Some students may think of using ul and li tags, but this actually increases the complexity. Degree, the gain outweighs the loss. We write the html code as follows:
<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>
This is the most basic html code structure, including the container div.pager of the paging plug-in, the current page span.curPage, other page number a tags, previous page, next page and other buttons.
Next is the css code, mainly the current page tag, other page tags, previous page next page, mouse hovering on the button, etc. Several styles are written as follows:
.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; }
Write js code
After writing the basic html and css, the next most critical thing is the js code. First, we set up the basic form of jQuery plug-in development:
; (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);
Here mainly provides the default values of some optional parameters, such as the default page number is 0, the number of items per page is 6, etc.
Next, let’s consider the idea of paging plug-in:
1. Set the current page number to 0, which means the first page
2. Generate the html code of the paging plug-in
3. Modify the page number and generate html code
Based on this idea, we write the code as follows:
; (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);
There are two key points to remember in this code:
(1) Generation of html code. Since there may be too many page numbers, some page numbers need to be hidden, so we need to generate an ellipsis to represent the hidden page number, and use maxButtonCount to represent the most page number buttons
(2) Event binding, html will be regenerated every time the page number changes. We use event proxy to improve performance and eliminate the need to repeatedly bind events
Such a basic paging plug-in is enough.
But is this enough?
Suppose we need to support the function of jumping directly by entering a page number. What should we do? Do we need to modify the original HTML structure and CSS? We mentioned earlier that developing a plug-in should start with the most basic requirements, so how to deal with these potential requirements.
My solution is this, providing a simple API, no UI, and completely customized by the user.
We add three APIs to the above code: getPageIndex, setPageIndex and setItemCount, which respectively represent getting the current index, setting the current index, and setting the total number of items. The code is as follows:
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(); }
For the full version of the code, please view jquery.page.js
These three APIs are provided. If the user needs the function of jumping to the page number, he can directly use the setPageIndex method to jump. The UI is completely customized by the user. The plug-in itself only focuses on basic functions and does not interfere with others.
You can view DEMO
The entire plug-in code has been placed on my github. Interested students can click to view github
Summary
Finally, I will sort out my ideas for developing some js plug-ins:
1. Focus on the most basic needs themselves and ignore possible potential needs for the time being
2. Try to provide no or as little UI as possible to reduce restrictions on users
3. Consider possible potential needs and provide APIs. Potential needs are completely customized by users
These are some of my thoughts on how to be lightweight and highly applicable when writing js plug-ins. Welcome everyone to share!
This article’s address: http://luopq.com/2016/02/04/think-js-plugin/, please indicate
when reprinting