Vue實戰(zhàn)(六)通用Table組件

本文是Vue實戰(zhàn)系列的第六篇文章,主要介紹Falcon項目中通用 Table 組件的開發(fā)和使用 。Falcon項目地址:https://github.com/thierryxing/Falcon

通用 Table 組件 TableBox

隨著業(yè)務(wù)的發(fā)展和功能的增多,我們發(fā)現(xiàn)不少頁面都具備相似的功能,這里舉幾個比較俗的例子:可以多選的下拉菜單,帶輸入的對話框雄可,日期選擇器等等,于是我們會想辦法將這些共有的功能抽取成一個個公共組件缠犀,以便能夠在不同的頁面或業(yè)務(wù)中使用数苫。

對于一個中后臺類的項目,一個比較常見的展示形式就是Table了夭坪,相信大家都不陌生文判,如下圖所示:

DingTalk20171120203642.png

一個Table通常由如下幾個部分構(gòu)成:

  • 標(biāo)題
  • 表頭
  • 表格內(nèi)容
  • 分頁控件

除此之外,由于 Table 中的數(shù)據(jù)往往都是從后端獲取的室梅,所以這個包含 Table 的頁面還需要發(fā)起一個請求戏仓,并且將最終的內(nèi)容渲染在表格之內(nèi),請求的過程由于是異步的亡鼠,所以需要給用戶展示一個 Loading 動畫赏殃;當(dāng)請求數(shù)據(jù)為空時,需要顯示一個占位的空元素控件间涵。

在 Falcon 項目的實踐中仁热,我們發(fā)現(xiàn),每一個頁面中的 Table 除了行數(shù)勾哩,列數(shù)抗蠢,及單元格的內(nèi)容不同之外举哟,其它的地方,包括樣式迅矛,分頁及數(shù)據(jù)處理邏輯都是一樣的妨猩,每次新增一個這樣的頁面無非就是拷貝粘貼了,那么在這種情況下秽褒,我們抽取出了一個通用的 Table 組件壶硅,取名為:TableBox。

說到這里插一個題外話:

我們在編寫前端代碼的過程中有時會感到困惑销斟,究竟什么時候應(yīng)該將模塊抽取出來庐椒,而不是寫死在頁面中呢?

關(guān)于這個問題我認(rèn)為蚂踊,如果一個功能只出現(xiàn)在了一個或兩個頁面中约谈,往往是沒有必要處理的,因為一兩個功能的重復(fù)還不足以說明問題悴势,也很難看出其中的共性窗宇,如果強(qiáng)行抽取的話,反而會增加維護(hù)的負(fù)擔(dān)特纤;如果出現(xiàn)的地方超過了兩處,那么我們就需要考慮將這個功能進(jìn)行抽取了侥加,我也常常和 Team 的人說:“如果一個功能你拷貝粘貼了1次捧存,沒關(guān)系,不用糾結(jié)担败;2次的話昔穴,就得考慮其復(fù)用性和組件化了”。

當(dāng)然提前,以上內(nèi)容只適用于那些初期開發(fā)過程中無法預(yù)測未來變化的項目吗货,如果剛開始產(chǎn)品設(shè)計的時候,就能夠充分的預(yù)見和考慮未來的業(yè)務(wù)發(fā)展狈网,并且給出詳細(xì)的產(chǎn)品及UI設(shè)計方案宙搬,那么就另當(dāng)別論了。

回到我們的主題拓哺,抽取這個 TableBox 其實并不是一氣呵成的勇垛,而是在業(yè)務(wù)迭代中,不斷地發(fā)現(xiàn)新的場景士鸥,新的問題闲孤,帶著這些問題我們不斷的優(yōu)化 TableBox,最終達(dá)到一個較為完整的狀態(tài)烤礁。這也符合 Vue 本身漸進(jìn)式的理念讼积。接下來我們花些時間肥照,一起探討一下這些場景和問題:

場景&問題1:Table 數(shù)據(jù)獲取和渲染

我們發(fā)現(xiàn),對于不同的頁面勤众,只要帶有 Table 的舆绎,其數(shù)據(jù)都需要從遠(yuǎn)端服務(wù)器獲取,一般情況下决摧,我們會在每個業(yè)務(wù)中都去寫一下這個網(wǎng)絡(luò)獲取數(shù)據(jù)的邏輯亿蒸,但是,如果仔細(xì)想想掌桩,你就會發(fā)現(xiàn)边锁,其實這類列表數(shù)據(jù)獲取和處理的邏輯都是一樣的。所以針對這個情況波岛,我們只要和后端協(xié)商好列表相關(guān)的統(tǒng)一 API 數(shù)據(jù)結(jié)構(gòu)茅坛,如:

{
    "status": 0, 
    "message": "", 
    "data": {
        "list": [
            ...
        ], 
        "total": 30  //總個數(shù)
    }
}

那么數(shù)據(jù)獲取,渲染则拷,Loading 動畫展示隱藏贡蓖,分頁加載等操作都可以在 TableBox 中完成。
這個組件需要的只是向外暴露出數(shù)據(jù)請求的 API 地址及各種參數(shù):

props: {
  // API URL
  url: {
    type: String,
    default: ''
  },
  // URL路徑中的參數(shù)煌茬,如:/posts/#{id}/comments
  pathParams: {
    type: Object,
    default: () => {}
  },
// Query參數(shù)
  options: {
    type: Object,
    default: function () { return {params: {}} }
  }
}

然后寫好對應(yīng)的獲取數(shù)據(jù)的 fetchData 方法:

fetchData (page) {
    this.showLoading()
    this.options.params.page = page >= 1 ? page : 1
    NetWorking
      .doGet(this.url, this.pathParams, this.options)
      .then(response => {
        this.items = response.data.list
        this.paginate.totalRows = response.data.total
        this.hideLoading()
      }, () => {
        this.hideLoading()
      })
}

這樣對于調(diào)用者來說斥铺,只需要簡單的傳入相關(guān) API 地址及參數(shù)就可以了,數(shù)據(jù)加載的事情讓 TableBox 去處理就好了坛善,非常的方便晾蜘。

場景&問題2:適配不同的表格列

因為 TableBox 組件本身是和業(yè)務(wù)無關(guān)的,所以其肯定無法知道我的這個 Table 的表頭是什么眠屎,有多少行剔交,也無法知道每一行展示什么數(shù)據(jù),這些內(nèi)容全部應(yīng)該由父組件告知 TableBox改衩。

要實現(xiàn)以上的功能岖常,我們可以借助于 Vue 本身提供的強(qiáng)大的工具 Slot,如果簡單點說葫督,大家可以把 Slot 理解為一個坑位竭鞍,因為大多數(shù)情況下,組件自己無法預(yù)先知道某塊區(qū)域放置什么內(nèi)容候衍,那么組件可以先將個區(qū)域放置一個 Slot笼蛛,就是挖個坑,當(dāng)父組件引入子組件時蛉鹿,會告訴子組件往這個坑位中填充什么樣的內(nèi)容滨砍。

回到我們的 TableBox 組件,我們首先挖兩個坑(放置兩個 Slot ),命名為 ths 和 item 惋戏,分別用于表頭和行列表內(nèi)容:

<table class="table table-hover table-bordered">
    <tbody>
        <slot name="ths"></slot>
        <slot name="item"
              v-for="item in items"
              :item="item">
        </slot>
    </tbody>
</table>

這樣對于表頭的數(shù)據(jù)领追,可以由引入 TableBox 的父組件來指定,用法如下响逢,其中 slot='ths' 就是剛才我們在 TableBox
中放置的 Slot

<tr slot="ths">
    <th>ID</th>
    <th>Project</th>
    <th>Version</th>
...
</tr>

同樣绒窑,對于每一行的內(nèi)容,也是由引入 TableBox 的父組件來指定舔亭,用法如下:

<template slot="item" scope="props">
    <tr>
          <td>
            {{ props.item.id }}
          </td>
          <td>
            {{ props.item.project.name }}
          </td>
          <td>
            <div>
              {{ props.item.version }}
            </div>
            <div>
              {{ props.item.number }}
            </div>
        </td>
    </tr>
</template>

問題&場景3:表格數(shù)據(jù)自刷新

在開發(fā)業(yè)務(wù)的過程中些膨,遇到一個場景:當(dāng)頁面數(shù)據(jù)已經(jīng)全部加載完畢后,在某些場景下需要改變 Table 中某些數(shù)據(jù)的狀態(tài)(刪除某列或改變某一列的數(shù)據(jù))钦铺。

DingTalk20171205113218.png

這里具體舉個 Falcon 中的實際例子:
我們允許用戶給每個項目分配多個環(huán)境订雾,以區(qū)分測試,生產(chǎn)矛洞,開發(fā)和各種自定義的場景洼哎,在每個環(huán)境下,用戶可以設(shè)置不同的 Git Branch 沼本。用戶點擊 Choose Branch 按鈕后噩峦,會觸發(fā)一個請求到后端,變更當(dāng)前環(huán)境的 Git Branch抽兆, 修改成功后該列表項的按鈕會顯示為 Current Branch识补。

由于以上邏輯都是在引入了 TableBox 的父組件中完成的,其能夠控制數(shù)據(jù)的刷新辫红,由于 場景1 中我們已經(jīng)把數(shù)據(jù)請求的邏輯都封裝在了 TableBox 中李请,所以我們需要讓其向外暴露出一個 Boolean 屬性:reloadData,當(dāng)此屬性為 true 時厉熟,TableBox 會重新請求一次API,并刷新列表较幌。

reloadData: {
    type: Boolean,
    default: false
}

同理揍瑟,由于操作數(shù)據(jù)是由父組件發(fā)起的,所父組件中也需要有同樣的屬性乍炉,并且和 TableBox 中的 reloadData 保持?jǐn)?shù)據(jù)同步绢片,這里用到了 Vue 2.3 版本增加的一個 .sync 修飾符進(jìn)行處理 。

<table-box :url="url" :pathParams="pathParams" :reloadData.sync="reloadData">

這樣岛琼,當(dāng) reloadData 在數(shù)據(jù)更新完畢后還原為 false 狀態(tài)時底循,我們可以顯示的觸發(fā)一個 emit 事件:

hideLoading () {
    this.showOverlay = false
    this.$emit('update:reloadData', false)
}

問題&場景4:父組件獲取表格數(shù)據(jù)

由于目前所有的數(shù)據(jù)獲取都是在 TableBox 內(nèi)部處理的,所以父組件本身是無法直接獲取到數(shù)據(jù)的槐瑞。但是在某些情況下熙涤,我們又希望父組件能夠獲取到數(shù)據(jù),以便能夠在頂層進(jìn)行更靈活的處理,這時我們就需要在 TableBox 內(nèi)部將數(shù)據(jù)拋出祠挫。

拋出的方式也很簡單那槽,我們可以使用 emit 方法拋出一個事件。根據(jù)這個思路我們改造一下上文提到的 fetchData 方法:

fetchData (page) {
    this.showLoading()
    this.options.params.page = page >= 1 ? page : 1
    NetWorking
      .doGet(this.url, this.pathParams, this.options)
      .then(response => {
        this.items = response.data.list
        this.paginate.totalRows = response.data.total
        // 拋出data完整對象等舔,以便父組件使用
        this.$emit('afterFetchData', response.data)
        this.hideLoading()
      }, () => {
        this.hideLoading()
      })
}

然后在父組件中監(jiān)聽這個事件骚灸,這樣就能獲取到完整的數(shù)據(jù)了。

<table-box :url="url" :pathParams="pathParams" @afterFetchData="afterFetchData" :reloadData="reloadData">
...

methods: {
      afterFetchData (data) {
        this.slb = data
      },

...

總結(jié)

解決了以上4個場景的問題后慌植,我們這個 TableBox 可以說告一段落了甚牲,后續(xù)如果有遇到新的場景,新的問題蝶柿,我們只需要不斷的去優(yōu)化去完善這個組件即可丈钙。
到目前為止,TableBox 已經(jīng)應(yīng)用到了我們內(nèi)部的三個后臺項目約幾十個頁面中只锭,可以說大大節(jié)省了我們的時間著恩,提升了整體效率。
并且隨著這樣的組件越來越多蜻展,甚至我們的后端工程師經(jīng)過簡短的培訓(xùn)喉誊,也可以上手部分前端頁面的開發(fā)了。
最后附上 TableBox 的地址:https://github.com/thierryxing/Falcon/blob/mock/src/components/global/TableBox.vue

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纵顾,一起剝皮案震驚了整個濱河市伍茄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌施逾,老刑警劉巖敷矫,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異汉额,居然都是意外死亡曹仗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門蠕搜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怎茫,“玉大人,你說我怎么就攤上這事妓灌」旄颍” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵虫埂,是天一觀的道長祥山。 經(jīng)常有香客問我,道長掉伏,這世上最難降的妖魔是什么缝呕? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任澳窑,我火速辦了婚禮,結(jié)果婚禮上岳颇,老公的妹妹穿的比我還像新娘照捡。我一直安慰自己,他們只是感情好话侧,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布栗精。 她就那樣靜靜地躺著,像睡著了一般瞻鹏。 火紅的嫁衣襯著肌膚如雪悲立。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天新博,我揣著相機(jī)與錄音薪夕,去河邊找鬼。 笑死赫悄,一個胖子當(dāng)著我的面吹牛原献,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播埂淮,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼姑隅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了倔撞?” 一聲冷哼從身側(cè)響起讲仰,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痪蝇,沒想到半個月后鄙陡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡躏啰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年趁矾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片给僵。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡愈魏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出想际,到底是詐尸還是另有隱情,我是刑警寧澤溪厘,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布胡本,位于F島的核電站,受9級特大地震影響畸悬,放射性物質(zhì)發(fā)生泄漏侧甫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望披粟。 院中可真熱鬧咒锻,春花似錦、人聲如沸守屉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拇泛。三九已至滨巴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俺叭,已是汗流浹背恭取。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留熄守,地道東北人蜈垮。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像裕照,于是被迫代替她去往敵國和親攒发。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容