发布于 2025-12-08 0 阅读
0

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

如何在 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