在 Vue 中将对象属性与子组件同步
Posted
技术标签:
【中文标题】在 Vue 中将对象属性与子组件同步【英文标题】:Syncing an object property with child components in Vue 【发布时间】:2019-12-01 20:56:09 【问题描述】:背景
我正在使用 Typescript 3.5.3 中基于类的组件在 Vue.js 2.6.10 中构建一个包含大量输入的大型表单。 (在下面的代码 sn-ps 中,我已将其转换为 JS,因为我确信 TS 不是问题。)为了不让用户不知所措,表单被分解为多个组件;一次只向用户显示一个。
父组件
<template>
<form>
<Basic :Container.sync="Container"/>
<!-- More custom components -->
<output>Container.name</output>
</form>
</template>
Container
对象是要收集的所有数据的包装器。您会看到我将整个对象传递给子组件,而不是只传递请求给子组件的属性。这似乎是固执己见,但我有我的理由。
Form 组件归结为:
// Imports left out for brevity
@Component( components: Basic, , )
export default class Form extends Vue
private Container
async created()
this.Container =
name: null
setInterval(()=>console.log(this.Container.name), 5000)
// Logs whatever will be entered in the input in the Basic component
子组件
引用的组件Basic
是一个普通的输入元素:
<template>
<input v-model="Container.name" @input="emitUpdate">
</template>
使用同样简单的组件定义:
// Imports left out for brevity
@Component()
export default class Basic extends Vue
@Prop() Container
emitUpdate()
console.log(this.Container.name) // Logs whatever was entered in the input
this.$emit('update:container', this.Container)
问题
我期望看到的是要更新的容器,以及第一个 html sn-p 中的 <output>
以显示当前名称。然而,即使变量发生了变化,它也不会,正如两个控制台日志所证明的那样,它们都正确地注册了输入值。
似乎 Vue 的响应性无法将更改注册到 Container
对象,因此不会重新渲染任何 HTML。我该如何解决这个问题?
尝试的解决方案
我尝试使用<Basic … :Container.sync="Container" @Container="Container = $event" />
明确监听更新事件,但无济于事。
考虑到 Vue 可能不会对对象属性的变化做出反应(毕竟,观察对象也需要设置 deep
选项),我还尝试单独传递 Container 的属性,导致以下结果变化:
在表单的 html 中object is used as the argument to v-bind(我尝试过使用和不使用@update:name
):
<Basic … v-bind.sync="Container" @update:name="name=$event"/>
在子组件中,我已更改为 getter 和 setter,以避开“Thou shall not change Props”警告:
<input v-model="Name" @input="emitUpdate">`
@Prop() name
get Name() return this.name
set Name(newName) this.$emit('update:name', newName)
【问题讨论】:
我可以使用纯 'JS' 编写解决方案,但不能使用TS
,我应该吗?
@EvilArthas 是的,请这样做。问题很可能不是源于打字稿。
【参考方案1】:
在我看来,这里的问题是您在 created
钩子内初始化了 Container
对象,这使得它没有反应。如果你想让一个状态响应,你需要将它初始化为初始数据(或使用Vue.observable API)。我对 TS 了解不多,但看看the docs,我认为你需要做的就是......
@Component( components: Basic, , )
export default class Form extends Vue
Container: object =
name: null
【讨论】:
确实如此!我的印象是,通过在类中简单地声明Container
,它将获得undefined
的初始值,这足以启用反应性。当我实施您建议的更改(或最初将 Container 设置为 null
时,反应性已启用。【参考方案2】:
由于 Vue 不允许我们改变 props,我们需要创建 Container
的本地副本,所以让我们为其使用 computed
属性。当我们尝试访问这个属性时,我们得到了我们的Component
,但是当我们想要设置它时,我们只是触发了我们的父组件的事件,新值为Component
,我们将通过props
获得。
Reference
<Basic v-model="container" />
// Basic.vue
<input v-model="localContainer.name">
props:
value:
type: Object,
required: true
,
computed:
localContainer:
get()
return this.value
,
set(container)
this.$emit('input', container)
【讨论】:
以上是关于在 Vue 中将对象属性与子组件同步的主要内容,如果未能解决你的问题,请参考以下文章