抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

vue是一个数据驱动视图的前端框架,通过slot可实现父组件传递dom给子组件渲染,但是灵活性并没有jsx/tsx那么高,因此本文通过vue的template结合jsx/tsx,使用elementPlus组件库,实现配置动态渲染视图

一、模拟使用场景

1.my-table组件封装

my-table.vue

<template>
  <el-table>
    <el-table-column v-for="item in option" :key="item.prop" prop="item.prop" label="item.name">
      <template #default="{row}">
        <slot :name="item.prop" :row="row">
          {{row[item.prop]}}
        </slot>
      </template>
    </el-table-column>
  </el-table>
</template>
<script setup lang="ts">
const props = defineProps<{
    option: any
    data: any
}>()
</script>

2.简单使用组件

index.vue

<template>
    <my-table :option="option" :data="data"></my-table>
</template>
<script setup>
const option=[{
  name:"Date",prop:"date"
},{
  name:"Name",prop:"name"
},{
  name:"Address",prop:"address"
}]

const data = [{
    date:"2016-05-03",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-02",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-04",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-01",name:"Tom",address:"No. 189, Grove St, Los Angeles"
}]
</script>

3.自定义某一列渲染样式

<template>
  <my-table :option="option" :data="data">
    <template #name="{row}">
      <strong>{{row.name}}</strong>
    </template>
  </my-table>
</template>
<script setup>
const option=[{
  name:"Date",prop:"date"
},{
  name:"Name",prop:"name"
},{
  name:"Address",prop:"address"
}]

const data = [{
    date:"2016-05-03",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-02",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-04",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-01",name:"Tom",address:"No. 189, Grove St, Los Angeles"
}]
</script>

二、技术选择

1.问题痛点

虽然通过slot的方式可以自定义某一列的数据展示,但是当项目中多处使用,且经常只需要更改数据的格式化,这样的话使用slot显得比较麻烦了。

2.技术选择

动态渲染dom可以通过v-html或者render实现,而render可以渲染自定义组件且灵活性更高,因此优化思路为将数据展示格式化通过option来传递给my-table。

但自定义的my-table组件是template方式(相信大多数vue项目都是采用该方式),而template是不能渲染render的(基于vue3.2.20),因此需要考虑如何让template和render相结合。

三、实现

1.定义render-function组件

render-function.tsx

import { defineComponent, PropType, VNodeTypes } from 'vue';

export default defineComponent({
  name: 'RenderFunction',
  props: {
    render: {
      required: true,
    },
  },
  render() {
    return this.render;
  },
});

2.修改my-table组件

my-table.vue

<template>
  <el-table>
    <el-table-column v-for="item in option" :key="item.prop" prop="item.prop" label="item.name">
      <template #default="{row}">
        <slot :name="item.prop" :row="row">
            
          <template v-if="isFunction(item.formatValue)">
            <render-function :render="item.formatValue(row)"/>
          </template>
          
          <template v-else>
            {{row[item.prop]}}
          </template>

        </slot>
      </template>
    </el-table-column>
  </el-table>
</template>
<script setup lang="ts">
import renderFunction from './render-function'
const props = defineProps<{
    option: any
    data: any
}>()
</script>

3.option增加自定义render即可

index.vue

<template>
  <my-table :option="option" :data="data"/>
</template>
<script setup>
import {h} from 'vue'
  
const option=[{
  name:"Date",prop:"date"
},{
  name:"Name",prop:"name",
  formatValue:(row)=>{
    return h('strong',row.name)
  }
},{
  name:"Address",prop:"address"
}]

const data = [{
    date:"2016-05-03",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-02",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-04",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-01",name:"Tom",address:"No. 189, Grove St, Los Angeles"
}]
</script>

4.使用自定义组件,例如el-tag

index.vue

<template>
  <my-table :option="option" :data="data"/>
</template>
<script setup>
import {h} from 'vue'
import {ElTag} from 'element-plus'
  
const option=[{
  name:"Date",prop:"date"
},{
  name:"Name",prop:"name",
  formatValue:(row)=>{
    return h(ElTag,row.name)
  }
},{
  name:"Address",prop:"address"
}]

const data = [{
    date:"2016-05-03",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-02",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-04",name:"Tom",address:"No. 189, Grove St, Los Angeles"
},{
    date:"2016-05-01",name:"Tom",address:"No. 189, Grove St, Los Angeles"
}]
</script>

评论