Vue从template到render函数JSX组件

530 阅读2分钟

本文章,没有原理,没有乱七八糟的废话,除了这一行以外,其余直接上代码,如有不妥,感谢留言指正

为什么用render函数写JSX组件?

  • 拓展性更好
  • 更解耦
  • 性能好
  • 抛弃插槽的弊端

对比

<el-table
  ref="table"
  class="table"
  :data="table.data"
  v-bind="table.config"
  v-on="table.on">
  <el-table-column
    v-for="(item, index) of table.columns.filter(key => !key.isButton)"
    :key="index"
    v-bind="item">
    
    <!--如果我们想在这里加上自定义的元素,那么我们只能用插槽,或者改写column组件-->
    
    <!--插槽的话,我们得用<component></component>标签,写这个,我们得多传很多参数,导致参数结构混乱-->
    
  </el-table-column>
  <el-table-column
    v-for="(item, i) of table.columns.filter(key => key.isButton)"
    :key="i + Math.random()"
    v-bind="item">
    <template slot-scope="scope">
      <el-button
        v-for="i of item.btns"
        :key="i.code"
        @click="handlerClick({i, scope})"
        v-bind="i">
        {{ i.name }}
      </el-button>
    </template>
  </el-table-column>
</el-table>
export default {
  name: 'table',
  props: {
    table: {
      type: Object,
      default () {
        return {}
      }
    }
  },
  methods: {
    handlerClick (data) {
      this.$emit('handlerClick', data)
    }
  }
}

于是我们改成render函数JSX组件

export default {
  name: 'render-table',
  props: {
    table: {
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  methods: {
    handlerClick (e, i, props) {
      // 内部先解决冒泡问题
      e.stopPropagation()
      // 然后根据是否带on函数,来进行回调
      if (i.on) {
        i.on(props, i.code)
      }
    }
  },
  render () {
    return (
      <div class="plus-render-table">
        <el-table { ...{ on: { ...this.table.on } } } { ...{ attrs: this.table.config } } ref="plus-render-table" class="table" data={ this.table.data }>
          {
            this.table.columns.filter(key => !key.isButton).map(item => {
              if (item.renderCell) {
                const scoped = (item) => {
                  return {
                    default: props => {
                      return (item.renderCell(props, item))
                    }
                  }
                }
                const scopedSlots = scoped(item)
                return <el-table-column { ...{ attrs: item } } { ...{ scopedSlots } }></el-table-column>
              }
              return <el-table-column { ...{ attrs: item } }></el-table-column>
            })
          }
          {
            this.table.columns.filter(key => key.isButton).map(item => {
              const scopedSlots = {
                default: (props) => {
                  return item.btns.map(it => {
                    return <el-button on-click={ (e) => this.handlerClick(e, it, props) } { ...{ attrs: it } }>{ it.name }</el-button>
                  })
                }
              }
              return <el-table-column { ...{ attrs: item } } { ...{ scopedSlots } }></el-table-column>
            })
          }
        </el-table>
      </div>
    )
  }
}

这样,我们的table组件,就改写完成了

来看看怎么用它

table: {
  // table的列配置
  columns: [
    { type: 'selection' },
    { type: 'index', label: '序号', align: 'center' },
    {
      prop: 'name',
      // 这是render函数来渲染表头的方式
      renderHeader: (h, scope) => {
        return <div>名称</div>
      },
      renderCell: (props) => {
        return <div class="common-row-start align-center">{props.row[props.column.property]}</div>
      }
    },
    {
      label: '年龄',
      prop: 'age',
      sortable: true,
      formatter: (row, column, cellValue, index) => {
        return row.age
      }
    },
    {
      label: '性质',
      prop: 'address',
      // 这个是一个render函数,通过render函数就可以写我们的特殊配置,只能有一个根元素,也就是return括号里面的第一个标签必须是唯一的div,它没有兄弟元素
      renderHeader () {
        return (
          <div>
            <span>岗位性质</span>
            <el-tooltip effect="light" placement="right">
              <div slot="content">
                <div style="text-align: center;margin-bottom: 5px">说明</div>
                <div>
                  <div>xxxxxxxxxxxxxx</div>
                  <div>aaaaaaaaaaaaaaaa</div>
                  <div>qqqqqqqqqqqqqqqqqq</div>
                </div>
              </div>
              <i class="el-icon-question" style="font-size: 16px;margin-left: 3px;vertical-align: -10%;"></i>
            </el-tooltip>
          </div>
        )
      },
      showOverflowTooltip: true
    },
    {
      label: '操作',
      isButton: true,
      width: 150,
      btns: [
        {
          name: '查看详情',
          code: 'info',
          type: 'text',
          size: 'mini',
          on: (props, code) => {
            console.log(props, code)
          }
        },
        {
          name: '新增',
          code: 'add',
          type: 'text',
          size: 'mini',
          on: (props, code) => {
            console.log(props, code)
          }
        },
        {
          name: '修改',
          code: 'edit',
          type: 'text',
          size: 'mini',
          on: (props, code) => {
            console.log(props, code)
          }
        },
        {
          name: '删除',
          code: 'remove',
          type: 'text',
          size: 'mini',
          on: (props, code) => {
            console.log(props, code)
          }
        },
        {
          name: '启用',
          code: 'enable',
          type: 'text',
          size: 'mini',
          on: (props, code) => {
            console.log(props, code)
          }
        },
        {
          name: '停用',
          code: 'disable',
          type: 'text',
          size: 'mini',
          on: (props, code) => {
            console.log(props, code)
          }
        }
      ]
    }
  ],
  config: {
    stripe: true,
    border: true,
    height: 300,
    maxHeight: 600,
    size: 'mini',
    fit: true,
    showHeader: true,
    highlightCurrentRow: true,
    showSummary: false,
    emptyText: '没有数据'
  },
  data: _this.data
}