Подтвердить что ты не робот

Vue 2 - Мутирующий реквизит vue-warn

Я начал серию https://laracasts.com/series/learning-vue-step-by-step. Я остановился на уроке Vue, Laravel и AJAX с этой ошибкой:

vue.js: 2574 [Vue warn]: избегайте прямого изменения свойства, так как значение будет перезаписываться при каждом повторном рендеринге родительского компонента. Вместо этого используйте данные или вычисляемое свойство на основе значения проп. Мутация пропеллера: "список" (находится в компоненте)

У меня есть этот код в main.js

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

Я знаю, что проблема в create(), когда я перезаписываю реквизит списка, но я новичок в Vue, поэтому я совершенно не знаю, как это исправить. У кого-нибудь есть идеи, как (и, пожалуйста, объясните, почему) это исправить?

4b9b3361

Ответ 1

Это связано с тем фактом, что мутирование опоры локально считается анти-паттерном в Vue 2

Что вы должны сделать сейчас, если вы хотите локально изменить мутацию, это объявить поле в ваших data которое использует значение props качестве исходного значения, а затем мутировать копию:

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

Вы можете прочитать больше об этом в официальном руководстве Vue.js


Примечание 1: Обратите внимание, что вы не должны использовать одно и то же имя для вашей prop и data, а именно:

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

Примечание 2: Поскольку я чувствую, что существует некоторая путаница в отношении props и реактивности, я предлагаю вам взглянуть на эту тему

Ответ 2

Vue просто предупреждает вас: вы меняете реквизит в компоненте, но когда родительский компонент перерисовывается, "список" будет перезаписан, и вы потеряете все свои изменения. Так что это опасно.

Используйте вычисленное свойство вместо этого:

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

Ответ 3

Паттерн Vue - это props вниз и events вверх. Это звучит прямо, но легко забыть при написании пользовательского компонента.

Начиная с версии 2.2.0 вы можете использовать v-модельвычисляемыми свойствами). Я считаю, что эта комбинация создает простой, чистый и согласованный интерфейс между компонентами:

  • Любой props, переданный вашему компоненту, остается реактивным (то есть он не клонируется и не требует функции watch для обновления локальной копии при обнаружении изменений).
  • Изменения автоматически отправляются родителю.
  • Может использоваться с несколькими уровнями компонентов.

Вычисляемое свойство позволяет устанавливать и устанавливать и определять отдельно. Это позволяет переписать компонент 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)
            }
        }
    }
})  

Свойство модели определяет, какой prop связан с v-model, и какое событие будет генерироваться при изменениях. Затем вы можете вызвать этот компонент из родительского элемента следующим образом:

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

Вычисляемое свойство listLocal обеспечивает простой интерфейс получения и установки в компоненте (воспринимайте его как частную переменную). В #task-template вы можете отрендерить listLocal, и он останется реактивным (то есть, если parentList изменится, он обновит компонент Task). Вы также можете изменить listLocal, вызвав сеттер (например, this.listLocal = newList), и он отправит изменения родительскому элементу.

Что хорошего в этом шаблоне, так это то, что вы можете передать 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 выдает изменение, он соответствующим образом вызовет set() для listLocal и, таким образом, распространяет событие на верхний уровень. Аналогично, компонент EditTask также может вызывать другие дочерние компоненты (например, элементы формы), используя v-model.

Ответ 4

Если вы используете Lodash, вы можете клонировать опору, прежде чем возвращать ее. Эта схема полезна, если вы изменяете эту опору как для родителя, так и для ребенка.

Скажем, у нас есть список поддержки на сетке компонентов.

В родительском компоненте

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

В дочернем компоненте

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

Ответ 5

Опора вниз, события вверх. Это Vue Pattern. Дело в том, что если вы попытаетесь изменить пропуски, передаваемые от родителя. Это не сработает, и просто перезаписывается родительским компонентом. Дочерний компонент может генерировать только событие, чтобы уведомить родительский компонент о выполнении чего-либо. Если вам не нравятся эти ограничения, вы можете использовать VUEX (на самом деле этот шаблон будет засасывать сложную структуру компонентов, вам следует использовать VUEX!)

Ответ 6

Вы не должны изменять значение реквизита в дочернем компоненте. Если вам действительно нужно изменить его, вы можете использовать .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

Согласно VueJs 2.0, вы не должны мутировать пропеллер внутри компонента. Они только видоизменены их родителями. Поэтому вы должны определять переменные в данных с разными именами и поддерживать их в актуальном состоянии, наблюдая за фактическими данными. В случае, если реквизиты списка изменены родителем, вы можете проанализировать его и назначить его в 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 для визуализации вашего шаблона, таким образом, вы сохраняете свой список в безопасности в компоненте.

Ответ 8

не меняйте реквизиты непосредственно в компонентах. если вам нужно изменить их, установите новое свойство, например:

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

И измените значение listClone.

Ответ 9

Я столкнулся с этой проблемой. Предупреждение исчезло после того, как я использовал $on и $emit. Это что-то вроде использования $on и $emit рекомендуется отправлять данные из дочернего компонента в родительский компонент.

Ответ 10

Если вы хотите мутировать реквизит - используйте объект.

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

компонент:

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

Ответ 11

Вам нужно добавить вычисляемый метод, подобный этому

component.vue

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

Ответ 12

Односторонний поток данных, согласно https://vuejs.org/v2/guide/components.html, компонент следует одностороннему потоку данных. Все реквизиты образуют одностороннюю привязку между дочерним свойством и родительским Во-первых, когда родительское свойство обновляется, оно передается дочернему элементу, но не наоборот, это предотвращает случайное изменение дочерних компонентов родительского, что может затруднить понимание потока данных вашего приложения.

Кроме того, каждый раз, когда родительский компонент обновляется, все реквизиты в дочерних компонентах обновляются с последним значением. Это означает, что вы не должны пытаться изменить опору внутри дочернего компонента. Если вы это сделаете.vue предупредит вас в консоли.

Обычно есть два случая, когда возникает соблазн мутировать реквизит: реквизит используется для передачи начального значения; потом дочерний компонент хочет использовать его как локальное свойство данных. Опора передается как необработанное значение, которое необходимо трансформировать. Правильный ответ на эти случаи использования: Определите локальное свойство данных, которое использует начальное значение реквизита в качестве своего начального значения:

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

Определите вычисляемое свойство, которое вычисляется из значения реквизита:

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

Ответ 13

Реквизиты Vue.js не должны быть мутированы, так как это рассматривается как Anti-Pattern в Vue.

Подход, который вам нужно предпринять, - это создание свойства данных на вашем компоненте, которое ссылается на исходное свойство prop списка

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

Теперь ваша структура списка, которая передается компоненту, ссылается и мутируется с помощью свойства data вашего компонента: -)

Если вы хотите сделать больше, чем просто проанализировать свое свойство списка, используйте свойство Vue component <computed. Это позволит вам сделать более глубокие мутации для ваших реквизитов.

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

В приведенном выше примере анализируется список и фильтрует его до активных списков, регистрируется для schnitts и хихикает и возвращает его.

Примечание: в свойствах data и computed указаны в шаблоне те же самые, например,

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

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

Легко думать, что должно быть вызвано свойство computed (являющееся методом)... оно не

Ответ 14

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'
})

Может быть, это будет соответствовать вашим потребностям.

Ответ 15

Добавляя к лучшему ответу,

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

Установка реквизитов с помощью массива предназначена для разработки/создания прототипов, в производстве убедитесь, что вы задали типы реквизитов (https://vuejs.org/v2/guide/components-props.html) и установите значение по умолчанию, если реквизит не имеет был заселен родителем, как таковой.

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

Таким образом вы, по крайней мере, получаете пустой объект в mutableList вместо ошибки JSON.parse, если он не определен.

Ответ 16

Vue.js считает, что это анти-шаблон. Например, объявление и установка некоторых реквизитов, таких как

this.propsVal = 'new Props Value'

Итак, чтобы решить эту проблему, вам нужно взять значение из реквизита для данных или вычисленное свойство экземпляра Vue, например:

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

Это определенно будет работать.

Ответ 17

В дополнение к вышесказанному, для других, имеющих следующую проблему:

Msgstr "Если значение реквизита не требуется и, следовательно, не всегда возвращается, переданные данные вернут undefined (вместо пустого)". Что может испортить значение <select> умолчанию, я решил это, проверив, установлено ли значение в beforeMount() (и установите его, если нет) следующим образом:

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>

Ответ 18

Я хочу дать этот ответ, который поможет избежать использования большого количества кода, наблюдателей и вычисляемых свойств. В некоторых случаях это может быть хорошим решением:

Опоры предназначены для обеспечения односторонней связи.

Когда у вас есть модальная кнопка show/hide с подпоркой, лучшее решение для меня - создать событие:

<button @click="$emit('close')">Close Modal</button>

Затем добавьте слушателя к модальному элементу:

<modal :show="show" @close="show = false"></modal>

(В этом случае опора show, вероятно, не нужна, потому что вы можете использовать легкий v-if="show" прямо на базовом моде)

Ответ 19

Я лично всегда предлагаю, если вам нужно изменить мутации, сначала передать их в вычисляемое свойство и вернуться оттуда, после этого можно легко изменять мутации, даже при этом вы можете отслеживать мутацию рек, если они мутируют из другого. компонент тоже, или мы можем также посмотреть.

Ответ 20

En este código, estoy usando un filterro para cada lista dentro de un component. Декларация о наличии свободных мест для локальных и компьютерных технологий.

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));
                },
            },
        });