天天看點

從Toast到Pagination: 如何優雅地寫元件

作者:孫輝,美團金融前端團隊成員。15年畢業加入美團,相信技術,更相信技術隻是大千世界裡知識的一種,個人部落格: sunyuhui.com

最近在做的新業務(PC端)裡面,自己寫了幾個元件,既有簡單的

toast

,也有稍微複雜一點的

pagination(分頁)

,在這裡記錄下自己的思考過程,希望對大家有幫助 ~

Toast

實作基本功能

toast

大家已經很熟悉了,其應用場景是:完成某個操作後,給使用者一個提示,一定時間後消失。

基于這個思路,我們很快能實作出一個基于jQuery能用的初版toast。

其中關鍵的代碼隻有幾行:

var elementContent = '<div class="toast-container" style="' + style + '"> <span class="toast-content"> ' + options.content + ' </span> </div>';
$('body').append(elementContent);
//一定時間後消失
var timeout = setTimeout(function() {
    $('.toast-container').remove();
}, options.time*1000);複制代碼
           
使用優化

上面的

toast

已經可以用了,但是我在用的時候,發現有一點不友善,因為我每次調用的時候都需要傳入一個

object對象

,即便我就想用預設的樣式,預設的消失時間。那我能不能就傳入一個我想要給使用者展示的字元串呢?

簡單改造一下:

...
//參數為字元串時,将其作為content屬性值
if(Object.prototype.toString.call(options) === '[object String]') {
    options = $.extend(defaultParam, {
        content: options
    });
} else if(Object.prototype.toString.call(options) === '[object Object]') {
        options = $.extend(defaultParam, options);
} else {
        console.error('only support String or Object for param');
        return;
}
...複制代碼
           

沒有花費太多心思,我們就能讓使用方式變得更加簡單,有了支援多參數形式的toast

去掉第三方依賴

我們在實作

toast

時使用了jQuery這個第三方庫,不得不說,我有時候真心覺得jQuery讓前端門檻降低了不少,但是,業務技術架構并不是一成不變的,有的項目裡并不會使用jQuery。那麼我們就需要提供一個不依賴第三方庫的

toast

隻需要将上面用到jQuery的地方使用原生JS實作就行了,其中有個深拷貝函數$.extend大家需要關注,在這裡我們就不細說了。

完整代碼:原生JavaScript實作的toast

到這裡我們就實作了一個功能比較完整的

toast

,整個代碼很簡單,但我重點想說的是不斷優化的過程:

  • 第一個版本,我們的要求是:能用
  • 第二個版本,我們的要求是:用起來友善
  • 第三個版本,我們的要求是:不依賴第三方庫,能适應不同技術架構

Pagination

我們通過寫

toast

元件的過程,已經了解了如何寫好一個元件,現在我們将這個思路應用到一個稍微有點複雜的元件上:分頁元件。

分頁元件通常是和表格一起用的,使用場景是:将比較大的資料量分頁,每頁展示固定條數的資料,使用者可以通過分頁元件自由選擇檢視任何頁碼的資料,我們梳理下思路,分為這麼幾步:

  1. 通過

    目前頁碼

    總頁數

    展示出頁碼選擇器
  2. 給每一頁綁定點選事件,點選時擷取資料,并且更新頁碼選擇器

其中有個詞是:頁碼選擇器,其實指的就是這個東西:

頁碼選項

如何展示出頁碼選擇器?,我發現有篇部落格講解的思路非常好:如何寫一個簡單的分頁,但是在代碼細節上,對于不熟悉分頁的人不太好了解,我沿用這個思路,具體細節上有修改。

總體思路就是以

目前頁碼

為分割點,分别得到之前和之後的頁碼。

現在我們用代碼初版pagination元件實作這個效果(忽略CSS),得到上圖中的頁碼選擇器,其中關鍵代碼如下,我做了詳細注釋:

/**
 * [getPage 擷取頁碼展示]
 * @param  {[type]} currentPage [目前頁碼]
 * @param  {[type]} totalPage  [頁碼總數]
 * @return {[type]}             [description]
 */
function getPage(currentPage, totalPage) {
    //顯示:第一頁,目前頁,目前頁的前後兩頁,最後一頁
    //以目前頁為分割點,分别得到目前頁前面的頁碼和後面的頁碼
    var pageStr = '<a class="active">' + currentPage + '</a>';
    // 将目前頁前後2頁的頁碼展示出來
    for(var i = 1; i<=2; i++) {
        //得到目前頁前兩頁的的頁碼
        //其中的1指第一頁
        if(currentPage > i+1) {
            pageStr = '<a>' + (currentPage - i) + '</a>' + pageStr;
        }
        //得到目前頁後兩頁的頁碼
        if(currentPage+i < totalPage) {
            pageStr = pageStr + '<a>' + (currentPage + i) + '</a>';
        }
    }
    //得到目前頁前面用...表示的頁碼
    //兩個1分别表示第一頁,和目前頁
    if( currentPage > 2+1+1 ) {
        pageStr = ' ... ' + pageStr;
    }

    //得到上一頁
    if(currentPage > 1) {
        pageStr = '<a class="prePage">上一頁</a><a>1</a>' + pageStr;
    }

    //得到目前頁後面用...表示的頁碼
    //其中1表示最後一頁,目前頁已經在前面計算過,這裡不再計算
    if( currentPage+2+1 < totalPage ) {
        pageStr = pageStr + ' ... ';
    }

    //得到下一頁
    if( currentPage < totalPage ) {
        pageStr = pageStr + '<a>' + totalPage + '</a><a class="lastPage">下一頁</a>';
    }

    return pageStr;
}複制代碼
           

最終我們達到的效果是這樣的:

分頁效果圖

其中

.pagination-container

這個元素是整個分頁元件的容器,直接在html頁面中定義就行。

在上面的效果圖中,我們點選不同的頁碼隻是【跳轉到了不同的頁面】,但在實際項目中,我們通常都是擷取要擷取資料,更新表格。

我們把

bindEvent

函數稍微更新一下

var $this = $(this);
var pageNum;
var currentPage = +$('.pagination-list a.active').text();
if( $this.hasClass('prePage') ) {
    //點選【上一頁】
    pageNum = currentPage - 1;
} else if( $this.hasClass('lastPage') ) {
    //點選【下一頁】
    pageNum = currentPage + 1;
} else {
        pageNum = +$this.text();
}
//擷取表格資料
//執行ajax之前的回調函數,比如加個loading
options.beforeCallback && options.beforeCallback();
$.ajax({
    url:options.url,
    type: options.type,
    data: options.data
}).done(function(res){
    //執行ajax之後的回調函數,比如隐藏loading
    options.afterCallback && options.afterCallback();
    //請求成功後的回調函數
    options.callback && options.callback(res);
    // 更新頁碼
    var pageHtml = getPage(pageNum, options.totalPage);
    $('.pagination-list').html(pageHtml);
}).fail(function(error){
    //執行ajax之後的回調函數,比如隐藏loading
    options.afterCallback && options.afterCallback();
    alert('請求出錯,請重試');
});複制代碼
           

這樣我們就完成了一個比較完整的分頁元件

去掉第三方依賴

在這個元件裡去掉依賴就是去掉對

jQuery

的依賴,我們隻需要把使用jquery方法的地方用原生JavaScript實作一遍就行。總結一下,我們需要自己實作深拷貝函數、事件代理函數、發送Ajax。完整代碼:原生JavaScript實作的分頁元件。

大家可以對比一下,去掉了

jQuery

之後,我們多寫了多少代碼啊。其中有很多細節需要考量,是以我一直覺得寫原生JavaScript才能真正考察一個前端的功底。

總結

ok, 到這裡就差不多了,本文重點不在于告訴大家如何去實作某一個具體的元件(畢竟我們隻涉及到兩個元件),而在于闡述一種觀點:我們寫的元件即便隻是用在我們自己的業務中,也不要滿足于能用就行,一步步優化元件,優化功能,能适應不同技術場景,不僅能提升技術能力,還能讓我們意識到并且實踐如何把一件事從60分做到90分。

最後,團隊為了招聘友善,整了個公衆号,主要是一些招聘資訊,團隊資訊,所有的技術文章在公衆号裡也可以看到,對了,如果你想去美團其他團隊,我們也可以幫你内推哦 ~

二維碼

參考:

如何寫一個簡單的分頁

繼續閱讀