Vuex `people [0] = {age: people [0] .age + 1};` не будет вызывать повторную визуализацию, почему? - PullRequest
0 голосов
/ 09 апреля 2020

Пожалуйста, посмотрите этот минимальный пример:

Разметка

<template>
  <div>
    <button @click="$store.commit('changePeople1Works')">changePeople1Works</button>
    <button @click="$store.commit('changePeopleNotWorking')">changePeopleNotWorking</button>
    <pre>
      <code>
        {{$store.state}}
      
    

Vuex

import Vue from "vue";
import Vuex from "vuex";
import App from "./App.vue";

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    people: [{ age: 1 }]
  },
  mutations: {
    changePeople1Works(state) {
      let people = state.people;
      people[0].age = people[0].age + 1;
    },
    changePeopleNotWorking(state) {
      let people = state.people;
      people[0] = { age: people[0].age + 1 };
    }
  }
});

Vue.config.productionTip = false;

new Vue({
  store,
  render: h => h(App)
}).$mount("#app");

enter image description here

Codesandbox: https://codesandbox.io/s/recursing-sound-fvql7?file= / src / main. js: 0-519

У меня две мутации вот так

  1. people[0].age = people[0].age + 1;
  2. people[0] = { age: people[0].age + 1 };

1 работает, однако 2 не работает, почему это происходит?

Я знаю, что вы должны использовать push, pop, sort для запуска повторного рендеринга.

И если вы изменяете элементы массива напрямую, это не должно t вызвать повторную визуализацию.

Однако методы 1 работают, почему?!

1 методы также напрямую изменяют элемент массива, это так сбивает с толку.

Ответы [ 3 ]

1 голос
/ 09 апреля 2020

С документы

Vue не может обнаружить добавление или удаление свойства.

и

... можно добавить реактивные свойства к вложенному объекту, используя метод Vue .set (object, propertyName, value)

и

Vue не удается обнаружить следующие изменения в массиве:

  • Когда вы непосредственно устанавливаете элемент с индексом, например vm.items [indexOfItem] = newValue
  • Когда вы изменяете длину массив, например vm.items.length = newLength

Но это ничего не говорит о мутировании существующего object[property].

  • В первом примере существующее свойство объекта изменяется, поэтому оно работает. (Свойство age первого элемента массива people.)

  • Во втором примере элемент массива устанавливается непосредственно по индексу, поэтому он не Работа. (Индекс 0 массива people.)

Это может показаться нелогичным, поскольку ранее установленный индекс массива нельзя изменить индексом, но предварительно установить свойство объекта может.

Я предполагаю, что причина этого во многом связана с процессом реактивности Object.defineProperty и Vue.

( Сначала меня тоже смутило.)

0 голосов
/ 09 апреля 2020

Свойства простых объектов являются реактивными (определяется как пара геттер / установщик), в то время как индексы массива не являются:

reactive array

Это назначение массива элемент приводит к нереактивному простому объекту:

people[0] = { age: people[0].age + 1 }

Это ограничение Vue реактивность для массивов:

Vue не может обнаружить следующие изменения в массиве:

  • Когда вы непосредственно устанавливаете элемент с индексом, например, vm.items[indexOfItem] = newValue
  • Когда вы изменяете длину массива, например, vm.items.length = newLength

К нему можно обращаться с помощью Vue.set или массива splice, как следует из документации:

  Vue.set(people, 0, { age: people[0].age + 1 });
  // or
  people.splice(0, 1, { age: people[0].age + 1 });

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

  let [person, ...people] = state.people;
  state.people = [{ age: person.age + 1}, ...people];
0 голосов
/ 09 апреля 2020

Это предупреждение о Vue. js реактивности. Вы должны использовать Vue.set, если хотите изменить ссылку на объект. Например, это будет работать:

changePeopleNotWorking(state) {
      const newPeople = [...state.people];
      newPeople[0].age = newPeople[0].age + 1;
      Vue.set(state, "people", newPeople);
}

https://codesandbox.io/s/dreamy-cdn-x90sk?file= / src / main. js

UPD:

Из документов:

Vue не может обнаружить следующие изменения в массиве:

Когда вы непосредственно устанавливаете элемент с индексом, например, vm.items [indexOfItem] = newValue

Когда вы изменяете длину массива, например, vm.items.length = newLength

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // is NOT reactive
vm.items.length = 2 // is NOT reactive

Таким образом, используя массив, вы не можете изменить элемент по индексу (даже существующий элемент), потому что он не реактивный.

Но вы можете использовать

Vue.set(vm.items, indexOfItem, newValue)

Например:

Vue.set(state.people, 0, { age: state.people[0}.age + 1} )

UPD 2. 1 и 2 они не одинаковы. В первом примере мы не переназначаем элемент массива по ссылке, вместо этого мы меняем свойство наблюдаемого объекта. Если вы посмотрите на состояние в console.log, вы увидите, что объекты в массиве (которые были инициализированы) являются наблюдаемыми. Таким образом, Vue может наблюдать за изменениями там.

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...