Vue 2 - 变异道具vue-warn

Dar*_*ski 144 javascript vue.js vuejs2

我开始https://laracasts.com/series/learning-vue-step-by-step系列.我停止了Vue,Laravel和AJAX这个课程的错误:

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.list = JSON.parse(this.list);
    }
});
new Vue({
    el: '.container'
})
Run Code Online (Sandbox Code Playgroud)

我在main.js中有这段代码

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.list = JSON.parse(this.list);
    }
});
new Vue({
    el: '.container'
})
Run Code Online (Sandbox Code Playgroud)

我知道当我覆盖列表道具时问题出在created()中,但我是Vue的新手,所以我完全不知道如何解决它.任何人都知道如何(并请解释原因)来修复它?

ira*_*ira 216

这与在本地改变道具被认为是Vue 2中的反模式这一事实有关

你现在应该做什么,如果你想在本地改变一个道具,就是声明你的一个字段data使用该props值作为其初始值,然后改变副本:

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

您可以在Vue.js官方指南中阅读更多相关信息


注1:请注意,您应该为你使用相同的名称propdata,即:

data: function () { return { list: JSON.parse(this.list) } // WRONG!!
Run Code Online (Sandbox Code Playgroud)

注2:由于我觉得有一些混乱关于props反应,我建议你有一看这个线程

  • 这是一个很好的答案,但我也认为应该更新它以包含事件绑定。子级触发的事件可以更新父级,父级将更新的 props 传递给子级。这样,所有组件的事实来源仍然得到维护。还值得一提的是用于管理跨多个组件共享的状态的 Vuex。 (7认同)
  • @danb4r `.sync` 已被弃用并且[现已删除](https://v3-migration.vuejs.org/writing-changes/v-model.html)。 (2认同)

sea*_*lla 32

Vue只是警告你:你改变组件中的prop,但是当父组件重新渲染时,"list"将被覆盖,你将丢失所有的更改.所以这样做很危险.

使用计算属性,如下所示:

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
        listJson: function(){
            return JSON.parse(this.list);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 2路绑定道具怎么样? (23认同)
  • 如果你想要2路数据绑定你应该使用自定义事件,更多信息请阅读:https://vuejs.org/v2/guide/components.html#sync-Modifier你仍然无法直接修改道具,你需要一个事件和功能,为您处理道具变更. (6认同)

chr*_*ris 24

Vue模式很简单:props向下和events向上.这听起来很直接,但在编写自定义组件时很容易忘记.

从Vue 2.2.0开始,您可以使用v-model(具有计算属性).我发现这种组合在组件之间创建了一个简单,干净,一致的界面:

  • props传递给您的组件的任何内容仍然是被动的(即,它没有被克隆,也不需要watch在检测到更改时更新本地副本的功能).
  • 更改会自动发送给父级.
  • 可与多级组件一起使用.

计算属性允许单独定义setter和getter.这允许Task按如下方式重写组件:

Vue.component('Task', {
    template: '#task-template',
    props: ['list'],
    model: {
        prop: 'list',
        event: 'listchange'
    },
    computed: {
        listLocal: {
            get: function() {
                return this.list
            },
            set: function(value) {
                this.$emit('listchange', value)
            }
        }
    }
})  
Run Code Online (Sandbox Code Playgroud)

模型,其属性定义prop相关联v-model,并且该事件将在改变发射.然后,您可以从父级调用此组件,如下所示:

<Task v-model="parentList"></Task>
Run Code Online (Sandbox Code Playgroud)

所述listLocal计算出的属性提供部件内的简单的获取和设置接口(认为它像被一个私有变量).在#task-template你内部可以渲染listLocal,它将保持反应(即,如果parentList更改它将更新Task组件).您也可以listLocal通过调用setter(例如,this.listLocal = newList)进行变异,然后它将向父级发出更改.

这种模式的优点在于,您可以传递listLocalTask(使用v-model)的子组件,并且子组件的更改将传播到顶级组件.

例如,假设我们有一个单独的EditTask组件来对任务数据进行某种类型的修改.通过使用相同v-model和计算的属性模式,我们可以传递listLocal给组件(使用v-model):

<script type="text/x-template" id="task-template">
    <div>
        <EditTask v-model="listLocal"></EditTask>
    </div>
</script>
Run Code Online (Sandbox Code Playgroud)

如果EditTask发出改变它会适当地调用set()listLocal,从而将事件传播到顶层.同样,EditTask组件也可以使用调用其他子组件(如表单元素)v-model.

  • 我认为这是向父组件发出更改的更轻松的方式。 (2认同)

For*_*ame 18

如果你正在使用Lodash,你可以在返回之前克隆道具.如果您在父项和子项上修改该prop,则此模式很有用.

假设我们在组件网格上有道具列表.

在父组件中

<grid :list.sync="list"></grid>
Run Code Online (Sandbox Code Playgroud)

在子组件中

props: ['list'],
methods:{
    doSomethingOnClick(entry){
        let modifiedList = _.clone(this.list)
        modifiedList = _.uniq(modifiedList) // Removes duplicates
        this.$emit('update:list', modifiedList)
    }
}
Run Code Online (Sandbox Code Playgroud)


edi*_*xue 17

道具失败,事件发生了.这是Vue的模式.关键是,如果你试图改变从父母传递的道具.它不起作用,它只是被父组件重复覆盖.子组件只能发出事件来通知父组件做某事.如果您不喜欢这些限制,可以使用VUEX(实际上这种模式会吸引复杂的组件结构,您应该使用VUEX!)

  • 还有一个使用事件总线的选项 (2认同)

小智 12

您不应该更改子组件中props的值.如果你真的需要改变它,你可以使用.sync.像这样

<your-component :list.sync="list"></your-component>

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.$emit('update:list', JSON.parse(this.list))
    }
});
new Vue({
    el: '.container'
})
Run Code Online (Sandbox Code Playgroud)


Vec*_*yte 8

答案很简单,您应该通过将值分配给一些局部组件变量(可以是数据属性,使用 getter、setter 或 watchers 计算)来打破直接 prop 突变。

这是一个使用观察者的简单解决方案。

<template>
  <input
    v-model="input"
    @input="updateInput" 
    @change="updateInput"
  />

</template>

<script>
  export default {
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      input: '',
    };
  },
  watch: {
    value: {
      handler(after) {
        this.input = after;
      },
      immediate: true,
    },
  },
  methods: {
    updateInput() {
      this.$emit('input', this.input);
    },
  },
};
</script>
Run Code Online (Sandbox Code Playgroud)

这是我用来创建任何数据输入组件的东西,它工作得很好。任何从父级发送(v-model(ed))的新数据都将被值观察者监视并分配给输入变量,一旦收到输入,我们就可以捕获该动作并向父级发出输入提示数据已输入从表单元素。


小智 6

根据VueJs 2.0,你不应该改变组件内的prop.他们只是由父母变异.因此,您应该在具有不同名称的数据中定义变量,并通过观察实际道具来更新它们.如果父级更改了列表道具,您可以解析它并将其分配给mutableList.这是一个完整的解决方案.

Vue.component('task', {
    template: ´<ul>
                  <li v-for="item in mutableList">
                      {{item.name}}
                  </li>
              </ul>´,
    props: ['list'],
    data: function () {
        return {
            mutableList = JSON.parse(this.list);
        }
    },
    watch:{
        list: function(){
            this.mutableList = JSON.parse(this.list);
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

它使用mutableList来渲染您的模板,从而使您的列表支持在组件中保持安全.


小智 5

我也遇到了这个问题。我使用$onand后警告消失了$emit。它类似于使用$on$emit建议将数据从子组件发送到父组件。


小智 5

单向数据流,\n根据 https://v2.vuejs.org/v2/guide/components.html,组件遵循单向\n数据流,\n所有 props 在子属性和父属性,当父属性更新时,它将流向子组件,而不是相反,这可以防止子组件意外改变父组件,从而使您的应用程序的数据流动更难理解。

\n

此外,每次父组件更新所有 props 时,子组件都会刷新为最新值。这意味着您不应该尝试改变子组件内的 prop。如果您这样做,.vue 将在控制台中警告您。\n

\n

通常有两种情况会导致\xe2\x80\x99 想要改变 prop:\nprop 用于传入初始值;子组件随后希望将其用作本地数据属性。\nprop 作为需要转换的原始值传入。\n这些用例的正确答案是:\n定义使用该 prop 的本地数据属性\xe2\x80\x99s初始值作为其初始值:

\n
props: [\'initialCounter\'],\ndata: function () {\n  return { counter: this.initialCounter }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

定义一个根据 prop\xe2\x80\x99s 值计算得出的计算属性:

\n
props: [\'size\'],\ncomputed: {\n  normalizedSize: function () {\n    return this.size.trim().toLowerCase()\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


小智 5

不要直接在组件中更改道具,如果需要更改,请设置一个新属性,如下所示:

data () {
    return () {
        listClone: this.list
    }
}
Run Code Online (Sandbox Code Playgroud)

并更改listClone的值。


Mr.*_*Web 5

我想给出这个答案,这有助于避免使用大量代码、观察器和计算属性。在某些情况下,这可能是一个很好的解决方案:

Props 旨在提供单向通信。

当你有一个show/hide带有 prop 的模态按钮时,对我来说最好的解决方案是发出一个事件:

<button @click="$emit('close')">Close Modal</button>
Run Code Online (Sandbox Code Playgroud)

然后将侦听器添加到模态元素:

<modal :show="show" @close="show = false"></modal>
Run Code Online (Sandbox Code Playgroud)

(在这种情况下,该 prop可能是不必要的,因为您可以直接在基本模态上show使用 easy )v-if="show"