基於element-ui實現table可配置化

qfkobe發表於2017-12-12

寫在前面

感謝 餓了麼前端團隊提供元件化框架elememt-ui,本文基礎元件使用element-ui。

大背景

在開發一些系統過程中,使用table作資料展示在所難免。先來看看el-table元件。

基於element-ui實現table可配置化

非常簡單易用的元件,根據提供的data資料,配置table每一列的資料和label。沒錯,這樣似乎都是ok的,但是在開發大量包含table的頁面,會發現每次都需要去複製el-table-column,或者你會說你已經很屬性element-ui整套框架的文件。然而,重複的程式碼很多及程式碼量很大。作為一名程式設計師,某一天需要寫自己已經寫過的某段程式碼,是不是感覺有點不應該呢。。。

資料驅動

<el-table
  :data="tabledata">
   <el-table-column
    v-for="(item,key) in columnsConfig"
    :key="key"
    :label="item.label"
    :prop="item.prop">
    </el-table-column>
</el-table>
複製程式碼
data () {
  return {
    columnsConfig: [{
      prop: 'logicCategoryId',
      label: '編號(ID)'
    }, {
      prop: 'name',
      label: '分類名稱'
    }]
  }
}
複製程式碼

這樣可以很輕鬆的渲染出來table。

這裡有個小技巧

el-table-column改造下

<el-table
  :data="tabledata">
   <el-table-column
    v-for="(item,key) in columnsConfig"
    :key="key"
    v-bind="item">// 這裡改造
    </el-table-column>
</el-table>
複製程式碼

v-bind可以繫結一個物件,這樣會讀取item物件的每個屬性,使用到元件當中去。比如增加width: 100,只需要在columnsConfig沒一項增加width即可,el-table-column不需要改動。

使用slots怎麼辦

<el-table-column
  label="日期"
  width="120">
  <template scope="scope">{{ scope.row.date }}</template>
</el-table-column>
複製程式碼

很明顯,el-table-column的slots配置在columnsConfig陣列不能用。 在原始碼找到解決辦法(檔案路徑:node_modules_element-ui@1.3.7@element-ui\packages\table\src\table-column.js),create鉤子函式程式碼如下:

基於element-ui實現table可配置化
在this.columnConfig.renderCell函式渲染每一個table cell,如果使用slots會讀取,slots中的內容。 編寫一個新元件使用vue extends,繼承el-table-columns,重寫了renderCell函式改變其返回內容,這時候用jsx將內容用return slots的函式傳回來。

首先,看看columnsConfig怎麼寫

columnsConfig: [{
  cellType: 'slots',// 不是所有的列都需要重寫,加個欄位標記下
  prop: 'logicCategoryId',
  label: '編號(ID)',
  renderCell: (scope) => {
    return (
      <el-button type="text" on-click={() => that.viewGoods(scope.row)}>
        { scope.row.productCount }
      </el-button>
    )
  }
}, {
  prop: 'name',
  label: '分類名稱'
}],
複製程式碼

其次,開發新元件 column-plus.vue,程式碼如下

<script>
import { TableColumn } from 'element-ui'
// renderCell 函式,型別可擴充套件
const renderCell = {
  slots: function (h, data) {
    // 接受傳入的renderCell函式
    let renderCell = () => {
      return this.renderCell ? this.renderCell(data) : ''
    }
    return <div class="cell">{ renderCell(h, data) }</div>
  }
}
export default {
  extends: TableColumn,// 繼承el-table-column
  props: {
    prop: {
      type: [String, Number]
    },
    cellType: {
      type: String,
      validator: function (value) {
        let valid = ['text', 'input', 'slots'].includes(value)
        !valid && console.error(`columnPlus元件不適配 ${value} 型別`)
        return valid
      }
    },
    renderCell: {
      type: Function
    }
  },
  // el-table-column 先呼叫在呼叫本身的
  created () {
    if (renderCell[this.cellType]) {
      this.columnConfig.renderCell = renderCell[this.cellType].bind(this)
    }
  }
}
</script>

複製程式碼

最後,元件用通過模板,配置資料即可

<el-table
  :data="tabledata">
   <column-plus v-bind="item" v-for="(item,key) in columnConfigs" :key="key">
  </column-plus>
</el-table>
複製程式碼

配置資料

columnsConfig: [{
  cellType: 'slots',
  prop: 'logicCategoryId',
  label: '編號(ID)',
  renderCell: (scope) => {
    return (
      <el-button type="text" on-click={() => that.viewGoods(scope.row)}>
        { scope.row.productCount }
      </el-button>
    )
  }
}, {
  prop: 'name',
  label: '分類名稱'
}]****
複製程式碼

寫在後面

元件開發實現可配置開發,可達到快速開發的目的和簡化程式碼。 需要說明vue extends和mixins屬性,這兩個屬性都是繼承,屬性和方法重寫,鉤子函式是先呼叫父類在呼叫自己的,上面的例子(el-table-column 先呼叫在呼叫本身的),但是區別是:

  • extends單繼承,同時使用優先順序高
  • mixins多繼承,同時使用優先順序低

另外,vue-cli使用jsx編寫需要安裝三個外掛。

  • babel-helper-vue-jsx-merge-props
  • babel-plugin-syntax-jsx
  • babel-plugin-transform-vue-jsx

補充

釋出了一天沒想到有這麼小夥伴閱讀,有幾點需要補充下。

評論區 @hold 評論更iview如出一轍,後來去翻看iview的文件確實,一直都沒用iview開發過,不知道table也是用render來實現的,確實分享之後自己學到了更多,感謝@hold。

這是iview,table元件的程式碼

render: (h, params) => {
    return h('div', [
        h('Button', {
            props: {
                type: 'text',
                size: 'small'
            }
        }, 'View'),
        h('Button', {
            props: {
                type: 'text',
                size: 'small'
            }
        }, 'Edit')
    ]);
}
複製程式碼

當然使用render之後,需要使用filter,就比較尷尬了。不過,vue的filter其實也是語法糖,最終編譯是編譯成函式處理。

那如果想使用filter怎麼辦?

data () {
    return {
      columns: [{
        renderCell: (scope) => {
          return (
              { that.stateTxt(scope.row.state)}
          )
        }
      }]
    }
    },
    methods: {
        stateTxt (val) {}
    }
複製程式碼

寫過react都知道是這麼幹的。

所以,一直以來前端把js寫在html中,還是把html寫js中,都是爭論不休的話題,如何選擇還開發自己拿捏。

不使用render程式碼是這樣的

<template>
    <el-table
      :data="tableData"
      style="width: 100%">
      <el-table-column
        prop="date"
        label="日期"
        width="180">
      </el-table-column>
      <el-table-column
        prop="name"
        label="姓名"
        width="180">
      </el-table-column>
      <el-table-column
        prop="address"
        label="地址">
      </el-table-column>
    </el-table>
  </template>
複製程式碼

使用render處理是這樣的

<el-table
  :data="tabledata">
   <column-plus v-bind="item" v-for="(item,key) in columnConfigs" :key="key">
  </column-plus>
</el-table>

data () {
    return {
      columnConfigs: [{
        label: '日期',
        width: '180',
        prop: 'date'
      }, {
        label: '姓名',
        width: '180',
        prop: 'name'
      }, {
        label: '地址',
        prop: 'address'
      }]
    }
}
複製程式碼

相關文章