如何在vue中实现自更新排序

how to achieve a selfupdating sort in vue

我有一个 vuex-store game,其中包含一个数组 players

每个 player 都有一个 idname 和一个 score

我现在有一个组件 scoreboard,它应该按分数列出所有玩家。

为了对玩家进行排序,我在我的组件中添加了一个计算 属性。

当我现在在应用程序中的某处更新玩家分数时,我计算的 属性 不会被触发。我知道它没有被触发,因为数组本身没有改变。我需要调用 push/pop/shift/anything 来改变数组。

解决这个问题的常用方法是什么?当我更新它的一些属性时,我是否也应该改变数组?由于呈现 player.points 的组件已经成功更新,这看起来很脏。

有没有办法将更多 listeners 添加到计算的 属性?

我的组件:

<template>
  <div class="scoreboard">
    <strong>Scoreboard</strong>
    <div class="scores">
      <score-board-entry v-for="player in sortedPlayers" :key="player.id" :player="player"></score-board-entry>
    </div>
  </div>
</template>

<script>
import _ from 'lodash'
import ScoreBoardEntry from './ScoreBoardEntry.vue'

export default {
  props: ['players'],
  data () {
    return { }
  },
  components: { ScoreBoardEntry },
  computed: {
    sortedPlayers () {
      return _.sortBy(this.players, 'score')
    }
  }
}
</script>

<style scoped>
</style>

我的店铺:

const initialState: state = {
  id: '',
  players: []
}

const game = {
  namespaced: true,
  state: initialState,
  mutations: {

    addPoints (state: state, payload: addPointsPayload) {
      const player = state.players.find((p) => p.id === payload.id)    
      player.points = player.points + payload.points
    }
 ...

如果不了解您如何使用记分板组件,很难说出为什么它没有更新。

无论如何,如果您要删除 scoreBoard 组件中的 属性 球员,并使用带有 mapState 或 mapGetters 的 Vuex 状态球员,它会在您更改球员数据时立即自动更新您的棋盘。

我在创建下面的演示过程中发现的另一点是您按分数排序,但我认为它应该是分数。因为您的状态中没有得分 属性 或得分计算不在您的代码段中。

请查看下面的演示或此 fiddle

而且我认为您的 loadash 排序没问题。我刚刚把结果倒过来,把最高分放在第一位。

在您的 addPoints 突变方法中,我更改为 shorthand += 因为我认为这样更容易阅读 - 您的版本在这里也可以。

mapState 或 mapMutations 演示中的注释代码与映射器的作用相同,保留在那里以了解映射器在做什么。

const initialState = {
  id: '',
  players: [{
    id: 0,
    name: 'first',
    points: 20
  }, {
    id: 1,
    name: 'second',
    points: 10
  }, {
    id: 2,
    name: 'third player',
    points: 35
  }]
};

const game = {
  namespaced: true,
  state: initialState,
  mutations: {
    addPoints(state, payload) {
      const player = state.players.find((p) => p.id === parseInt(payload.id));
      //console.log('addpoints', payload, player);
      player.points += payload.points;
    }
  }
};

const ScoreBoardEntry = {
  props: ['player'],
  template: `
   <div>
     Name: {{player.name}}<br/>
      Score: {{player.points}}
      <pre>Debug:
{{player}}
      </pre>
    </div>
  `
};

const scoreBoard = {
  //props: ['players'],
  template: `
   <div class="scoreboard">
    <strong>Scoreboard</strong>
    <div class="scores">
      <score-board-entry v-for="player in sortedPlayers" :key="player.id" :player="player"></score-board-entry>
    </div>
  </div>
  `,
  data() {
    return {}
  },
  components: {
    ScoreBoardEntry
  },
  computed: {
    ...Vuex.mapState({
     players: (state) => state.game.players // info: mapping more complicated here because of module --> with-out module we could just use ...mapState(['players]);
    }),
    /*players() { // same as mapState
     console.log(this.$store.state.game.players);
     return this.$store.state.game.players;
    },*/
    sortedPlayers() {
      return _.sortBy(this.players, 'points').reverse(); //'score')
    }
  }
};

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
    game
  }
});

new Vue({
  el: '#app',
  store,
  data() {
   return {
     testData: {
       id: 2,
        points: 10
      }
    };
  },
  components: {
    scoreBoard
  },
  methods: {
   /*addPoints(payload) {
     this.$store.commit('game/addPoints', payload);
    }*/
   ...Vuex.mapMutations({
     addPoints: 'game/addPoints'
    })
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.3.1/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
  user id: <input v-model="testData.id">
  points: <input v-model="testData.points" type="number">
  <button @click="addPoints(testData)">
  
  Add points
  </button>
  <score-board></score-board>
</div>