Vue優雅設計一個組件

Vue優雅設計一個組件javascript

本組件已開源,已npm publish,可自行npm install km-gridcss

效果展現

設計原則

注:雖然如今已經出現大量優秀的UI框架,如elementUI,iviewUI,單從業務、功能及性能仍不能知足項目全部需求,這時,就要考慮如何設計適合本身的組件。
組件設計其實就是模塊的設計。我把它分爲4個部分:UI設計基本功能業務拓展需求性能。如下我已我本身設計的高性能table爲例,剖析我是如何優雅的設計一個組件。前端

  • UI設計交互就比如模塊的importexports,要保持統一的出口。即統一風格配色,統一交互。
  • 基本功能就比如模塊的基本做用。必須保證基本功能的同時,兼容其餘。
  • 業務拓展就比如模塊間的集成,這就要求組件可依耐性低、可拔插集成、高聚低耦合。
  • 性能要考慮到大量數據或大量計算與GUI渲染線程互斥致使的性能問題。

實現步驟

注:一個組件的設計先從基本功能開始。vue

1、佈局

先看table的基本功能包括:頭部、序號列、多選列、左右固定列、總計行;
業務拓展功能:列搜索。java

考慮到左右固定列須要單獨的頭部、搜索行、內容、總計行。可將km-grid設計爲3個單獨的table,每一個單獨的table都是一個獨立的km-grid-itemweb

<div :class="cls" :style="tableStyles">
  <km-grid-item v-if="fixedLeftCol&&fixedLeftCol.length" fixed="left" v-on="$listeners" :columns="fixedLeftCol" :header-styles="leftFixedHeaderStyles" :body-styles="leftFixedBodyStyles"></km-grid-item>
  <km-grid-item v-on="$listeners" :columns="centerCol" :expandColumn="expandCol" :header-styles="headerStyles" :body-styles="bodyStyles"></km-grid-item>
  <km-grid-item v-if="fixedRightCol&&fixedRightCol.length" fixed="right" v-on="$listeners" :columns="fixedRightCol" :header-styles="rightFixedHeaderStyles" :body-styles="rightFixedBodyStyles"></km-grid-item>
</div>
複製代碼

這樣,整個table的設計便會輕便且簡潔。npm

單個table的設計又怎麼辦呢?
那我是如何在單個組件驅動其餘組件的呢? 編程


首先在km-grid-item mounted是,發送當前的VueComponent:

mounted () {
  this.dispatch("KmGrid""on-km-grid-item-add"this);
}
複製代碼

在km-grid接收:後端

created () {
  this.$on('on-km-grid-item-add', item => {
    this.itemVms.push(item);
  })
}
複製代碼

接着在事件中觸發同步驅動整個table:api

HandleBodyScroll (event) {
  this.$refs.header.scrollLeft = event.target.scrollLeft
  if (this.$parent.itemVms && this.$parent.itemVms.length > 0
{
    this.$parent.itemVms.forEach(v => {
      let scrollDom = v.$el.querySelectorAll('.km-grid-noscroll')[0]
      if (scrollDom) {
        scrollDom.scrollTop = event.target.scrollTop
      }
    })
  }
}
複製代碼

2、業務拓展需求

業務拓展需求有:根據行數據顯示不一樣按鈕,不一樣行爲,數據格式化,行下拉動態渲染。

一、根據行數據顯示不一樣按鈕

把需求轉化爲數據操做就是先獲取行數據,根據行數據展現不一樣的按鈕,而後自定義行爲在業務中編寫:
先定義column及操做:

{
  title: '操做',
  type'handle',
  handle: [
    {
      type'edit'
    },
    {
      type'warehouse',
      icon: 'km-stock',
      click: row => {
        this.$refs.listpage.showList = false
        this.$refs.WarehouseInfo.loadEntity(row.id)
      }
    }
  ],
  width: 90,
  align: 'left',
  fixed: 'right'
}
複製代碼

根據行數據渲染:

showCustomOperate: {
  warehouserow => {
    switch (row.type) {
      case '4':
        return true
      default:
        return false
    }
  }
}

RebuildTableDataByColOperate (columns, data) {
  data.forEach(v => {
    handles.forEach(handle => {
      v['__' + handle.type] =
        this.showCustomOperate[handle.type](v) || false
    })
  })
}

HandleIconClick (item, row) {
  return item.click(row);
}
複製代碼

經過這種集成方式組件沒必要關心業務需求,自動化配置自定義按鈕及操做行爲,可拔插的集成,避免影響平臺組件維護。

二、行下拉動態渲染

首先在columns裏配置:

{
  type'expand',
  width: 50,
  render: (h, params) => {
    return h(inventoryControl, {
      props: {
        row: params.row
      }
    })
  }
}

components: {
  inventoryControl: () => import('@/view/main-components/inventoryControl.vue')
}
複製代碼

而後在km-grid-item的tr裏面根據行數據動態render:

<div v-if="!fixed&&expandColumn.render" :class="cls+'-tr-expand'">
  <div style="width:100%" v-if="row._clicked">
    <td-render :row="row" :render="expandColumn.render"></td-render>
  </div>
</div>

複製代碼

經過這種集成方式組件沒必要關心業務需求,返回行數據讓業務本身交互,不會污染當前組件DOM及多餘的代碼邏輯。

三、數據格式化

數據格式化是一個老生常談的問題,例以下面咱們只用經過這個方法便可格式化系統數據:

GetTdData (row, col) {
  if (col.enumData) {
    let desc = (col.desc && row[col.desc(row)]) ? '(' + row[col.desc(row)] + ')' : ''
    return col.desc ? col.enumData[row[col.key]] + desc : col.enumData[row[col.key]]
  } else if (col.valueKey) {
    return row[col.key] ? `[${row[col.key]}]${row[col.valueKey]}` : ''
  } else if (['date''datetime''time'].includes(col.type)) {
    const dateEnum = { date'yyyy-MM-dd'datetime'yyyy-MM-dd HH:mm:ss'time'HH:mm:ss' }
    if (!row[col.key]) {
      return ''
    } else {
      return formatDate(row[col.key], col.format || dateEnum[col.type])
    }
  } else if (col.bizType && ['price''amt''qty''rate'].includes(col.bizType)) {
    const formatEnum = { price4amt2qty3rate4 }
    if (typeOf(row[col.key]) === 'null') {
      return '-'
    } else {
      return parseFloat(row[col.key]).toFixed(col.format || formatEnum[col.bizType])
    }
  }
  return row[col.key]
},
GetTotalData (col) {
  if (!col.total) {
    return ''
  } else if (col.totalValue) {
    return col.totalValue
  } else if (col.bizType && ['price''qty''amt'].includes(col.bizType)) {
    const formatEnum = { price4qty3amt4 }
    return this.$parent.data.map(row => Number(row[col.key])).reduce((a, b) => a + b, 0).toFixed(col.format || formatEnum[col.bizType])
  }
  return ''
}
複製代碼

這種默認配置是前端與後端約定好,前端在columns規定好,便可有序展開。

總結一下就是,拓展需求就是要可儘可能少的配置、自動化、可拔插、不污染組件、減小增長組件邏輯、無代碼侵入的支持業務功能。

3、性能

經測試,km-grid擁有高效的性能併兼容主流瀏覽器(ie11以上,包含ie11)。對比了一樣的iview-tableelement-table,在500條級數據時,km-grid表現更佳,大大超越iview-table。各位同窗可直接在npm install

組件API設計

1、prop

屬性名 屬性類型 描述
data Array 元數據
headerRowHeight Number 頭部高度
rowHeight Number 行高度
totalRowHeight Number 總計高度
height Number 指定高度
width Number 指定寬度
border Boolean 顯示分割線
pageSize Number 分頁數據
current Number 分頁數據
draggable Boolean 是否可拖拽列寬
clickAsync Number 點擊事件是否異步加載

2、events

事件名 描述
on-selection-change 全選,單選時觸發
on-row-click 點擊
on-row-dblclick 雙擊
on-sort-change 排序

3、methods

方法名 描述
GetSelectRows 獲取勾選選行
ClearSelectRows 清除溝選行
GetCurrentRow 獲取選中行

km-grid在設計上精簡,使用v-on="$listeners"向上拋出事件,因此寫km-grid-itememit事件不用重複的再往上面拋,而對於須要對外開放的APIkm-grid則故意將須要開放的API寫在km-grid裏面。

// public API so i Expose in km-grid
GetSelectRows () {
  return this.itemVms[0].GetSelectRows()
},
// public API so i Expose in km-grid
ClearSelectRows () {
  this.data.forEach(row => {
    this.$set(row, '_checked'false)
  })
},
// public API so i Expose in km-grid
GetCurrentRow () {
  return this.data.find(row => row._clicked)
}
複製代碼

可編輯的雙向綁定table

如今的前端愈來愈後後端化,從npm倉庫到模塊化,自動化,前端早已不是幾年前的那個畫頁面前端。更多的考慮是模塊化組件的同時,處理好組件與平臺,平臺與業務這三方面的「交互」。若是你能很好的理解VUE的VueComponent響應式,將在VUE的海洋如魚得水,同時也對面向對象編程有本身獨到的看法。
下一期我將展現如何將km-grid即成爲高性能的可編輯雙向綁定table,同時也將開源。

相關文章
相關標籤/搜索