본문 바로가기

Vue/vue 기초 공부하기

Vue로 틱택토 만들기( $root.data를 활용)

https://github.com/loy124/Vue/tree/master/틱택토

 

loy124/Vue

Vue 공부. Contribute to loy124/Vue development by creating an account on GitHub.

github.com

 

 

일일히 props에 데이터를 넘겨주는 방식에 굉장한 불편함이 따르기때문에

 

props로 값을 넘겨주는 방식 + Vue 자체에서 $root 를 활용하여 최상단의 데이터값을 가져올수 있는 방식이 활용되었다

 

 

 

 

root쪽

Tictactoe.vue

 

:table-data (v-bind:table-data) 를 활용하여 

tableData의 값을 자식 컴퍼넌트 table-component에 넘겨준다 

<template>
  <div>
    <div>{{ turn }}님의 턴입니다.</div>
    <table-component :table-data="tableData"></table-component>
    <div v-if="winner">{{ winner }}님의 승리!</div>
  </div>
</template>

<script>
import Vue from 'vue';
import TableComponent from './TableComponent';
export default {
  components: {
    TableComponent,
  },
  data() {
    return {
      tableData: [
        ['', '', ''],
        ['', '', ''],
        ['', '', ''],
      ],
      turn: 'O',
      winner: '',
    };
  },

  methods: {
    //뷰에서 객체나 배열이 있고 내부의값을 인덱스로 변경하면 화면에 적용되지 않는다
    //객체로 key값을 바꾸는경우도 마찬가지
    // //push등으로 사용하면 적용이 된다
    // onChangeData(){
    //   this.tableData[0][1] = 'O';
    // }
    onChangeData() {
      //this.tableData[1][0] = 'X' 작동하지 않는다
      //Vue를 import하고 바꾸고싶은 값을 Vue.set을 사용한다
      // this.tableData[1][0]에 'X'를 넣는다
      // Vue.set(this.tableData[1], 0, 'X');
      //Vue를 import 안하더라도 해당 방식으로 해결가능하다
      // this.$set(this.tableData[1], 0, 'X');
    },
  },
  computed: {},
  mounted() {},
  beforeDestroy() {},
};
</script>

<style scoped></style>

 

TableComponent.vue

props로 tableData를 전달받고 해당 tableData를 각각 rowData, index로 나누어서 tr-component에

:row-data, :row-index 로 값을 넘겨준다

<template>
  <table>
    <tr-component
      v-for="(rowData, index) in tableData"
      :key="index"
      :row-data="rowData"
      :row-index="index"
    ></tr-component>
  </table>
</template>

<script>
import TrComponent from './TrComponent';
export default {
  components: {
    TrComponent,
  },
  props: {
    tableData: Array,
  },
};
</script>

<style>
table {
  border-collapse: collapse;
}
td {
  border: 1px solid black;
  width: 40px;
  height: 40px;
  text-align: center;
}
</style>

 

 

TrComponent.vue

 

 

row-data, :row-index 를 통해 rowData rowIndex를 넘겨받고 

rowData는 ["", "", ""] 식의 배열로 구성되어있기 때문에

이를 또 cellData와 cellIndex로 나누어서 td컴포넌트에 넘겨준다

 

<template>
  <tr>
    <td-component
      v-for="(cellData, index) in rowData"
      :key="index"
      :cell-data="cellData"
      :cell-index="index"
      :row-index="rowIndex"
    ></td-component>
  </tr>
</template>

<script>
import TdComponent from './TdComponent';
export default {
  components: {
    TdComponent,
  },
  data() {
    return {
      parent: '부모',
    };
  },
  props: {
    rowData: Array,
    rowIndex: Number,
  },
};
</script>

<style></style>

TdComponent.vue

 

넘겨받은 cellData를 활용해서 받아온다

 

이곳에서 O X 표시를 나타내줘야 하는데 

정상적으로 나타나지 않게된다

 

javascript및 vue의 한계로써

배열로된 index값을 통해 값을 바꿔주면 정상적으로 화면이 바뀌지 않는다

따라서 Vue.set 혹은 $set을 활용하여 새로 그려주는 작업이 필요하다 

 

 

<template>
  <td @click="onClickTd">{{ cellData }}</td>
</template>

<script>
export default {
  props: {
    cellData: String,
    rowIndex: Number,
    cellIndex: Number,
  },
  methods: {
    onClickTd() {
      //root값의 데이터를 가져온다
      // console.log(this.$root.$data);
      //부모의 데이터를 가져온다
      // console.log(this.$parent.$data);
      //자식 컴포넌트에서 부모컴포넌트의 데이터를 변경할 수 있다.
      //화면에서 반영이 되지 않는다

      //부모의 데이터는 잘 변경이되는데 화면에는 그려지지 않는다
      // this.$root.$data.tableData[this.rowIndex][
      //   this.cellIndex
      // ] = this.$root.$data.turn;

      //this.$set으로 일치화해주는 작업을 실시한다
      // 또한 인덱스를 여러번 쓰는경우 마지막 index를 key로 하면 된다
      if (this.cellData) return;
      const rootData = this.$root.$data;
      this.$set(
        rootData.tableData[this.rowIndex],
        this.cellIndex,
        rootData.turn,
      );

      let win = false;
      if (
        rootData.tableData[this.rowIndex][0] === rootData.turn &&
        rootData.tableData[this.rowIndex][1] === rootData.turn &&
        rootData.tableData[this.rowIndex][2] === rootData.turn
      ) {
        win = true;
      }
      if (
        rootData.tableData[0][this.cellIndex] === rootData.turn &&
        rootData.tableData[1][this.cellIndex] === rootData.turn &&
        rootData.tableData[2][this.cellIndex] === rootData.turn
      ) {
        win = true;
      }

      //대각선
      if (
        rootData.tableData[0][0] === rootData.turn &&
        rootData.tableData[1][1] === rootData.turn &&
        rootData.tableData[2][2] === rootData.turn
      ) {
        win = true;
      }
      if (
        rootData.tableData[0][2] === rootData.turn &&
        rootData.tableData[1][1] === rootData.turn &&
        rootData.tableData[2][0] === rootData.turn
      ) {
        win = true;
      }
      //승리했을때
      if (win) {
        rootData.winner = rootData.turn;
        rootData.turn = 'O';
        rootData.tableData = [
          ['', '', ''],
          ['', '', ''],
          ['', '', ''],
        ];
      } else {
        //무승부일때
        let all = true; //all이 true면 무승부
        //무승부 검사
        rootData.tableData.forEach(row => {
          //2차원 배열이니 두번 forEach 실행
          row.forEach(cell => {
            // 칸이 비어있는경우면 all = false
            if (!cell) {
              all = false;
            }
          });
        });
        //무승부일때 값 초기화
        if (all) {
          rootData.winner = '';
          rootData.turn = 'O';
          rootData.tableData = [
            ['', '', ''],
            ['', '', ''],
            ['', '', ''],
          ];
          //무승부가 아니므로 턴만 넘긴다
        } else {
          rootData.turn = rootData.turn === 'O' ? 'X' : 'O';
        }
      }

      // console.log(rootData);
    },
  },
};
</script>

<style></style>