Vue:使用$attrs和render实现动态表单

场景

很多时候我们需要对一些UI框架进行二次封装,比如对elementUI进行二次封装

<template>
  <div>
    <el-input v-model="value"/>
  </div>
</template>

这个时候我们需要能在组件外面设置el-input的属性,那我们的第一反应都是,通过props传值进来,然后一个一个进行设置

<template>
  <div>
    <el-input 
      v-model="value"
      :placeholder="placeholder"
      ...
  />
  </div>
</template>

这样虽然行的通,但是缺点也很明显,单属性多的时候会导致代码的可读性差,增加维护的难度,而且容易遗漏

利用v-bind="$attrs"实现透传

首先我们来看看文档对$attrs的解释

image.png

当我们在组件中没有声明,即显式的使用props去接收父组件传过来的属性时,传入相关的属性,会直接将属性传到根节点上

image.png

红框中的placeholder和maxleng都是父组件传入的属性
怎么样将属性简单、便捷的将属性传入到el-input中呢,只需要给el-input加一个v-bind="$attrs"就行了

<template>
  <div>
    <el-input v-model="value" v-bind="$attrs"/>
  </div>
</template>

利用render函数实现动态表单

首先看看文档中对render的详细说明

image.png

render接收三个参数:
1: 渲染的tag
2:tag所拥有的属性
3:子节点
参数说明地址
根据这些参数,我们可以直接实现动态表单,上代码
父组件:

// 父组件
<template>
<div>
  <div>child-components-A</div>
  <childComponentB :configJsonArr="configJsonArr"></childComponentB>
</div>
</template>

<script>
import childComponentB from './childComponentB'
export default {
  components: {
    childComponentB,
  },
  data () {
    return {
      configJsonArr: [
        {
          type: 'Input',
          props: {
            placeholder: 'dfsdffsdfsdfds',
            maxlength: 12
          }
        },
        {
          type: 'Select',
          props: {
            placeholder: 'dfsdffsdfsdfds',
            options: [
              { value: 1, key: '111' },
              { value: 2, key: '222' },
              { value: 3, key: '333' },
            ]
          }
        }
      ]
    }
  },
  methods: {

  }
}
</script>

子组件:

<template>
  <div>
    <div>child-components-B</div>
    <childComponentC v-bind="$attrs"></childComponentC>
    <form-item :configJson="item" v-for="(item, index) in configJsonArr" :key="index"></form-item>
  </div>
</template>

<script>
import childComponentC from './childComponentC'
import Input from './Input'
import Select from './Select'

const FormItem = {
  components: {
    Input,
    Select
  },
  props: [ 'configJson' ],
  render(h) {
    console.log(this.configJson);
    return h(
      `${this.configJson.type}`,
      {
        props: {
          ...this.configJson.props
        },
        attrs: {
          ...this.configJson.props
        },
      },
    )
  }
}


export default {
  components: {
    childComponentC,
    FormItem
  },
  data () {
    return {
      test: {
        id: 1234234,
        length: 12,
      },
      configJsonArr: []
    }
  },
  mounted() {
    console.log(this.$attrs)
    this.configJsonArr = this.$attrs.configJsonArr
  },
  methods: {

  }
}
</script>

input组件

<template>
  <div>
    <el-input v-bind="$attrs" v-model="value"/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: '',
    }
  }
}
</script>

这里只列出input组件的代码,其余控件类推就行了
当然我们也可以使用component去实现,但是使用component时,我们直接在 el-input 设置 v-bind="$attrs" 是不行的,原因在于动态组件传入的属性 configProps 是一个对象,而不是解构后的对象属性

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容