0

假设我的 vue.js 应用程序中有以下模式:

表单.vue:

<template>
    <form>
        <page-1 v-if="step === 1" :firstName="firstName" :lastName="lastName" @submit="step = 2"></page-1>
        <page-2 v-if="step === 2" :email="email" @submit="step = 3"></page-2>
        <page-3 v-if="step === 3" :comments="comments" @submit="submit()"></page-3>
    </form>
</template>

<script>
import Page1 from './Page1.vue';
import Page2 from './Page2.vue';
import Page3 from './Page3.vue';

export default {
    components: {
        Page1,
        Page2,
        Page3
    },

    data() {
        return {
            firstName: '',
            lastName: '',
            email: '',
            comments: '',
            step: 1
        }
    },

    methods: {
        submit() {
            // submit the form
        }
    }
}
</script>

<style>
</style>

页面1.vue:

<template>
    <label>First Name:</label>
    <input type="text" v-model="firstName"></input>
    <label>Last Name:</label>
    <input type="text" v-model="lastName"></input>
    <button @click="$emit('submit')">Next</button>
</template>

<script>
export default {
    props: {
        firstName: '',
        lastName: ''
    }
}
</script>

<style>
</style>

页面2.vue:

<template>
    <label>Email:</label>
    <input type="email" v-model="email"></input>
    <button @click="$emit('submit')">Next</button>
</template>

<script>
export default {
    props: {
        email: ''
    }
}
</script>

<style>
</style>

页面3.vue:

<template>
    <label>Comments:</label>
    <textarea v-model="comments"></textarea>
    <button @click="$emit('submit')">Submit</button>
</template>

<script>
export default {
    props: {
        comments: ''
    }
}
</script>

<style>
</style>

本质上,它是一个三页的表格。父组件是表单,每个页面都是一个子组件。父组件使用其数据跟踪表单字段,并通过 props 将它们传递到每个页面。但这是一个糟糕的设计,因为我通过在每个输入上将它们分配给 v-model 来改变每个子组件中的道具。

我在 Google 上看到的这个问题的大多数解决方案都建议使用计算属性,但是因为我使用的是 v-model,所以我需要维护双向绑定,而计算属性似乎只是单向的。所以我想知道以下是否是解决这个问题的常见模式,这被认为是一个好的设计:

表单.vue:

<template>
    <form>
        <page-1 v-if="step === 1" :firstName="firstName" :lastName="lastName" @first-name-changed="val => firstName=val" last-name-changed="val => lastName=val" @submit="step = 2"></page-1>
        <page-2 v-if="step === 2" :email="email" @email-changed="val => email=val" @submit="step = 3"></page-2>
        <page-3 v-if="step === 3" :comments="comments" @comments-changed="val => comments=val" @submit="submit()"></page-3>
    </form>
</template>

<script>
import Page1 from './Page1.vue';
import Page2 from './Page2.vue';
import Page3 from './Page3.vue';

export default {
    components: {
        Page1,
        Page2,
        Page3
    },

    data() {
        return {
            firstName: '',
            lastName: '',
            email: '',
            comments: '',
            step: 1
        }
    },

    methods: {
        submit() {
            // submit the form
        }
    }
}
</script>

<style>
</style>

页面1.vue:

<template>
    <label>First Name:</label>
    <input type="text" v-model="firstNameData"></input>
    <label>Last Name:</label>
    <input type="text" v-model="lastNameData"></input>
    <button @click="$emit('submit')">Next</button>
</template>

<script>
export default {
    props: {
        firstName: '',
        lastName: ''
    },

    data() {
        return {
            firstNameData: '',
            lastNameData: ''
        }
    },

    watch: {
        'firstNameData': function() {
            this.$emit('first-name-changed', this.firstNameData);
        },

        'lastNameData': function() {
            this.$emit('last-name-changed', this.lastNameData);
        }
    },

    created() {
        this.firstNameData = this.firstName;
        this.lastNameData = this.lastName;
    }
}
</script>

<style>
</style>

在其他两个子组件中不重复相同的模式,想法是将 props 的值复制到子组件中的数据变量,将数据变量绑定到输入,观察数据变量的变化并发出事件备份当他们改变时给父母。这些发射将带有新值,并且父级会将这些新值分配给其数据变量。

我不需要担心传递给每个子组件的道具会动态变化。我想将它们传递给子组件的唯一原因是用户可能想从草稿中重新加载表单(否则,根本不需要道具)。

我的问题是:这是一个好的设计模式吗?

4

1 回答 1

0

这里有一些处理这种情况的方法,Vuex 是一个不错的选择,但是对于三个页面,我认为没有必要。

使用.sync修饰符

<Child :name.sync="childName"></Child>

在子组件中,您可以使用事件更新值

this.$emit('update:name', newName);

您可以收听来自子级的事件并更新父级数据属性

//parent component
<div>
  <input-name @updateName="eventToUpdateName" /> <!--child component-->
</div>
...
data: () => ({ nameFromChild: '' )},
methods: {
  eventToUpdateName(value) {
    this.nameFromChild = value; // Update from child value emitted
  }
}
...

并且在子组件中

// Child component
<input v-model="name" />
...
data: () => ({ name: '' }),
// watch for changes in the name property and emit an event, and pass the value to the parent
watch: { name() { this.$emit('updateName', this.name } }
...

此外,您可以使用 v-model 指令并从子级发出“输入”事件。

//parent component
<div>
  <input-name v-model="nameFromChild" /> <!--child component-->
</div>
...
data: () => ({ nameFromChild: '' )}
...

现在在子组件中你可以拥有

// Child component
<div>
  <input v-model="name" />
</div>
data: () => ({ name: '' }),
props: { value: { type: String, default: '' },
created() { this.name = this.value }, // You can receive a default value
watch: { name() { this.$emit('input', this.name } }
...
于 2020-06-07T08:03:08.263 回答