Вычисляемое свойство в Vue не вычисляется

У меня есть приложение для электронной коммерции (SPA), построенное на Vue, и ниже приведен снимок магазина.

state: {
    cart: [],
    totalItems: {
      count: 0
    }
},
getters: {
    totalItems(){
      if(window.localStorage.totalItems){
        return JSON.parse(window.localStorage.totalItems)
      }
      else{
        let totalItems = { count: 0}
        return totalItems
      }
    }
},
mutations: {
    setCart(state, cart){
      state.cart = cart
      window.localStorage.cart = JSON.stringify(cart)
    },
    setTotalItems(state, totalItems){
      state.totalItems = totalItems
      window.localStorage.totalItems = JSON.stringify(totalItems)
    }
},
actions: {
    addToCart({ commit, getters }, productId){
      let cart = getters.cart
      let totalItems = getters.totalItems
      if(cart.length == 0){
        cart.push({id: productId, quantity: 1})
        totalItems.count++
      }
      else if(cart.find(({ id }) => id == productId)){
        let item = cart.find(({ id }) => id == productId)
        item.quantity++
        totalItems.count++
      }
      else{
        cart.push({id: productId, quantity: 1})
        totalItems.count++
       }
       commit('setCart', cart)
       commit('setTotalItems', totalItems)
    },
    setTotalItems({ commit }, totalItems){
      commit('setTotalItems', totalItems)
    }
}

В моем файле App.vue ниже -

<template>
  <v-app>
    <v-app-bar
      app
      color="red"
      dark
    >
      <v-btn text to="/">Vue Shop</v-btn>

      <v-spacer></v-spacer>

      <v-btn text to="/cart">
        <v-badge v-if="totalItems.count" :content="totalItems.count"><v-icon>mdi-cart</v-icon></v-badge>
      </v-btn>

    </v-app-bar>

    <v-main>
      <router-view></router-view>
    </v-main>
  </v-app>
</template>

<script>


export default {
  name: 'App',
  components: {
  },
  computed: {
    totalItems(){
      return this.$store.getters.totalItems
    }
  }
};
</script>

Когда я загружаю сайт, я вижу, что вычисленное свойство вычисляется. Однако, когда я нажимаю кнопку «Добавить в» в файле Home.vue, показанном ниже, он должен

  1. Вызовите метод addToCart
  2. Что, в свою очередь, отправляет действие addToCart из магазина.
  3. Где я вычисляю totalItems и устанавливаю значение с помощью мутации setTotalItems

Проблема, с которой я столкнулся, заключается в том, что когда я нажимаю кнопку «Добавить в», я вижу, что значение totalItems обновляется в localStorage, но оно не отражается в компоненте v-app-bar. Это произойдет только в том случае, если я перейду на другую страницу, а затем вернусь на главную.

Когда я реализовал, сохранив значение в состоянии вместо localStorage, оно отображается правильно, без необходимости переходить на другую страницу.

Есть ли способ добиться этого, все еще используя localStorage вместо магазина

<template>
      <v-container>
        <v-row>
          <v-col v-for="product in products" :key="product.id">
            <v-card width="300" height="300">
              <v-img :src=product.imgUrl width="300" height="200"></v-img>
              <v-card-title>
                {{ product.name }}
              </v-card-title>
              <v-card-text>
                ${{ product.price }}
                <v-btn small class="ml-16 primary" @click="addToCart(product.id)">Add to <v-icon>mdi-cart</v-icon></v-btn>
              </v-card-text>
            </v-card>
          </v-col>
        </v-row>
      </v-container>
    </template>
    
    <script>
    
    
    export default {
      name: 'Home',
      components: {
      },
      computed: {
        products(){
          return this.$store.getters.products
        },
        cart(){
          return this.$store.getters.cart
        }
      },
      methods: {
        addToCart(id){
          this.$store.dispatch('addToCart', id)
        }
      }
    }
    </script>

person Community    schedule 11.08.2020    source источник
comment
Изменение элемента в localStore не приведет к изменению vue, поэтому вычисленные свойства не пересчитываются.   -  person Fabian Bettag    schedule 11.08.2020
comment
В вычисляемом свойстве я вызываю метод получения из магазина, должно ли оно не возвращать последнее значение? Почему значение отображается правильно после перехода на другую страницу и возврата на главную страницу?   -  person    schedule 11.08.2020
comment
Это должно быть и есть. Но поскольку вы используете localStorage только для получения значения (которое не является реактивным), значение получателя никогда не изменится, и, следовательно, вычисленное значение не пересчитывается.   -  person Fabian Bettag    schedule 11.08.2020


Ответы (1)


Проблема. Локальное хранилище не реагирует, поэтому ваш геттер никогда не будет переоценивать.


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

getters: {
    totalItems(state){
      if(state.totalItems.count < 0){
        return JSON.parse(window.localStorage.totalItems || '{count: 0}')
      }

      return state.totalItems;
    }
},

Затем вы можете инициализировать свой count с помощью -1, так что первая оценка получателя будет смотреть в локальное хранилище.

Ссылаясь на свойство totalItems в вашем геттере, vue знает, что нужно переоценивать этот геттер при изменении этого свойства.

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


Возможно, вы также можете инициализировать свое состояние с помощью вызова localStorage и удалить его из получателя.

state: {
   totalItems: {
      count: JSON.parse(window.localStorage.totalItems || '{count: 0}')
   }
},
getters: {
    totalItems(state){
      return state.totalItems;
    }
},

Плюс: ваше состояние и геттер синхронизированы, но это также делает ваш геттер избыточным.

person Fabian Bettag    schedule 11.08.2020