首页 文章

Vue 2 - 变异道具vue-warn

提问于
浏览
86

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

vue.js:2574 [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: "list" (found in component <task>)

我在 main.js 有这段代码

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

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

17 回答

  • 0

    这与mutating a prop locally is considered an anti-pattern in Vue 2的事实有关

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

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

    您可以在Vue.js official guide上阅读更多相关信息


    Note 1: 请注意you should not use the same name for your prop and data,即:

    data: function () { return { list: JSON.parse(this.list) } // WRONG!!
    

    Note 2:I feel there is some confusion关于 props 和反应性,我建议你看一下this主题

  • 25

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

    使用像这样的计算属性:

    Vue.component('task', {
        template: '#task-template',
        props: ['list'],
        computed: {
            listJson: function(){
                return JSON.parse(this.list);
            }
        }
    });
    
  • 140

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

  • 9

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

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

    In Parent Component

    <grid :list.sync="list"></grid>
    

    In Child Component

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

    您不应该更改子组件中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'
    })
    
  • 7

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

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

    • 传递给您的组件的任何 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)
                }
            }
        }
    })
    

    model属性定义 propv-model 关联,以及更改时将发出的事件 . 然后,您可以从父级调用此组件,如下所示:

    <Task v-model="parentList"></Task>
    

    listLocal computed属性在组件中提供了一个简单的getter和setter接口(将其视为私有变量) . 在 #task-template 中,您可以渲染 listLocal 并且它将保持被动(即,如果 parentList 更改它将更新 Task 组件) . 您也可以通过调用setter(例如 this.listLocal = newList )来改变 listLocal ,它会将更改发送给父级 .

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

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

    <script type="text/x-template" id="task-template">
        <div>
            <EditTask v-model="listLocal"></EditTask>
        </div>
    </script>
    

    如果 EditTask 发出更改,它将在 listLocal 上正确调用 set() ,从而将事件传播到顶层 . 同样, EditTask 组件也可以使用 v-model 调用其他子组件(例如表单元素) .

  • 0

    根据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);
            }
        }
    });
    

    它使用mutableList来渲染你的模板,因此你可以保持你的列表支持安全零件 .

  • 4

    不要直接在components中更改道具 . 如果你需要更改,它会设置一个新属性,如下所示:

    data () {
        return () {
            listClone: this.list
        }
    }
    

    并更改listClone的值 .

  • 14

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

  • 1

    如果你想改变道具 - 使用对象 .

    <component :model="global.price"></component>
    

    零件:

    props: ['model'],
    methods: {
      changeValue: function() {
        this.model.value = "new value";
      }
    }
    
  • 15

    你需要像这样添加计算方法

    component.vue

    props: ['list'],
    computed: {
        listJson: function(){
            return JSON.parse(this.list);
        }
    }
    
  • 2

    单向数据流,根据https://vuejs.org/v2/guide/components.html,组件遵循单向数据流,所有道具形成子属性和父属性之间的单向向下绑定,当父属性更新时,它将向下流向而不是反过来,这可以防止子组件意外地改变父级's, which can make your app'的数据流更难以理解 .

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

    通常有两种情况,它很容易改变道具:道具用于传递初始值;子组件之后想要将它用作本地数据属性 . prop是作为需要转换的原始值传递的 . 这些用例的正确答案是:定义使用prop的初始值作为其初始值的本地数据属性:

    props: ['initialCounter'],
    data: function () {
      return { counter: this.initialCounter }
    }
    

    定义根据prop的值计算的计算属性:

    props: ['size'],
    computed: {
      normalizedSize: function () {
        return this.size.trim().toLowerCase()
      }
    }
    
  • 2

    Vue.js道具不会被改变,因为它被认为是Vue中的反模式 .

    您需要采取的方法是在组件上创建一个引用list的原始prop属性的数据属性

    props: ['list'],
    data: () {
      return {
        parsedList: JSON.parse(this.list)
      }
    }
    

    现在,通过组件的 data 属性引用和变异传递给组件的列表结构:-)

    如果您希望做的不仅仅是解析列表属性,那么请使用Vue组件' computed 属性 . 这允许您对道具进行更深入的突变 .

    props: ['list'],
    computed: {
      filteredJSONList: () => {
        let parsedList = JSON.parse(this.list)
        let filteredList = parsedList.filter(listItem => listItem.active)
        console.log(filteredList)
        return filteredList
      }
    }
    

    上面的示例解析了您的列表prop并将其过滤到 active list-tems,将其记录出 for schnitts and giggles 并返回它 .

    注意:模板中引用了 datacomputed 属性,例如

    <pre>{{parsedList}}</pre>
    
    <pre>{{filteredJSONList}}</pre>
    

    可以很容易地认为需要调用 computed 属性(作为方法)......但事实并非如此

  • 0

    Vue.js认为这是一种反模式 . 例如,声明和设置一些道具,如

    this.propsVal = 'new Props Value'
    

    因此,要解决此问题,您必须从props获取值到数据或Vue实例的计算属性,如下所示:

    props: ['propsVal'],
    data: function() {
       return {
           propVal: this.propsVal
       };
    },
    methods: {
    ...
    }
    

    这肯定会奏效 .

  • -4

    除上述内容外,对于有以下问题的其他人:

    "If the props value is not required and thus not always returned, the passed data would return undefined (instead of empty)". Which could mess <select> default value, I solved it by checking if the value is set in beforeMount() (and set it if not) as follows:

    JS:

    export default {
            name: 'user_register',
            data: () => ({
                oldDobMonthMutated: this.oldDobMonth,
            }),
            props: [
                'oldDobMonth',
                'dobMonths', //Used for the select loop
            ],
            beforeMount() {
               if (!this.oldDobMonth) {
                  this.oldDobMonthMutated = '';
               } else {
                  this.oldDobMonthMutated = this.oldDobMonth
               }
            }
    }
    

    Html:

    <select v-model="oldDobMonthMutated" id="dob_months" name="dob_month">
    
     <option selected="selected" disabled="disabled" hidden="hidden" value="">
     Select Month
     </option>
    
     <option v-for="dobMonth in dobMonths"
      :key="dobMonth.dob_month_slug"
      :value="dobMonth.dob_month_slug">
      {{ dobMonth.dob_month_name }}
     </option>
    
    </select>
    
  • 2
    Vue.component('task', {
        template: '#task-template',
        props: ['list'],
        computed: {
          middleData() {
            return this.list
          }
        },
        watch: {
          list(newVal, oldVal) {
            console.log(newVal)
            this.newList = newVal
          }
        },
        data() {
          return {
            newList: {}
          }
        }
    });
    new Vue({
        el: '.container'
    })
    

    也许这将满足您的需求 .

  • 4

    Enestecódigo,estoy usando un filtro para cada lista dentro de un component . declaro una vaviable buscar de forma local al componente y creo un metodo computed para usar la lista y filtrarla ..

    Vue.component('comun-list',{
                props: ['list'],
                data: function () {
                  return {
                    buscar: '',
                  }
                },
                template: `<ul class="list-group">
                                <li class="list-group-item">
                                    <input type="text" v-model="buscar" class="form-control" />
                                </li>
                                <li v-for="item in buscarList" class="list-group-item">
                                    {{item.title}}
                                </li>
                            </ul>`,
                computed: {
                    buscarList: function(){
                        return this.list.filter((item) => item.title.includes(this.buscar));
                    },
                },
            });
    

相关问题