• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

    使用Vue构建动态表单生成器的实现步骤

    发布者: 浪子 | 发布时间: 2025-6-16 07:37| 查看数: 82| 评论数: 0|帖子模式

    引言

    在前端开发中,表单是非常常见的交互元素。然而,当表单的结构和字段需要根据不同的业务场景动态变化时,手动编写每个表单的代码会变得非常繁琐。Vue 作为一个流行的前端框架,提供了强大的组件化和动态渲染能力,结合 Vuelidate 库进行表单校验,我们可以轻松实现一个动态表单生成器。本文将详细介绍实现动态表单生成器的原理,并通过实战演示如何使用 Vue 构建一个灵活的动态表单。

    原理分析


    动态组件渲染

    Vue 提供了component标签,通过 :is 属性可以动态指定要渲染的组件。结合 v-for 指令,我们可以遍历 JSON 配置对象,根据配置中的组件类型动态渲染不同的表单项。例如:
    1. <template>
    2.   <div v-for="item in formConfig" :key="item.name">
    3.     <label :for="item.name">{{ item.name }}</label>
    4.     <component :is="getComponent(item.type)"
    5.       v-model="formData[item.name]"
    6.       v-bind="item.props"
    7.     />
    8.   </div>
    9. </template>
    复制代码
    在上述代码中,getComponent 方法根据 item.type 返回对应的组件,从而实现动态渲染。

    表单校验

    为了确保用户输入的数据符合业务规则,我们需要对表单进行校验。Vuelidate 是一个轻量级的 Vue 表单验证库,它提供了丰富的验证规则和便捷的使用方式。我们可以根据 JSON 配置中的 rule 字段动态生成校验规则,并使用 useVuelidate 函数对表单数据进行校验。例如:
    1. const getRules = () => {
    2.   const rules = {};
    3.   formConfig.value.forEach(i => {
    4.     if (i.rule) {
    5.       rules[i.name] = {};
    6.       if (i.rule.required === 'required') {
    7.         rules[i.name].required = required;
    8.       }
    9.       if (i.rule.maxLength) {
    10.         rules[i.name].maxLength = maxLength(i.rule.maxLength);
    11.       }
    12.       if (i.rule.minLength) {
    13.         rules[i.name].minLength = minLength(i.rule.minLength);
    14.       }
    15.     }
    16.   });
    17.   return rules;
    18. };
    19. const rules = getRules();
    20. const v$ = useVuelidate(rules, formData);
    复制代码
    插槽机制实现自定义表单项

    Vue 的插槽机制允许我们在组件内部预留一些位置,让使用者可以自定义插入内容。在动态表单生成器中,我们可以使用具名插槽,让用户自定义表单的提交按钮或其他特定的表单项。例如:
    1. <template>
    2.   <form @submit.prevent="handleSubmit">
    3.     <!-- 动态渲染表单项 -->
    4.     <div v-for="item in formConfig" :key="item.name">
    5.       <label :for="item.name">{{ item.name }}</label>
    6.       <component :is="getComponent(item.type)"
    7.         v-model="formData[item.name]"
    8.         v-bind="item.props"
    9.       />
    10.     </div>
    11.     <slot name="submit">
    12.       <button @click="handleSubmit">submit</button>
    13.     </slot>
    14.   </form>
    15. </template>
    复制代码
    实战演示


    项目初始化

    首先,我们需要创建一个新的 Vue 项目,并安装所需的依赖。可以使用 Vite 快速搭建项目:
    1. npm init vite@latest dynamic-form -- --template vue
    2. cd dynamic-form
    3. npm install
    4. npm install @vuelidate/core @vuelidate/validators
    复制代码
    编写组件


    1. DynamicForm.vue 组件

    该组件是动态表单生成器的核心组件,负责解析 JSON 配置并渲染表单。同时,增加了表单数据提交和处理的逻辑。
    1. <template>
    2.   <form @submit.prevent=handleSubmit>
    3.    
    4.     <div v-for="item in formConfig" :key="item.name">
    5.    
    6.       <label :for="item.name">{{ item.name }}</label>
    7.       <component :is="getComponent(item.type)"
    8.       v-model="formData[item.name]"
    9.       v-bind="item.props"
    10.       />
    11.     </div>
    12.     <slot name="submit">
    13.       <button @click="handleSubmit">submit</button>
    14.     </slot>
    15.   </form>
    16. </template>

    17. <script setup>
    18. import {reactive, ref}  from 'vue'
    19. import { useVuelidate } from '@vuelidate/core'
    20. import { required,maxLength } from '@vuelidate/validators'
    21. import textRenderer from './TextRenderer.vue'
    22. import selectRenderer from './SelectRenderer.vue'
    23. import dateRenderer from './DateRenderer.vue'
    24. const formData=reactive({})
    25. const props = defineProps({
    26.   formConfigJSON:{
    27.     type:String,
    28.     required:true,
    29.     validator: (configJSON) =>{
    30.       const config=JSON.parse(configJSON)
    31.       return config.every((i)=>'name' in i && 'type' in i)
    32.     },
    33.   }
    34. })

    35. const formConfig=ref(JSON.parse(props.formConfigJSON))



    36. const componentTypeMap={
    37.   'text':textRenderer,
    38.   'select':selectRenderer,
    39.   'date-range':dateRenderer,
    40. }
    41. const getComponent=(type)=>{
    42.   return componentTypeMap[type]
    43. }

    44. const handleSubmit = () => {
    45.   v$.value.$validate();
    46.   if (v$.value.$invalid) {
    47.     console.log('表单验证不通过', v$.value.$errors);
    48.     return;
    49.   }
    50.   console.log('Form submitted:', formData)

    51. }

    52. const getRules=()=>{
    53. const rules={}
    54. formConfig.value.forEach(i=>{
    55.   if(i.rule){
    56.     rules[i.name]={}
    57.     if(i.rule.required==='required'){
    58.       rules[i.name].required=required
    59.     }
    60.     if (i.rule.maxLength) {
    61.       rules[i.name].maxLength = maxLength(i.rule.maxLength)
    62.     }
    63.     if (i.rule.minLength) {
    64.       rules[i.name].minLength = minLength(i.rule.minLength)
    65.     }
    66.   }
    67.   })
    68.   console.log('Rules:', rules)
    69. return rules
    70. }
    71. const rules=getRules()
    72. const v$ = useVuelidate(rules, formData);

    73. </script>



    74. <style scoped>
    75. form {
    76.   max-width: 600px;
    77.   margin: 0 auto;
    78.   padding: 20px;
    79. }

    80. div {
    81.   margin-bottom: 20px;
    82.   display: flex;
    83.   flex-direction: column;
    84.   gap: 8px;
    85. }

    86. label {
    87.   font-weight: 500;
    88.   color: #333;
    89.   font-size: 14px;
    90. }

    91. input, select {
    92.   padding: 8px 12px;
    93.   border: 1px solid #ddd;
    94.   border-radius: 4px;
    95.   font-size: 14px;
    96.   width: 100%;
    97.   box-sizing: border-box;
    98. }

    99. input:focus, select:focus {
    100.   outline: none;
    101.   border-color: #409eff;
    102.   box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.1);
    103. }

    104. button {
    105.   padding: 10px 20px;
    106.   background-color: #409eff;
    107.   color: white;
    108.   border: none;
    109.   border-radius: 4px;
    110.   cursor: pointer;
    111.   font-size: 14px;
    112.   transition: background-color 0.3s;
    113. }

    114. button:hover {
    115.   background-color: #66b1ff;
    116. }
    117. </style>
    复制代码
    2. TextRenderer.vue 组件

    该组件用于渲染文本输入框。
    1. <script setup>
    2. defineProps(['modelValue']);
    3. const emit = defineEmits(['update:modelValue']);
    4. </script>
    5. <template>
    6.   <input type="text" :value="modelValue" @input="emit('update:modelValue', $event.target.value)">
    7. </template>
    8. <style scoped></style>
    复制代码
    3. SelectRenderer.vue 组件

    该组件用于渲染下拉选择框。
    1. <script setup>
    2. defineProps(['modelValue', 'selections']);
    3. const emit = defineEmits(['update:modelValue']);
    4. </script>
    5. <template>
    6.   <select :value="modelValue" @change="emit('update:modelValue', $event.target.value)">
    7.     <option v-for="item in selections" :key="item.value" :value="item.value">{{ item.selection }}</option>
    8.   </select>
    9. </template>
    10. <style scoped></style>
    复制代码
    错误记录
    1. defineEmits
    复制代码
    使用错误
    1. defineEmits
    复制代码
    接收的参数应该是一个数组,这里没有使用数组包裹事件名。
    1. @select
    复制代码
    事件使用错误
    :在
    1. <select>
    复制代码
    元素上,通常使用
    1. @change
    复制代码
    事件来监听选择项的变化,而不是
    1. @select
    复制代码

    4. DateRenderer.vue 组件

    该组件用于渲染日期选择框。
    1. <script setup>
    2. defineProps(['modelValue']);
    3. const emit = defineEmits(['update:modelValue']);
    4. </script>
    5. <template>
    6.   <input type="date" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
    7. </template>
    8. <style scoped></style>
    复制代码
    使用动态表单组件

    在 App.vue 中使用 DynamicForm 组件,并传入 JSON 配置。
    1. <script setup>
    2. import { ref } from 'vue';
    3. import DynamicForm from './components/DynamicForm.vue';
    4. const config = [
    5.   {
    6.     "name": "username",
    7.     "type": "text",
    8.     "rule": {"required": "required"},
    9.   },
    10.   {
    11.     "name": "userid",
    12.     "type": "text",
    13.     "rule": {"required": "required", "maxLength": "6"},
    14.   },
    15.   {
    16.     "name": "date",
    17.     "type": "date-range",
    18.     "rule": { "required": "required" },
    19.   },
    20.   {
    21.     "name": "gender",
    22.     "type": "select",
    23.     "props": {
    24.       "selections": [
    25.         { "value": "male", "selection": "male" },
    26.         { "value": "female", "selection": "female" }
    27.       ]
    28.     },
    29.     "rule": { "required": "required" },
    30.   }
    31. ];
    32. const configJSON = JSON.stringify(config);
    33. </script>
    34. <template>
    35.   <DynamicForm :formConfigJSON="configJSON"/>
    36. </template>
    37. <style scoped></style>
    复制代码
    问题


    @submit.prevent

    在 Vue.js 中,
    1. @submit.prevent
    复制代码
    是一个事件绑定语法。
    1. @submit
    复制代码
    用于监听 HTML 表单的
    1. submit
    复制代码
    事件,当用户提交表单(比如点击表单内的提交按钮)时会触发该事件。
    1. .prevent
    复制代码
    是一个修饰符,它的作用是调用
    1. event.preventDefault()
    复制代码
    方法,阻止表单的默认提交行为。在默认情况下,表单提交会导致页面刷新,使用
    1. .prevent
    复制代码
    修饰符后就可以避免页面刷新,从而让开发者可以在 JavaScript 中自定义处理表单提交的逻辑,比如通过 AJAX 发送表单数据到服务器等。 例如:
    1. <template> <form @submit.prevent="handleSubmit">
    2. <input type="text" v-model="formData.name"> <input type="submit" value="提交">
    3. </form>
    4. </template>
    5. <script>
    6. export default {
    7. data()
    8. { return { formData: { name: '' } };
    9. },
    10. methods: { handleSubmit() {
    11. // 在这里处理表单提交的逻辑,比如发送数据到服务器 console.log('表单提交了', this.formData); }
    12. }
    13. };
    14. </script>
    复制代码
    在上述代码中,
    1. @submit.prevent
    复制代码
    会阻止表单的默认提交行为,然后调用
    1. handleSubmit
    复制代码
    方法来处理表单提交。

    component标签是怎么用的

    在不同的前端框架里, 标签的使用方式有所不同,下面主要介绍 Vue.js 和 React 中 标签的使用。

    Vue.js 里 标签的使用

    在 Vue.js 中, 标签是一个动态组件,可依据不同条件来渲染不同的组件。
    基本用法
    你可以借助 :is 绑定一个组件名或者组件选项对象,以此来动态渲染组件。
    错误用法
    1. 'text': 'InputRenderer',
    2. 'select': 'SelectRenderer',
    3. 'date-range': 'DateRangeRenderer'
    复制代码
    解释

    • :is 绑定的 currentComponent 是一个变量,其值为组件名。
    • 当点击按钮时,currentComponent 的值会发生改变,从而动态渲染不同的组件。

    input的type有哪些


    v-bind绑定对象
    1. v-bind="item.props"
    复制代码
    对象展开写法,会自动将
    1. item.props
    复制代码
    对象中的每个属性绑定为组件的props。 完整语法是
    1. v-bind:attributeName="expression"
    复制代码
    1.    
    2. <component
    3.   :is="getComponent(item.type)"
    4.   v-model="formData[item.name]"
    5.   v-bind="item.props" />
    6.     其中item.props={
    7.       selections: [
    8.         { value: male, selection: male },
    9.         { value: female, selection: female }
    10.       ]
    11.     }
    12.   v-bind="item.props"等价于v-bind:selections=item.props.selections
    复制代码
    总结

    通过以上步骤,我们成功实现了一个 Vue 动态表单生成器。利用 Vue 的动态组件渲染、表单校验和插槽机制,我们可以根据不同的 JSON 配置动态生成表单,并对用户输入的数据进行校验。同时,增加了表单数据提交和处理的逻辑,模拟了将表单数据发送到后端的过程。这种方式大大提高了表单开发的灵活性和可维护性,减少了重复代码的编写。
    以上就是使用Vue构建动态表单生成器的实现步骤的详细内容,更多关于Vue动态表单生成器的资料请关注脚本之家其它相关文章!

    来源:https://www.jb51.net/javascript/339795h6o.htm
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

    最新评论

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表