Vuex 全局状态改变不会在 Nuxt 的 v-for 循环中触发重新渲染组件

Vuex global state change does not trigger re-render component in v-for loop in Nuxt

我很难在 Vue.js 中使用 vuex 全局状态结合重新渲染子组件。 全局状态发生了变化,但不会在 v-for 循环中重新呈现其数据。

所有数据列表都渲染出来了,但是当新的数据变化时,/blog中的组件不会改变其中的数据。

这是一些代码:

/store/index.js

export const state = () => ({
  allData: [],
})
export const getters = {
  getAllData: (state) => state.allData,
}
export const mutations = {
  GET_DATAS(state, payload) {
    state.allData = payload
  },
  UPDATE_DATA(state, payload) {
    const item = state.allData[payload.index]
    Object.assign(item, payload)
  },
}
export const actions = {
  getDatas({ commit, state }, payload) {
    return fetch(`URL_FETCH`)
      .then((data) => data.json())
      .then((data) => {
        commit('GET_DATAS', data)
      })
      .catch((err) => console.log(err))
  },
  updateData({ commit, state }, payload) {
    commit('UPDATE_DATA', payload)
  },
}

/layouts/default.vue

beforeCreate() {
  this.$store.dispatch('getDatas').then(() => {
    connectSocket()
  })
},
methods: {
  connectSocket() {
    // connect & received message from socket
    // received message from socket
    this.$root.$emit('updateData', {
      index: 12,
      price: 34,
      change: 56,
      percent: 78,
    })
  },
},

并在 /pages/blog/index.vue

<template>
  <div>
    <div
      v-for="index in getAllData"
      :key="index.name"
      class="w-100 grid-wrapper"
    >
      <div>{{ index.price }}</div>
      <div>{{ index.change }}</div>
      <div>{{ index.percent }}</div>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  data() {
    return {}
  },
  computed: {
    ...mapGetters(['getAllData']),
  },
  mounted() {
    this.$root.$on('updateData', (item) => {
      this.$store.dispatch('updateData', {
        index: item.index,
        price: item.price,
        percent: item.percent,
        change: item.change,
      })
    })
  },
}
</script>

这是一个关于如何使用 Vuex 并将数据高效加载到 Nuxt 应用程序的完整示例(主观但使用良好实践)。

/pages/index.vue

<template>
  <div>
    <main v-if="!$fetchState.pending">
      <div v-for="user in allData" :key="user.id" style="padding: 0.5rem 0">
        <span>{{ user.email }}</span>
      </div>
    </main>
    <button @click="fakeUpdate">Update the 2nd user</button>
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex'

export default {
  data() {
    return {
      mockedData: {
        name: 'John Doe',
        username: 'jodoe',
        email: 'yoloswag@gmail.com',
        phone: '1-770-736-8031 x56442',
        website: 'hildegard.org',
      },
    }
  },
  async fetch() {
    await this.setAllData()
  },
  computed: {
    ...mapState(['allData']),
  },
  methods: {
    ...mapActions(['setAllData', 'updateData']),

    fakeUpdate() {
      this.updateData({ index: 1, payload: this.mockedData })
    },
  },
}
</script>

/store/index.js

import Vue from 'vue'

export const state = () => ({
  allData: [],
})

export const mutations = {
  SET_ALL_DATA(state, payload) {
    state.allData = payload
  },
  UPDATE_SPECIFIC_DATA(state, { index, payload }) {
    Vue.set(state.allData, index, payload)
  },
}

export const actions = {
  async setAllData({ commit }) {
    try {
      const httpCall = await fetch('https://jsonplaceholder.typicode.com/users')
      const response = await httpCall.json()
      commit('SET_ALL_DATA', response)
    } catch (e) {
      console.warn('error >>', e)
    }
  },
  updateData({ commit }, { index, payload }) {
    commit('UPDATE_SPECIFIC_DATA', { index, payload })
  },
}