Vue 两种方式的 prop 绑定

Posted

技术标签:

【中文标题】Vue 两种方式的 prop 绑定【英文标题】:Vue two way prop binding 【发布时间】:2018-08-05 09:15:51 【问题描述】:

以下是我当前的结构(不起作用)。

父组件:

<template>
<field-input ref="title" :field.sync="title" />
</template>

<script>
import Field from './input/Field'
export default 
  components: 
    'field-input': Field
  ,
  data() 
    return 
      title: 
        value: '',
        warn: false
      
    
  

</script>

子组件:

<template>
<div>
  <input type="text" v-model="field.value">
  <p v-bind:class=" 'is-invisible' : !field.warn ">Some text</p>
</div>
</template>

<script>
export default 
  props: ['field']

</script>

要求是:

如果 parent 的数据 title.warn 在 parent 中发生变化,则应该更新 child 的 class 绑定 (field.warn)。 如果孩子的&lt;input&gt; 已更新(field.value),则应更新父母的title.value

实现这一目标的最简洁有效的解决方案是什么?

【问题讨论】:

所有的 props 在子属性和父属性之间形成一个单向向下的绑定:当父属性更新时,它会向下流向子属性,反之则不会.这可以防止子组件意外改变父组件的状态,从而使您的应用程序的数据流更难理解。 [...] 这意味着您应该尝试改变子组件内的道具。如果你这样做了,Vue 会在控制台中警告你。 vuejs.org/v2/guide/components.html#One-Way-Data-Flow 对于您的第一个用例,只需将 title.warn 作为道具传递给孩子。对于您的第二个用例,将对处理程序的引用传递给子级。在子节点上,发出触发此处理程序的事件。 您基本上是在自制v-model,文档中概述了如何操作。绑定 prop 并发出事件。 你的代码应该符合要求,见codesandbox.io/s/421m2611p4,但你说不行?也许是其他问题。请查看有关同步的 vue 文档,因为您没有正确使用它vuejs.org/v2/guide/components.html#sync-Modifier 【参考方案1】:

不要将子组件的&lt;input&gt; 绑定到父组件的title.value(如&lt;input type="text" v-model="field.value"&gt;)。这是known bad practice,能够让您的应用程序的数据流更难理解。

要求是:

如果 parent 的数据 title.warn 在 parent 中发生变化,则应该更新 child 的 class 绑定 (field.warn)。

这很简单,只需创建一个 warn 属性并将其从父级传递给子级。

父母(将道具传递给孩子):

<field-input ref="title" :warn="title.warn" />

子/模板(使用道具--仅阅读):

<p v-bind:class=" 'is-invisible' : !warn ">Some text</p>

Child/javascript(声明 prop 及其预期类型):

export default 
  props: warn: Boolean

请注意,在模板中它是!warn,而不是!title.warn。此外,您应该将warn 声明为Boolean 属性,因为如果您不这样做,父级可能会使用会产生意外结果的字符串(例如&lt;field-input warn="false" /&gt;)(!"false" 实际上是false,而不是@987654343 @)。

如果孩子的&lt;input&gt; 被更新(field.value),那么父母的title.value 应该被更新。

您在这里有几个可能的选项 (like using .sync in a prop),但我认为在这种情况下最干净的解决方案是在父级上创建一个 value prop and use v-model

父母(使用v-model绑定道具):

<field-input ref="title" v-model="title.value" />

子/模板(使用道具作为初始值并在它发生变化时发出input事件):

<input type="text" :value="value" @input="$emit('input', $event.target.value)">

Child/JavaScript(声明 prop 及其预期类型):

export default 
  props: value: String

Click here for a working DEMO 将这两种解决方案放在一起。

【讨论】:

谢谢。关于第一个:所以 vue 不会更新具有嵌套对象的道具?这就是你内联属性的原因吗?关于第二个:为什么不在孩子中使用 v-model ?因为我们需要 $emit 事件给父级,默认情况下 v-model 不会执行? 嵌套道具更新也可以。好吧,归根结底,我的逻辑也没问题。几乎没有额外的逻辑错误。 您好,抱歉耽搁了(看到电话上的评论,无法马上接听)。所以,是的,更新prop 对象的 nested 属性的值会起作用,但这是不受欢迎的,它被视为一种不好的做法。文档在这里继续:vuejs.org/v2/guide/components.html#One-Way-Data-Flow(底部):请注意,JavaScript 中的对象和数组是通过引用传递的,因此如果 prop 是数组或对象,则在子对象内部改变对象或数组本身会影响父状态。 据信,从长远来看,这会使您的应用程序逻辑流更难理解。 为什么不在子级中使用 v-model? 因为子级中的v-model="value" 与尝试改变value @987654360 的:value="value" @input="value = $event.target.value"&gt; 相同@ -- 如果你尝试它,它会显示一个警告 [Vue warn]: Avoid mutating a prop directly,因为只要父组件重新渲染,该值就会被覆盖。相反,使用基于道具值的数据或计算属性。正在变异的道具:“值”. 我们$emitting 事件是因为我们想将子元素的&lt;input&gt;input 事件“转发”到父元素。当此类事件到达父级时,它会更新父级value。但是请注意,此更新是在父级(本地)中进行的,而不是在子级中进行的。这是一个很大的区别。【参考方案2】:

有几种方法可以实现双向数据绑定:

    使用props on components 使用v-model attribute 使用sync modifier 使用Vuex

对于双向绑定,请记住,它可能会导致难以维护的突变链, 引用自文档:

不幸的是,真正的双向绑定会产生维护问题,因为子组件可以使父组件发生变异,而该变异的来源在父组件和子组件中都不明显。

以下是可用方法的一些详细信息:

1.) 在组件上使用道具

使用 props 进行双向绑定是not usually advised,但可能的是,通过传递一个对象或数组,您可以更改该对象的属性,并且它将在子级和父级中观察到,而 Vue 不会在控制台中打印警告。

每次父组件更新时,子组件中的所有props 组件将使用最新值刷新。这意味着你 不应尝试改变子组件内的道具

道具易于使用,是解决最常见问题的理想方法。 由于how Vue observes changes,所有属性都需要在对象上可用,否则它们将不会是反应性的。 如果在 Vue 完成使它们可观察之后添加任何属性,则必须使用 'set'。

 //Normal usage
 Vue.set(aVariable, 'aNewProp', 42);
 //This is how to use it in Nuxt
 this.$set(this.historyEntry, 'date', new Date());

该对象对组件和父级都是响应式的:

如果您将对象/数组作为道具传递,它会自动进行双向同步 - 更改 子,它在父中被改变了。

如果您传递简单的值(字符串、数字) 通过道具,您必须明确使用.sync modifier

引自 --> https://***.com/a/35723888/1087372

2.) 使用 v-model 属性

v-model 属性是一种语法糖,可以在父子节点之间轻松进行双向绑定。它与同步修饰符的作用相同,只是它使用特定的道具和特定的事件进行绑定

这个:

 <input v-model="searchText">

和这个是一样的:

 <input
   v-bind:value="searchText"
   v-on:input="searchText = $event.target.value"
 >

prop 必须是 value 且 event 必须是 input

3.) 使用同步修饰符

sync 修饰符也是语法糖,与 v-model 的作用相同,只是 prop 和 event 名称由所使用的任何内容设置。

在父级中可以如下使用:

 <text-document v-bind:title.sync="doc.title"></text-document>

可以从子级发出事件以通知父级任何更改:

 this.$emit('update:title', newTitle)

4.) 使用 Vuex

Vuex 是一个state manager,可以从每个组件访问。 可以订阅更改。

通过使用 Vuex 存储,可以更轻松地查看数据突变的流动,并且它们是明确定义的。通过使用vue developer tools,可以轻松调试和回滚所做的更改。

这种方法需要更多样板,但如果在整个项目中使用,它会成为一种更清晰的方式来定义如何进行更改以及从何处进行更改。

见getting started guide

【讨论】:

以上是关于Vue 两种方式的 prop 绑定的主要内容,如果未能解决你的问题,请参考以下文章

Vue中双向绑定props的正确方式是啥?

vue父子组件数据传输以及实现父子组件数据双向绑定

如何在Vue2中实现组件props双向绑定

vue子父组件通信怎么实现

vue中的几种校验方式

Vue-google-map 自动完成两种方式绑定不起作用