【问题标题】:How to use v-model and props passed to parent from child?如何使用从孩子传递给父母的 v-model 和道具?
【发布时间】:2019-10-17 10:26:18
【问题描述】:

我已经学习 vue 几天了,我正在尝试在孩子和父母之间传递数据/道具。 现在我有了以下孩子:

<template>
  <div>
    <input v-model="name1" placeholder="string">
    <input v-model="number1" placeholder="number">
    <p v-text="name1"></p>
    <p v-text="number1"></p>
  </div>
</template>

<script>
export default {
  name: "child",
  props: {
    name1 : String,
    number1 : Number
  }
}
</script>

然后是父母:

<template>
  <div>
    <child/>
  </div>
</template>

<script>
import child from "@/components/complexComponent4/child.vue"

export default{
  name: "parent",
  components: {
    child
  }
}
</script>

现在,当我在输入字段中输入一些文本时,它会在段落中正确显示,因为绑定到段落的道具已更改。 但是,我收到此警告:

[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: "name1"

found in

---> <Child>
       <Parent> at src/components/complexComponent4/parent.vue
         <MyComplexView4.vue> at src/views/myComplexView4.vue
           <App> at src/App.vue
             <Root>

我在互联网上的多个地方以及文档中都读到了这个错误,我发现变异道具现在被认为是一种反模式: https://michaelnthiessen.com/avoid-mutating-prop-directly

不幸的是,我并没有真正找到任何具体的和/或有用的信息来解决这个问题。特别是在 vue 以不同方式处理原始数据和对象/数组的上下文中(对象/数组通过引用传递)。

v-model 似乎在利用 vue 的力量方面发挥了重要作用,因为它支持双向绑定。因此,我不想完全忽略它,除非它的使用变得如此困难以至于无法证明收益是合理的。

【问题讨论】:

    标签: vue.js


    【解决方案1】:

    正如警告所说,您应该避免在子组件中直接改变道具。 因此,您应该从子级向父级发出一个事件,让父级知道 prop 值已更改。父母将更改道具并将其传递给孩子。

    为此,Vue 中有一个语法糖,称为 .sync 修饰符。

    所以你的组件可能是这样的。 孩子:

    <template>
      <div>
        <input 
          :value="name1" 
          @change="$emit('update:name1', $event.target.value)"
          placeholder="string"
        />
        <input 
          :value="number1" 
          @change="$emit('update:number1', $event.target.value)" 
          placeholder="number"
        />
        <p v-text="name1"></p>
        <p v-text="number1"></p>
      </div>
    </template>
    
    <script>
    export default {
      name: "child",
      props: {
        name1 : String,
        number1 : Number
      }
    }
    </script>
    

    和父母:

    <template>
      <div>
        <child :name1.sync="name1" :number1.sync="number1"/>
      </div>
    </template>
    
    <script>
    import child from "@/components/complexComponent4/child.vue"
    
    export default{
      name: "parent",
      components: {
        child
      },
      data() {
        return {
          name1: '',
          number1: ''
        }
      }
    }
    </script>
    

    或者对于更复杂的情况,您可以在子组件中使用 v-model 和计算属性以及 setter:

    <template>
      <div>
        <input 
          v-model="computedName1" 
          placeholder="string"
        />
        <input 
          v-model="computedNumber1" 
          placeholder="number"
        />
        <p v-text="name1"></p>
        <p v-text="number1"></p>
      </div>
    </template>
    
    <script>
    export default {
      name: "child",
      props: {
        name1 : String,
        number1 : Number
      },
      computed: {
        computedName1: {
          get() { return this.name1 },
          set(value) {
            // some logic
            this.$emit('update:name1', value)
          },
        computedNumber1: {
          get() { return this.number1 },
          set(value) {
            // some logic
            this.$emit('update:number1', value)
          }
        }
      }
    }
    </script>
    

    【讨论】:

    • 哇,谢谢,看起来很棒! :D 两个后续:1)由于我对 vue 非常陌生,我如何测试这种方式,父组件的重新渲染不会覆盖道具内的数据?我的意思是,这就是直接改变道具的问题,所以如果我像你展示的那样做的话应该消除这个警告,对吗?输入后我已经重新加载了页面,但是当我使用 vue 调试器检查道具时,道具又变回了空? 2) 当我在数字字段中输入数字时,我收到一条警告,提示我输入了一个字符串。如何处理?
    • @baryon123 1) 当你重新加载页面时——所有的组件都会被重新渲染。如果您想永久存储一些数据...好的,您可以将其存储在本地存储中。以 vue-persistent-state 为例。 2)用计算的setter更新了我的答案。您可以在其中进行所有类型检查和逻辑
    【解决方案2】:

    如果您打算更改传递给子项的道具,请先将其分配给子项数据。

    <template>
      <div>
        <input v-model="name" placeholder="string">
        <input v-model="number" placeholder="number">
        <p v-text="name"></p>
        <p v-text="number"></p>
      </div>
    </template>
    
    <script>
    export default {
      name: "child",
      data() {
        return {
          name: null,
          number: null
        }
      },
      props: {
        name1 : String,
        number1 : Number
      },
      mounted() {
        this.name = this.name1;
        this.number = this.number1;
      }
    }
    </script>
    

    当数据改变时,你可以$emit这些改变到父组件

    1. 使用 同步

    父母

    <child :number1.sync="number1" :name1.sync="name1" />
    

    孩子

    watch: {
        name: value => this.$emit('update:name1', value)
        number : value => this.$emit('update:number1', value)
      },
    
    1. 事件

    父母

    <child :number1="number1" :name1="name1" @changeNumber="value => number1 = value" @changeName="value => name1 = value" />
    

    孩子

    watch: {
        name: value => this.$emit('changeName', value)
        number : value => this.$emit('updateNumber', value)
      },
    

    【讨论】:

    • 看起来是完美的答案!太棒了,谢谢!
    【解决方案3】:

    vue.js 的一个指导原则是,您可以使用 props 自动更改来自父级的子级数据,但反之则不行。为了改变父组件的数据,子组件应该使用事件。您可以考虑分别为 name1 和 number1 使用两个不同的组件,并通过使这些组件适用于 v-model 以双向方式绑定值,正如 here 所描述的那样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      相关资源
      最近更新 更多