はじめに
この記事は 2019 年まで素の JavaScript でコーディングしていた人間が初めて Vue.js を学習したときの記録です
↓のURLにアクセスするとアプリに触れます。
https://vue-mine.firebaseapp.com
何を作ったか?
見た目はあれです。今回は簡単な仕上がりを目指しました(適当)
データバインディングの動きの確認をする程度の目的ですので、セルは単純な色分けで区別しています。
開発環境
Windows10
Node.js v12.13.1
Vue.js 2.6.10
vue/cli 4.1.1
Atom 1.41.0
Github https://github.com/t-ube/vue-mine
作成時につまづいた点と答え
Q. まずどのファイルをいじればいいのかわからない
A. <プロジェクトフォルダ>\src\App.vue を編集する
よっしゃさっそく作り始めたるわーと思い、コマンドラインで
vue create <プロジェクト名>
と打ち込んだ直後に僕は手が止まりましたが、 @567000 様の記事を参考にして、まずは App.vue
内の <template>
を色々といじるところから始めました。
[Vue.js を vue-cli を使ってシンプルにはじめてみる][1]
[1]:https://qiita.com/567000/items/dde495d6a8ad1c25fa43
Q. 表示条件の分岐ってどうやる?
A. 表示する要素のタグ内に制御用のif文を書く
Vue.js ではタグ内の v-if ディレクティブに指定した値が評価されて表示が切り替わります。
のコードを例にとります。
<p v-if="gameover">Game Over</p>
<p v-else-if="complete">Clear</p>
<p v-else>Please click green cell</p>
まず、変数 gameover
の値が真のときに Game Over
が表示されます。
それ以外でかつ変数 complete
が真のときに Clear
が表示されます。
それ以外のときには Please click green cell
が表示されます。
素の JavaScript で getElementById で要素を取得してから表示/非表示の切り替えとか面倒なことをしていたのが嘘のようです。Vue.js は素晴らしいです
Q. マインスイーパーのセルの並べ方ってどうやる?
A. v-for で要素を並べて grid-template を使う
Vue.js / CSS の合わせ技です。
<template>
~略~
<div id="map">
<div v-for="cell in grid_size*grid_size"
~略~
</template>
<script>
~略~
#map {
--grid-size: 6;
display: grid;
grid-template-columns: repeat(var(--grid-size), 30px);
grid-template-rows: repeat(var(--grid-size), 30px);
justify-content: center;
align-content: end;
padding: auto;
margin: auto;
}
~略~
Vue.js では要素のタグ内に v-for ディレクティブを付けることで、同じ要素を繰り返し配置できます。
@miyauchoi 様の記事を参考にさせていただきました。
[200行のVue.jsでスネークゲームを作った][2]
[2]:https://qiita.com/miyauchoi/items/f753e5fa0209ab2034dd
Q. for 文で要素配置したらエラーになるんだけど?
A. v-bind:key が必要
v-for ディレクティブで要素を連続配置したらコンパイルエラーです。本当にありがとうございました。
./src/App.vue
Module Error (from ./node_modules/eslint-loader/index.js):
error: Elements in iteration expect to have 'v-bind:key' directives (vue/require-v-for-key) at src\App.vue:8:7:
6 | <p v-else>Please click green cell</p>
7 | <div id="map">
> 8 | <div v-for="cell in grid_size*grid_size"
| ^
9 | v-on:click="dig_cell(cell-1)"
10 | :class="{
11 | cell: true,
理由はエラーメッセージに書かれているので一目瞭然ですね!
僕は軽く1時間は悩みましたがね。
のようにコードを直しました。
<div v-for="cell in grid_size*grid_size"
v-on:click="dig_cell(cell-1)"
v-bind:key="cell.id"
:class="{
要素に値をバインドさせるためにidが必須とのことなので、v-for ディレクティブが動作しない怪現象に悩んだら v-bind:key を指定しているかを確認しましょう。
Q. クラスってどうやって作るのかわからない
A. コンポーネント化する(多分)
Google 先生で「Vue.js クラス」って検索しても CSS しかでてきません。
データを構造化したいときはどうするのでしょうか
コンポーネント化するっぽいのですが。
これは諦めて今後の課題としました。
というわけで、セルごとのパラメータ(周囲の地雷の数、地雷フラグ、掘削フラグ)はすべて独立した配列で管理しています。
data () {
return {
~略~
surround_bomb: [],
is_bomb: [],
is_dig: [],
~略~
}
気持ちが悪いですね。早いところ修正したいと思います。
Q. セル要素の二次元座標はパラメータ化する?
A. 座標パラメータは不要で、配列サイズから計算する
配列のインデックスと座標の変換関数を用意します。
配列のインデックスを得るための関数 get_index
に渡した座標が二次元座標の領域を超えている場合、存在しないものとして -1 を返しています。
get_cell_x(index){
return (Math.floor(index%this.grid_size));
},
get_cell_y(index){
return (Math.floor(index/this.grid_size));
},
get_index(x,y){
if(x < 0 || y < 0) return -1;
else if(x>=this.grid_size || y>=this.grid_size) return -1;
return ((y*this.grid_size)+x);
}
セルの周囲の地雷を数えるための関数 count_surrounding_bombs
に配列のインデックスを渡すと内部で座標に変換し、周囲の 8 セル分をチェックします。
is_bomb_index(index){
return (this.is_bomb[index] == 1);
},
is_bomb_cell(x,y){
return (this.is_bomb_index(this.get_index(x,y)));
},
count_surrounding_bombs(index) {
let x = this.get_cell_x(index);
let y = this.get_cell_y(index);
let counts = 0;
if(this.is_bomb_cell(x-1,y-1)) counts += 1;
if(this.is_bomb_cell(x,y-1)) counts += 1;
if(this.is_bomb_cell(x+1,y-1)) counts += 1;
if(this.is_bomb_cell(x-1,y)) counts += 1;
if(this.is_bomb_cell(x+1,y)) counts += 1;
if(this.is_bomb_cell(x-1,y+1)) counts += 1;
if(this.is_bomb_cell(x,y+1)) counts += 1;
if(this.is_bomb_cell(x+1,y+1)) counts += 1;
return counts;
}
感想
Vue.js のデータバインディングって強力ですね。
ミニゲームを簡単に作れてしまうのがすごいです
次はコンポーネント化にチャレンジしてみます。