如何在 Vue 中创建数据驱动的用户界面 基础知识 属性和事件是什么?你需要提前了解所有属性吗?表单呢?数据绑定 使生成器可复用 使用生成器

2025-06-05

如何在 Vue 中创建数据驱动的用户界面

基础知识

道具和事件怎么样?

您需要预先了解所有道具吗?

那么表格呢?

数据绑定

使生成器可重复使用

使用生成器

虽然我们通常知道在应用程序中构建大多数视图时需要哪些组件,但有时我们直到运行时才知道它们是什么。

这意味着我们需要根据应用程序状态、用户偏好设置或 API 响应来构建屏幕。一种常见的情况是创建动态表单,其中所需的问题和组件要么由 JSON 对象配置,要么字段会根据用户的回答而变化。

所有现代 JavaScript 框架都有处理动态组件的方法。这篇博文将向您展示如何在 Vue.JS 中实现这一点,它为上述场景提供了一个非常优雅且简单的解决方案。

一旦您了解使用 Vue.JS 可以多么轻松地实现这一点,您可能会受到启发并看到以前从未考虑过的动态组件应用程序!

我们必须先学会走路,然后才能跑步,因此,首先我将介绍动态组件的基础知识,然后深入研究如何使用这些概念来构建您自己的动态表单构建器。

基础知识

Vue 有一个名为 (fittingly) 的内置组件。你可以在VueJS 指南的动态组件部分<component>查看完整的详细信息

指南说:

“您可以使用相同的挂载点,并使用保留元素在多个组件之间动态切换,并动态绑定到其属性。”

这意味着组件之间的交换可以非常简单:

<component :is="componentType">
Enter fullscreen mode Exit fullscreen mode

让我们进一步充实一下,看看到底发生了什么。我们将创建两个名为DynamicOne和 的组件DynamicTwo——目前 One 和 Two 是相同的,所以我就不重复它们的代码了:

<template>
  <div>Dynamic Component One</div>
</template>
<script>
export default {
  name: 'DynamicOne',
}
</script>
Enter fullscreen mode Exit fullscreen mode

为了快速演示如何在它们之间切换,我们将在 App.vue 中设置我们的组件:

import DynamicOne from './components/DynamicOne.vue'
import DynamicTwo from './components/DynamicTwo.vue'

export default {
  name: 'app',
  components: {
    DynamicOne, DynamicTwo
  },
  data() {
    return {
      showWhich: 'DynamicOne'
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

注意:showWhich 数据属性是 DynamicOne 的字符串值 - 这是在组件上的 components 对象中创建的属性名称。

在我们的模板中,我们将设置两个按钮来在两个动态组件之间切换:

<button @click="showWhich = 'DynamicOne'">Show Component One</button>
<button @click="showWhich = 'DynamicTwo'">Show Component Two</button>

<component :is="showWhich"></component>
Enter fullscreen mode Exit fullscreen mode

单击按钮将DynamicOne与交换DynamicTwo

这时,你可能会想:“那又怎么样?这很方便 - 但我也可以同样轻松地使用 v-if。”

当您意识到它的工作方式与任何其他组件一样时,这个示例就开始闪耀<component>,并且可以与指令结合使用,例如v-for遍历集合,或使其:is可绑定到输入道具,数据道具或计算属性。

道具和事件怎么样?

组件并非孤立存在——它们需要一种与周围环境进行通信的方式。在 Vue 中,这通过 props 和事件实现。

您可以像任何其他组件一样在动态组件上指定属性和事件绑定,并且如果加载的组件不需要该属性,则 Vue 不会抱怨未知的属性或属性。

让我们修改一下组件来显示问候语。一个组件只接受 firstName 和 lastName,另一个组件则接受 firstName、lastName 和 title。

对于事件,我们将在 DynamicOne 中添加一个按钮,该按钮将发出一个名为“upperCase”的事件,并在 DynamicTwo 中添加一个按钮,该按钮将发出一个名为“lowerCase”的事件。

把它们放在一起,使用动态组件开始看起来像这样:

<component 
    :is="showWhich" 
    :firstName="person.firstName"
    :lastName="person.lastName"
    :title="person.title"
    @upperCase="switchCase('upperCase')"
    @lowerCase="switchCase('lowerCase')">
</component>
Enter fullscreen mode Exit fullscreen mode

并不是每个属性或事件都需要在我们正在切换的动态组件上定义。

您需要预先了解所有道具吗?

此时,您可能会想,“如果组件是动态的,并且不是每个组件都需要知道每个可能的道具 - 我是否需要预先知道道具,并在模板中声明它们?”

值得庆幸的是,答案是否定的。Vue 提供了一个快捷方式,你可以使用 将对象的所有键绑定到组件的 props 上v-bind

这将模板简化为:

<component 
    :is="showWhich" 
    v-bind="person"
    @upperCase="switchCase('upperCase')"
    @lowerCase="switchCase('lowerCase')">
</component>
Enter fullscreen mode Exit fullscreen mode

那么表格呢?

现在我们有了动态组件的构建块,我们可以开始在其他 Vue 基础之上构建表单生成器。

让我们从基本的表单模式开始——一个描述表单字段、标签、选项等信息的 JSON 对象。首先,我们将讨论以下内容:

  • 文本和数字输入字段
  • 选择列表

起始模式如下:

schema: [{
    fieldType: "SelectList",
    name: "title",
    multi: false,
    label: "Title",
    options: ["Ms", "Mr", "Mx", "Dr", "Madam", "Lord"],
  },
  {
    fieldType: "TextInput",
    placeholder: "First Name",
    label: "First Name",
    name: "firstName",
  },
  {
    fieldType: "TextInput",
    placeholder: "Last Name",
    label: "Last Name",
    name: "lastName",
  },
  {
    fieldType: "NumberInput",
    placeholder: "Age",
    name: "age",
    label: "Age",
    minValue: 0,
  },
]
Enter fullscreen mode Exit fullscreen mode

非常简单 - 标签、占位符等 - 对于选择列表,是可能的选项列表。

对于此示例,我们将保持这些组件的实现简单。

文本输入.vue

<template>
<div>
    <label>{{label}}</label>
    <input type="text"
         :name="name"
          placeholder="placeholder">
</div>
</template>
<script>
export default {
  name: 'TextInput',
  props: ['placeholder', 'label', 'name']
}
</script>
Enter fullscreen mode Exit fullscreen mode

选择列表.vue

<template>
  <div>
    <label>{{label}}</label>
    <select :multiple="multi">
      <option v-for="option in options"
              :key="option">
        {{option}}
      </option>
    </select>
  </div>
</template>
<script>
export default {
  name: 'SelectList',
  props: ['multi', 'options', 'name', 'label']
}
</script>
Enter fullscreen mode Exit fullscreen mode

要根据此模式生成表单,请添加以下内容:

<component v-for="(field, index) in schema"
  :key="index"
  :is="field.fieldType"
  v-bind="field">
</component>
Enter fullscreen mode Exit fullscreen mode

其结果如下:

数据绑定

如果生成了一个表单,但没有绑定数据,这还有用吗?可能不行。我们目前正在生成一个表单,但没有办法绑定数据。

您的第一反应可能是向模式添加一个值属性,并在组件中v-model像这样使用:

<input type="text" 
    :name="name"
    v-model="value"
    :placeholder="placeholder">
Enter fullscreen mode Exit fullscreen mode

这种方法存在一些潜在的陷阱,但我们最关心的是 Vue 会给我们一个错误/警告:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"

found in

---> <TextInput> at src/components/v4/TextInput.vue
       <FormsDemo> at src/components/DemoFour.vue
         <App> at src/App.vue
           <Root>
Enter fullscreen mode Exit fullscreen mode

虽然 Vue 确实提供了一些辅助函数来简化组件状态的双向绑定,但该框架仍然使用单向数据流。我们尝试在组件内部直接修改父级数据,因此 Vue 发出了警告。

更仔细地看一下 v-model,它并没有那么多神奇之处,所以让我们按照 [Vue 表单输入组件指南]( https://vuejs.org/v2/guide/components-custom-events.html#Binding-Native-Events-to-Components_ ) 中的说明进行分解。

<input v-model="something">
Enter fullscreen mode Exit fullscreen mode

类似于:

<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">
Enter fullscreen mode Exit fullscreen mode

随着魔法的揭晓,我们想要实现的目标是:

  • 让父组件向子组件提供值
  • 让父母知道值已更新

我们通过绑定:value并发出@input事件来通知父级某些事情已经发生变化来实现这一点。

让我们看看我们的 TextInput 组件:

 <div>
  <label>{{ label }}</label>
  <input
    type="text"
    :name="name"
    :value="value"
    @input="$emit('input',$event.target.value)"
    :placeholder="placeholder"
  />
</div>

Enter fullscreen mode Exit fullscreen mode

由于父组件负责提供值,因此它也负责处理与其自身组件状态的绑定。为此,我们可以在组件标签上使用 v-model :

FormGenerator.vue

<component v-for="(field, index) in schema"
    :key="index"
    :is="field.fieldType"
    v-model="formData[field.name]"
    v-bind="field">
</component>
Enter fullscreen mode Exit fullscreen mode

注意我们是如何使用的v-model="formData[field.name]"。我们需要在 data 属性上提供一个对象来实现这一点:

export default {
  data() {
    return {
      formData: {
        firstName: 'Evan'
      },
}
Enter fullscreen mode Exit fullscreen mode

我们可以将对象留空,或者如果我们想要设置一些初始字段值,我们可以在这里指定它们。

现在我们已经完成了表单的生成,很明显这个组件承担了相当多的责任。

虽然这不是复杂的代码,但如果表单生成器本身是一个可重复使用的组件就好了。

使生成器可重复使用

对于这个表单生成器,我们希望将模式作为道具传递给它,并能够在组件之间设置数据绑定。

当使用生成器时,模板变成:

GeneratorDemo.vue

<form-generator :schema="schema" v-model="formData">
</form-generator>
Enter fullscreen mode Exit fullscreen mode

这大大简化了父组件。它只关心 FormGenerator,而不关心每个可能用到的输入类型、事件连接等等。

接下来,创建一个名为 的组件FormGenerator。这基本上就是复制粘贴初始代码,并进行一些细微但关键的调整:

  • 更改为v-model:value事件@input处理
  • 添加道具价值和架构
  • 实施updateForm

FormGenerator组件变为:

FormGenerator.vue

<template>
  <component v-for="(field, index) in schema"
             :key="index"
             :is="field.fieldType"
             :value="formData[field.name]"
             @input="updateForm(field.name, $event)"
             v-bind="field">
    </component>
</template>
<script>
import NumberInput from '@/components/v5/NumberInput'
import SelectList from '@/components/v5/SelectList'
import TextInput from '@/components/v5/TextInput'

export default {
  name: "FormGenerator",
  components: { NumberInput, SelectList, TextInput },
  props: ['schema', 'value'],
  data() {
    return {
      formData: this.value || {}
    };
  },
  methods: {
    updateForm(fieldName, value) {
      this.$set(this.formData, fieldName, value);
      this.$emit('input', this.formData)
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

由于该formData属性不知道我们可以传入的每个可能的字段,this.$set因此我们希望使用 Vue 的反应系统来跟踪任何更改,并允许 FormGenerator 组件跟踪其自己的内部状态。

现在我们有一个基本的、可重复使用的表单生成器。

使用生成器

<template>
  <form-generator :schema="schema" v-model="formData">
  </form-generator>
</template>
<script>
import FormGenerator from '@/components/v5/FormGenerator'

export default {
  name: "GeneratorDemo",
  components: { FormGenerator },
  data() {
    return {
      formData: {
        firstName: 'Evan'
      },
      schema: [{ /* .... */ },
}
</script>
Enter fullscreen mode Exit fullscreen mode

现在你已经了解了表单生成器如何利用 Vue 中动态组件的基础知识来创建一些高度动态、数据驱动的 UI,

我鼓励您在GitHub上试用此示例代码,或在 [CodeSandbox] 上进行实验。如果您有任何疑问或想与我们探讨,请随时联系我们,在下方评论或通过以下方式联系我们:

注:本文最初于 2018 年 3 月 7 日发表在rangle.io博客上

文章来源:https://dev.to/eschultz/how-to-create-data-driven-user-interfaces-in-vue-3k6c
PREV
Git 和 GitHub 简介 那么...🤔
NEXT
你应该知道的 7 个 CSS 技巧!1. 禁用链接 2. 禁用文本选择 3. 调整图片大小以适应页面 4. 自定义插入符号颜色 5. 禁用文本区域大小调整 6. 使图片适应页面 7. 占位符颜色