近期遇到一个vue项目中的table页面,数据和模板强耦合在一个页面中,看起来比较复杂,维护较不方便,而该页面又重复性较高,于是尝试在vue中用循环渲染dom元素,抽离公共部分配置化,很不幸的是v-for并不支持动态的dom元素插入,那么怎么用vue实现动态dom元素节点的动态渲染呢。

联想到react的实现方式,想到vue的render函数,vue中的render号称可以像react一样写页面,有了想法就开始实施。render函数是比较好理解的,但是在render中如何实现 插槽,事件点击,Scope,  以及如何在render中使用 开源库呢?

经过对vue编译之后虚拟dom的调研,这些都是可以实现的。

插槽和scope:可以直接写成 (scope)=> {}的形式

事件点击:nativeOnClick

开源库:事件变为  'on-' + '    '   比如:  on-selection-change

下面是一个DynamicTablePage实例:

<script>
export default {
  name: 'DynamicTablePage',
  props: {
    msg: String,
    tableParams: Array,
    tableData: Array,
    selections: Array,
    layout: String,
    pageSizes: Array,
    handleSizeChange: Function,
    handleCurrentChange: Function,
    total: Number,
    currentPage: Number,
    hasPagination: Boolean,
    handleSelectionChange: {
      type: Function,
      default: () => { }
    },
    paginationFixed: Boolean
  },
  data () {
    return {
    }
  },
  mounted () {
    console.log(this.hasPagination)
  },
  methods: {

  },
  render (h) {
    return <div class="jsx-component">
      <el-table
        data={this.tableData}
        style={{ width: '100%' }}
        class="test"
        on-selection-change={this.handleSelectionChange}
      >
        {
          this.tableParams.map((item, index) => {
            return <el-table-column
              prop={item.param}
              label={item.title}
              width={item.width}
              key={index}
              type={item.type}
              allow-expand={item.allowExpand}
              filters={item.filters}
              align={item.align ? item.align : 'left'}
              selections={this.multipleSelection}
              filter-method={item.filterHandler}
            >
              {
                item.template ? scope => item.template(scope, this, h) : ''
              }
            </el-table-column>
          })
        }
      </el-table>
      {
        this.hasPagination ? <el-pagination
          class={this.paginationFixed ? "fixed" : ''}
          on-size-change={this.handleSizeChange}
          on-current-change={this.handleCurrentChange}
          current-page={this.currentPage}
          page-sizes={this.pageSizes}
          layout={this.layout}
          total={this.total}>
        </el-pagination> : null
      }
    </div >
  }
}
</script>

<style scoped>
.jsx-component {
  background: red;
}
.fixed {
  position: fixed;
  bottom: 0;
}
</style>

对应的配置文件:

let tableParams = [
    {
        title: '',
        param: "",
        width: '30px',
        type: 'selection'
    },
    {
        width: '30px',
        type: 'expand',
        test: {
            name: 1
        },
        allowExpand (_row, index) {
            return index !== 2;
        },
        // eslint-disable-next-line no-unused-vars
        template: (_scope, _self, h) => {
            return <div>测试</div>
        }
    },
    {
        title: '日期',
        param: "date",
        filters: [
            { text: '2018/12/01', value: '2018/12/01' },
            { text: '2018/12/02', value: '2018/12/02' },
            { text: '2018/12/03', value: '2018/12/03' },
            { text: '2018/12/04', value: '2018/12/04' },
            { text: '2018/12/05', value: '2018/12/05' },
            { text: '2018/12/10', value: '2018/12/10' }
        ],
        filterHandler (value, row, column) {
            console.log(value, row, column)
            const property = column['property'];
            return row[property] === value;
        }
    },
    {
        title: '字段2',
        param: "date2",
        template: (scope) => {
            return scope.row.date2.replace(/[1-9]+/g, '000')
        }
    },
    {
        title: '字段3',
        param: "date3",
        // eslint-disable-next-line no-unused-vars
        template: (scope, self, h) => {
            return <div>
                <el-button
                    icon="h-icon-edit"
                    size="mini"
                    nativeOnClick={() => {
                        self.$router.push({ name: 'PageTwo' })
                    }}
                />
            </div>
        }
    }
]

export const tableData = () => {
    return [
        {
            date: '2018/12/01',
            date2: 'dfkjdfjk',
            date3: '测试',
            id: 0
        },
        {
            date: '1221121221',
            date2: 'dfkjdfjk',
            date3: '测试',
            id: 1
        },
        {
            date: '1221121221',
            date2: '1373698562',
            date3: '测试',
            id: 2
        },
        {
            date: '1221121221',
            date2: 'dfkjdfjk',
            date3: '测试',
            id: 3
        }
    ]
}
export default tableParams

使用组件DynamicTablePage

<template>
  <div class="example">
    <h4>页面一</h4>
    <DynamicTablePage
      :tableParams="tableParams"
      :tableData="tableData"
      :total="total"
      layout="total, sizes, prev, pager, next, jumper"
      :currentPage="currentPage"
      :handleSizeChange="handleSizeChange"
      :handleCurrentChange="handleCurrentChange"
      :pageSizes="[100, 200, 300, 400]"
      hasPagination
      :handleSelectionChange="handleSelectionChange"
      paginationFixed
    ></DynamicTablePage>
  </div>
</template>
<script>
import DynamicTablePage from '../../components/DynamicTablePage'
import tableParams, { tableData } from './pageOne.config'
export default {
  name: 'PageOne',
  components: {
    DynamicTablePage
  },
  data () {
    return {
      tableData: tableData(),
      total: 1000,
      selections: [],
      tableParams: tableParams,
      currentPage: 1
    }
  },
  methods: {
    handleSelectionChange (val) {
      console.log(val)
      this.multipleSelection = val;
    },
    handleSizeChange () {

    },
    handleCurrentChange (val) {
      console.log(val)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.example {
  width: 800px;
  margin: 0 auto;
  height: auto;
}
</style>

 

该组件是对table的抽象,封装过后表格的每一列和数据都可以抽离为配置化,代码结构清晰,且有利于后期维护。如果有相同的页面,可以通过配置快速生成页面。

对于其他的功能,比如表单页面,展示页面等,都可以做类似的封装,从而达到效率和性能及维护的提升。

对于较为复杂组件的封装,还可以结合vuex,插件抽离实现。

 

 

 

 

Logo

前往低代码交流专区

更多推荐