【问题标题】:Vue JS How to correct "Avoid mutating a prop directly"?Vue JS如何纠正“避免直接改变道具”?
【发布时间】:2021-05-10 12:18:11
【问题描述】:

我有一个带有按钮的主要组件视图:

    <mdb-btn
                      color="light-blue"
                      outline="white"
                      rounded
                      @click="showLoginRegister = true"
                      icon="users"
                      iconRight
                  >Inscription / Connexion</mdb-btn>

  export default {
  name: "Landing",
  components: {
    ...
  },
  methods: {
  },
  data() {
    return {
      showLoginRegister: false
    };
  }
};

在这个父视图中有一个子组件(一个模态窗口):

<modal-inscription :showLoginRegister="showLoginRegister"></modal-inscription>

在模态铭文组件中:

<mdb-modal :show="showLoginRegister" @close="showLoginRegister = false">...

我添加了一个 prop 属性:

props: ['showLoginRegister'],

当我单击按钮时,它会出现模式窗口,但我在 js 控制台中有此错误消息:“[Vue warn]: Avoid mutating a prop directly because the value will be overwritten when the parent component re-渲染。相反,使用基于道具值的数据或计算属性。道具正在变异:“showLoginRegister”

我理解该消息,但如何通过在子组件中使用局部变量来避免它?

或者也许我不使用 rignt 方法... 感谢您的帮助

【问题讨论】:

    标签: javascript vue.js


    【解决方案1】:

    我不确定您的结构,但您可以获得与以下相同的结构,并且它应该以相同的方式工作,只需将 false 值发送给您的父母。
    只需将数据发送回父级(来自子级的$emit)并更新父级中的给定道具!文档中有关此模式的更多信息:https://vuejs.org/v2/guide/components.html#Emitting-a-Value-With-an-Event

    computed setters 也可能有所帮助,但理解起来有点复杂,所以最好坚持一个基本的用例。 v-model syntax on components 也是如此。


    您需要在组件之间建立 props/emit(也称为父/子)关系,例如

    Parent.vue

    <template>
      <div>
        <child :name="name" @update-name="updateName"></child>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          name: 'bob',
        }
      },
      methods: {
        updateName(newName) {
          this.name = newName
        },
      },
      components: {
        Child: () => import('./components/child'),
      },
    }
    </script>
    

    Child.vue

    <template>
      <div>
        <p>{{ name }}</p>
        <button @click="changeName">append nice</button>
      </div>
    </template>
    
    <script>
    export default {
      props: ['name'],
      methods: {
        changeName() {
          this.$emit('update-name', this.name + ' nice')
        },
      },
    }
    </script>
    

    Michael Thiessen 也就此发表了一篇博文:https://michaelnthiessen.com/avoid-mutating-prop-directly/

    【讨论】:

    • 感谢您的回答,但在我的情况下,按钮位于父视图中而不是子视图中。
    • 我不明白你的结构。如果您可以更好地解释它或制作可复制的链接,将能够提供帮助,但在这里,我不明白。
    【解决方案2】:

    正如警告消息所说 - 如果您更改道具,它不会影响父数据,并且在父组件重新渲染后,您的道具将被覆盖。 为避免这种情况,您应该从子组件中 $emit 事件并在父组件中更改 prop。

    <mdb-modal :show="showLoginRegister" @close="$emit('close-login')">
    
        <mdb-btn
                          color="light-blue"
                          outline="white"
                          rounded
                          @click="showLoginRegister = true"
                          icon="users"
                          iconRight
                          @close-login="showLoginRegister = false"
                      >Inscription / Connexion</mdb-btn>
    
      export default {
      name: "Landing",
      components: {
        ...
      },
      methods: {
      },
      data() {
        return {
          showLoginRegister: false
        };
      }
    };
    

    您也可以使用 Vuex、Event Bus 或 $root/$parent 直接在组件之间进行通信。

    【讨论】:

    • Vuex 不会阻止数据突变(仍然需要使用 cloneDeep 传递对象的副本)并且 eventBus 不是可以使用的东西,$root/$parent 也不是(在通常的工作流程中确实不推荐使用) )。在这里,它是一个直接的 n+1 结构,所以完全矫枉过正,不推荐。
    • 在您的回答中,我看不出与我的代码有什么不同,仍然使用 showLoginRegister。子组件如何知道父点击?
    猜你喜欢
    • 2019-11-07
    • 2018-11-26
    • 2020-06-23
    • 1970-01-01
    • 2018-03-05
    • 2019-02-07
    • 2018-08-25
    • 2020-09-24
    • 2020-11-16
    相关资源
    最近更新 更多