如果未在模板中使用特定的vuex状态,则点击处理程序将无法工作

时间:2019-08-30 00:20:20

标签: javascript vue.js vuex

一个非常令人迷惑的错误蔓延到我的Vue组件中;如果我从下面的代码中添加第5行,它将按预期工作,如果将其删除,则每个board-slot元素的click方法将停止更改boardSlots对象。

使用{{currentPlayer}}与这有什么关系?我没有在其他任何地方使用它。它不一定需要打印,仅需要在模板定义中的某个位置,因此具有currentPlayer类绑定的随机元素也可以使其神奇地工作。我缺少Vuex架构的东西吗?

<template>
    <div id="board-container">

    //THIS LINE 
    {{currentPlayer}}

        <div id="board">
            <div class="board-row" v-for="i in boardSlots">
                <div class="board-slot"
                    v-for="boardSlot in i"
                    :class="[{checked: (boardSlot.owner != 0)}, 'ownedBy-player-' + boardSlot.owner]"
                    @click="checkSlot(boardSlot.row, boardSlot.col)">
                </div>
            </div>
        </div>
    </div>
</template>

<script>

import { mapState, mapActions } from 'vuex';

export default{
    created(){
        for(var i = 0; i < 6; i++){
            this.boardSlots[i] = [];
        }

        var row = 0;
        var col = 0;

        for(var i = 1; i <= 42; i++){
            this.boardSlots[row].push({
                owner: 0,
                hover: false,
                row: row,
                col: col,
            });

            col++;

            if (col == 7) {
                col = 0;
                if (row < 5) {
                    row++;                  
                }
            }

        }
    },

    data(){
        return {
            boardSlots: [],
        }
    },

    computed: {
        ...mapState([
            'currentPlayer',
        ]),
    },

    methods: {
        ...mapActions([
            'swapToNextPlayer',
        ]),

        checkSlot(row, col){
            if (this.boardSlots[row][col].owner == 0) {
                this.boardSlots[row][col].owner = 1;
                this.swapToNextPlayer();
            }
        }
    }
}


</script>

2 个答案:

答案 0 :(得分:2)

我不确定{{currentPlayer}}的具体用途,但是每当您修改组件的状态并且视图不更新时,(假设渲染期间没有错误)通常意味着要么:

  1. 您正在以Vue无法观察到的方式改变状态。
  2. 您的状态不是被动的,这可能是由于上一点而发生的。

这是一个危险信号:

this.boardSlots[i] = [];

Vue无法检测到对数组元素的分配(读取Array Change Detection)。您需要使用splice()push()来让Vue知道。或者,您也可以在局部变量中构造boardSlots数组,然后在最后将其分配给this.boardSlots

const boardSlots = [];

// Initialize boardSlots ...

this.boardSlots = boardSlots;

您还以一种奇怪的方式渲染了行和插槽;您不应在渲染过程中改变任何状态,无论是局部渲染还是其他方式(--i)。只需这样做:

<div class="board-row" v-for="slots of boardSlots">
    <div class="board-slot"
        v-for="slot of slots"
        :class="[{checked: (slot.owner != 0)}, 'ownedBy-player-' + slot.owner]"
        @click="checkSlot(slot.row, slot.col)">
    </div>
</div>

但这可能与您的反应性问题无关。

答案 1 :(得分:2)

添加{{ currentPlayer }}将使其成为呈现依赖项。当任何渲染依赖项更改时,将导致整个模板重新运行。因此,在这种情况下,更改currentPlayer将导致重新渲染该模板中的所有内容。

如果模板中包含一些非反应性数据,或者您试图以反应性系统无法跟踪的方式更改数据,则对该数据的更改将不会触发重新渲染。

需要理解的关键是,当模板或render函数运行时,它始终会运行整个过程。尽管非反应性更改不会触发渲染,但是如果其他因素触发了渲染,它们仍然会被拉入。我的假设是swapToNextPlayercurrentPlayer进行了更改,从而触发了重新渲染。

有许多变更检测警告需要注意:

https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
https://vuejs.org/v2/guide/list.html#Caveats

在这种情况下,问题在于此行尝试通过索引更新数组:

this.boardSlots[i] = [];

结果是无法正确将阵列输入到反应性系统中。而是使用:

this.boardSlots.push([]);