打造组件库之table组件

2,072 阅读2分钟

组件需求

  • 传一个columns配置文件,即可生成对应的table
  • 传一个data即可渲染对应的数据
  • 可通过插槽的方式对数据进行二次处理

组件prop设计

| 参数 | 类型 | 是否必须 | 示例 | | --- | --- || --- | --- | | columns | Array | 是| [{title: '名字', dataIndex: 'name',customRender:true/false}] | data | Array | 是| [{name:'123'}]

参数解释

  • columns是一个数组类型(必须)
    • columns里面每一项是一个对象(必须)
      • columns每个对象应该包含:
        • title(String):根据这个键生成的thead对应的th(必填)
        • dataIndex (String): 根据这个键渲染对象的数据(这个键跟data里面的键一一对应)(必填)
        • customRender(Boolean):根据这个键判断是否要分配一个对应的插槽(可选)
    • data是一个数组类型(必须)
      • data里面每一项是一个对象,里面的键必须跟上面的columns的dataIndex一一对应(必须)

期待的使用方式


<y-table :columns="columns" :data="data">
  <template #sex="text">
    {{ text === 1 ? '男': '女'}}
  </template>
  <template #handle="text,record">
    <div style="display:flex">
      <y-button type='success' @click="test(record)" style="margin-right:10px">修改</y-button>
      <y-button type='danger' @click="test(record)">删除</y-button>
    </div>
  </template>
</y-table>

<script>
  data () {
    return {
      columns: [
        {
          title: '名字',
          dataIndex: 'name'
        },
        {
          title: '年龄',
          dataIndex: 'age'
        },
        {
          title: '性别',
          dataIndex: 'sex',
          customRender: true
        },
        {
          title: '操作',
          dataIndex: 'handle',
          customRender: true
        }
      ],
      data: [
        {
          name: '鸡太美',
          age: 39,
          sex: 1,
          reload: 3
        },
        {
          name: '鸡太美2',
          age: 39,
          sex: 2,
          reload: 3
        }
      ]
    }
  }
</script>

期待的效果

组件的实现

第一步:首先根据上面的的需求,先定义两个props


props: {
    columns: {
      type: Array,
      required: true
    },
    data: {
      type: Array,
      default: () => []
    }
  }

第二步:根据传进来的columns的title生成对应的thead

// 根据配置生成thead
_renderHeader (h) {
  return h('thead', { class: 'y-header' }, [
    h('tr', { class: 'y-header-row' }, [
      this.columns.map(row => {
        return h('th', { class: ['y-header-column', 'y-table-left'] }, row.title)
      })
    ])
  ])
}

第三步:根据columns的dataIndex结合data的键值对渲染对应的数据,再根据columns的customRender判断是否分配对应的插槽

// 根据配置生成tbody,再根据customRender分配作用域插槽
_renderBody (h) {
  return h('tbody', {}, [
    this.data.map(row => {
      return h('tr', { class: 'y-body-row' }, [
        this.columns.map(item => {
          return h('td', { class: 'y-body-column' }, item.customRender && this.$scopedSlots[item.dataIndex]
            ? this.$scopedSlots[item.dataIndex](row[item.dataIndex], {
              ...row
            }) : row[item.dataIndex])
        })
      ])
    })
  ])
}

这里生成的插槽是作用域插槽,有时候它是封装组件的神一样的存在,这里把当前的值传给插槽的第一参数,当前的行数据传给第二参数,所以外面就可以这样拿到数据

 <template #handle="text,record">
    <div style="display:flex">
      <y-button type='success' @click="test(record)" style="margin-right:10px">修改</y-button>
      <y-button type='danger' @click="test(record)">删除</y-button>
    </div>
  </template>

第四步:最后组装table,return 出去就OK了

// 组装table
  _renderTable (h) {
    return (
      <table class='y-table'>
        {this._renderHeader(h)}
        {this._renderBody(h)}
      </table>
    )
  }

  render (h) {
    return (
      this._renderTable(h)
    )
  }

总结

到此,根据上面的需求已经实现了对应的可配置的table组件,比较核心的是上面两个生成方法(_renderHeader,_renderBody)大概也就20行,对于熟悉的JSX和作用域插槽的同学来说再容易不过了,希望此文章对你有帮助。

完整逻辑代码

export default {

  props: {

    columns: {

      type: Array,

      required: true

    },

    data: {

      type: Array,

      default: () => []

    }

  },

  methods: {

    // 格式化数据

    formatter (row, item) {

      return (item.formatter && item.formatter(row[item.dataIndex])) || row[item.dataIndex]

    },

    // 根据配置生成thead

    _renderHeader () {

      const h = this.$createElement

      return h('thead', { class: 'y-header' }, [

        h('tr', { class: 'y-header-row' }, [

          this.columns.map(row => {

            return h('th', { class: ['y-header-column', 'y-table-left'] }, row.title)

          })

        ])

      ])

    },

    // 根据配置生成tbody,再根据customRender分配作用域插槽

    _renderBody () {

      const h = this.$createElement

      return h('tbody', {}, [

        this.data.map(row => {

          return h('tr', { class: 'y-body-row' }, [

            this.columns.map(item => {

              return h('td', { class: 'y-body-column' }, item.customRender && this.$scopedSlots[item.dataIndex]

                ? this.$scopedSlots[item.dataIndex](row[item.dataIndex], {

                  row: row

                }) : this.formatter(row, item))

            })

          ])

        })

      ])

    },

    // 组装table

    _renderTable () {

      return (

        <table class='y-table'>

          {this._renderHeader()}

          {this._renderBody()}

        </table>

      )

    }

  },

  render (h) {

    return (

      this._renderTable()

    )

  }

}

</script>