我创建了一个简单的Minesweeper游戏,当涉及到决策时,呈现哪个单元有三种可能性:
我创建了一个行组件,该组件呈现该行包含的所有单元格。
<template>
<div>
<component
v-for="(cell, columnIndex) in row"
:key="columnIndex"
v-bind="getCellProps(cell, columnIndex)"
:is="getComponentCell(cell)"
/>
</div>
</template>
<script>
// imports here
export default {
components: {
UnrevealedCell,
RevealedNeutralCell,
RevealedMineCell
},
props: {
row: Array,
rowIndex: Number
},
methods: {
getCellProps: function(cell, columnIndex) {
if(cell.revealed) {
if (cell.isMine) {
return {};
} else {
return {
mineNeighbours: cell.mineNeighbours
};
}
} else {
return {
unrevealedCell: cell,
x: columnIndex,
y: this.rowIndex,
cellClicked: this.onCellClicked
};
}
},
getComponentCell: function(cell) {
if(cell.revealed) {
if (cell.isMine) {
return RevealedMineCell;
} else {
return RevealedNeutralCell;
}
} else {
return UnrevealedCell;
}
},
onCellClicked: function(x, y) {
debugger;
}
}
}
</script>
很遗憾,我的cellClicked
事件无法正常工作。子组件能够正确发出事件,但是我的onCellClicked
无法执行。我认为这是因为我不会写
cellClicked: this.onCellClicked
通常会是
@cellClicked
如果没有@,则该属性可能会添加为组件属性。如何解决此问题以监听发出的cellClicked
事件?
答案 0 :(得分:0)
一些想法出现。
首先,该方法不起作用的原因是因为v-bind
用于设置组件属性和元素属性。 @
前缀是v-on
的简写,因此从这个意义上说,它不是属性或道具,它本身就是一条指令。 v-on
确实支持对象版本,就像v-bind
一样,因此您可以执行类似v-on="getCellEvents(cell, columnIndex)"
的操作,并为每种单元格类型返回合适的对象。这可能是对您原始问题的最干净直接的答案。还可以提供不太干净和不太直接的答案...
您可以通过将cellClicked
用作子单元格的支持,然后将其作为回调函数而不是发出事件来实现。不是说您应该,但是可以。这将与您上面发布的代码完全相同。
另一种选择是只为所有单元格添加事件监听器。在模板中包括@cellClicked="onCellCicked"
,而不必担心单元格类型。如果其他单元格类型不发出该事件,则不会发生任何事情。 Vue不知道组件可以触发什么事件,您可以听任何东西。
更多想法...
您的细胞模板有点贫血。我知道人们通常建议不要将逻辑放在模板之外,但就您而言,我想您可能已经太过分了,这只会使事情变得更难以理解。有两种解决方法:
render
函数。存在模板是因为人类发现它们比render
函数更易于阅读,但是对于您而言,您仍然拥有JavaScript中的所有逻辑。该模板并没有真正添加任何内容,并且使用render
函数进行全面学习可能比您当前拥有的更加容易理解。这两种方法都可以消除您添加事件监听器的问题。
关于click事件的最后一个想法是,您可以使用事件传播来处理它们。在周围组件的适当元素上添加一个单击侦听器,根本不侦听单元格/行上的事件。然后,单个侦听器可以确定(可能是轻率地)单击了哪个单元,以及是否需要对其进行任何操作。尽管这会增加组件之间的耦合,但我想这并不重要,因为这些组件无论如何也无法在其他地方重用。在现阶段,我不建议您使用这种方法,但是每当您发现自己创建大量需要相同事件的重复组件时,就应该牢记这一点。在您的情况下,只有开始遇到性能问题才可能有意义,即使那样,也可能会有更好的方法来解决此类问题。
因此,我向您承诺了模板方法的示例:
<template>
<div>
<template v-for="(cell, columnIndex) in row">
<unrevealed-cell
v-if="!cell.revealed"
:key="columnIndex"
:unrevealed-cell="cell"
:x="columnIndex"
:y="rowIndex"
@cellClicked="onCellClicked"
/>
<revealed-mine-cell
v-else-if="cell.mine"
/>
<revealed-neutral-cell
v-else
:mineNeighbours="cell.mineNeighbours"
/>
</template>
</div>
</template>
我不确定UnrevealedCell
为什么需要x
和y
,但是如果只是为了使其能够作为事件的一部分发出它们,那么您可能要考虑注册侦听器为@cellClicked="onCellClicked(columnIndex, rowIndex)"
,则无需从单元格发出坐标。我还想知道这些单元是否需要3个单独的组件。我的直觉是,一个组件更适合,而行组件根本不需要对单个单元格有任何了解。