什么是好的 vue.js 设计模式,用于在无法使用计算属性时直接避免改变道具



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

Form.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页:

<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页:

<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页:

<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 来改变每个子组件中的 props。

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

Form.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页:

<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 的值复制到子组件中的数据变量,将数据变量绑定到输入,观察数据变量的变化,并在事件更改时将事件发送回父组件。这些发出将随新值一起出现,父级将这些新值分配给其数据变量。

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

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

这里有一些处理这种情况的方法,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 模型指令并从子级发出"input"事件。

//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 } }
...

最新更新