럿고의 개발 노트
[인프런] 웹 게임을 만들며 배우는 Vue 7. 틱택토 본문
[인프런] 웹 게임을 만들며 배우는 Vue 7. 틱택토
KimSeYun 2019. 11. 16. 18:16[인프런] 웹 게임을 만들며 배우는 Vue
7. 틱택토
7.1. 2차원배열(테이블 구조 짜기)
7.2. this.$root, this.$parent
7.3. Vue.set
7.4. 틱택토 완성하기
7.5. EventBus 사용하기
7.6. Vuex 구조 세팅하기
7.7, Vuex Mutations
7.8. Vuex state 사용하기
7.9. mapState와 Q&A
7.10. Vuex devtools 분석히기
7.11. slot
- 예제(웹 게임)을 통해 Vue를 만나는 시간으로, 예제를 풀면서 주석으로 배운 내용과 알아야 할 내용을 최대한 적어 놓았습니다. 코드와 주석을 참고해주세요!. 노드와 웹팩을 사용하여 설정파일들이 존재합니다. 아래 깃허브를 참고해주세요.
Ex1. 틱택토
[main.js]
1 2 3 4 | import Vue from 'vue'; import TicTacToe from './TicTacToe'; new Vue(TicTacToe).$mount('#root'); | cs |
[package.json]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | { "name": "tic-tac-toe", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "bulid": "webpack --watch", "dev": "webpack-dev-server --hot" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.10", "vue-loader": "^15.7.2", "vue-template-compiler": "^2.6.10", "webpack": "^4.41.2", "webpack-cli": "^3.3.10" }, "devDependencies": { "css-loader": "^3.2.0", "vue-style-loader": "^4.1.2", "webpack-dev-server": "^3.9.0" } } | cs |
[webpack.config.js]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | const VueLoaderPlugin = require('vue-loader/lib/plugin'); const path = require('path'); // 경로 불러오는 것 module.exports = { // 노드에 모듈을 만듬, 이안에 웹팩 모든 설정을 넣으면 됨. mode:'development', // 배포할건지 개발한건지 devtool: 'eval', // 개발할때는 eval, 배포할때는 hid,den-source-map resolve: { extensions: ['.js', '.vue'], // 이걸 하면 import할때 확장자를 넣어줄 필요가 없다. }, entry: { // 스크립트를 모인것 중에서 대표적인 스크립트 app : path.join(__dirname, 'main.js'), // app은 하나로 합친 스크립트 이름 }, module: { // 웹팩의 핵심 // entry로 처리하다가 이상한거 나오면 loader를 실행 rules: [{ // 합칠때 어떻게 합칠지를 설정해주는 것 test: /\.vue$/, // .vue로 끝나는 파일은 vue-loader를 사용하겠다, 정규표현식 loader: 'vue-loader', // npm i vue-loader },{ test: /\.css$/, use: [ // loader와 user는 똑같은 기능 'vue-style-loader', 'css-loader', ] }], }, plugins: [ new VueLoaderPlugin(), ], output: { filename: '[name].js', // 출력할 파일 이름(최종결과) path: path.join(__dirname, 'dist'), // 폴더 경로 publicPath: '/dist', // webpack-dev-server 세팅시 필요 }, }; // entry, module, plugins, output이 주 설정 나머지는 부가적인 설정 | cs |
[TicTacToe.html]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>틱택토</title> </head> <body> <!-- 2차원배열을 이해하기가 가장 좋은 예제가 틱택토 엑셀이나 DB같은 것이 2차원배열을 이용하는데, 2차원배열을 잘 다루는것이 중요하다 Vue에서 배열을 인덱스로 값을 변경하면 보여지는 화면에서는 반영이 되지 않음. 그러나 배열의 메서드를 사용하면 화면에 반영은 됨 vuex는 중앙데이터관리실이라고 생각하면 됨 컴포넌트 연결이 10개 이상이 되버리면 최하위가 최상위 데이터를 가져오기 위해서는 엄청난 v-bind가 필요한데, 그 문제점을 해결하기 위해 나타난것이 vuex --> <div id="root"></div> <script src ="dist/app.js"></script> </body> </html> | cs |
[TicTacToe.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <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: { onChangData(){ // this.tableData[1][0] = 'X'; 작동하지 않음 //Vue.set(this.tableData[1], 0, 'X'); -> 작동하는 방법 1 this.$set(this.tableData[1], 0, 'X'); // Vue.set과 동일 } }, } </script> <style> table{ border-collapse: collapse; } td{ border: 1px solid black; width: 40px; height: 40px; text-align: center; } </style> | cs |
[TableComponent.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <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 { props: { tableData: Array, }, components: { TrComponent, } } </script> <style scoped> </style> | cs |
[TrComponent.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <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 { props:{ rowData: Array, rowIndex: Number, }, data(){ return{ }; }, components:{ TdComponent, } } </script> | cs |
[TdComponent.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <template> <td @click="onClickTd">{{cellData}}</td> </template> <script> export default { props: { cellData: String, rowIndex: Number, cellIndex: Number, }, methods: { onClickTd() { 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) { // 이긴 경우: 3줄 달성 rootData.winner = rootData.turn; rootData.turn = 'O'; rootData.tableData = [['', '', ''], ['', '', ''], ['', '', '']]; } else { // 무승부 let all = true; // all이 true면 무승부라는 뜻 rootData.tableData.forEach((row) => { // 무승부 검사 row.forEach((cell) => { if (!cell) { all = false; } }); }); if (all) { // 무승부 rootData.winner = ''; rootData.turn = 'O'; rootData.tableData = [['', '', ''], ['', '', ''], ['', '', '']]; } else { rootData.turn = rootData.turn === 'O' ? 'X' : 'O'; } } } } }; </script> | cs |
깃허브 주소 : https://github.com/ksy90101/Vue.js/tree/master/vue-Webgame-inflearn/Chapter07
Ex2. 틱택토 - EventBus
[main.js]
1 2 3 4 | import Vue from 'vue'; import TicTacToe from './TicTacToe'; new Vue(TicTacToe).$mount('#root'); | cs |
[package.json]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | { "name": "tic-tac-toe", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "bulid": "webpack --watch", "dev": "webpack-dev-server --hot" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.10", "vue-loader": "^15.7.2", "vue-template-compiler": "^2.6.10", "webpack": "^4.41.2", "webpack-cli": "^3.3.10" }, "devDependencies": { "css-loader": "^3.2.0", "vue-style-loader": "^4.1.2", "webpack-dev-server": "^3.9.0" } } | cs |
[webpack.config.js]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | const VueLoaderPlugin = require('vue-loader/lib/plugin'); const path = require('path'); // 경로 불러오는 것 module.exports = { // 노드에 모듈을 만듬, 이안에 웹팩 모든 설정을 넣으면 됨. mode:'development', // 배포할건지 개발한건지 devtool: 'eval', // 개발할때는 eval, 배포할때는 hid,den-source-map resolve: { extensions: ['.js', '.vue'], // 이걸 하면 import할때 확장자를 넣어줄 필요가 없다. }, entry: { // 스크립트를 모인것 중에서 대표적인 스크립트 app : path.join(__dirname, 'main.js'), // app은 하나로 합친 스크립트 이름 }, module: { // 웹팩의 핵심 // entry로 처리하다가 이상한거 나오면 loader를 실행 rules: [{ // 합칠때 어떻게 합칠지를 설정해주는 것 test: /\.vue$/, // .vue로 끝나는 파일은 vue-loader를 사용하겠다, 정규표현식 loader: 'vue-loader', // npm i vue-loader },{ test: /\.css$/, use: [ // loader와 user는 똑같은 기능 'vue-style-loader', 'css-loader', ] }], }, plugins: [ new VueLoaderPlugin(), ], output: { filename: '[name].js', // 출력할 파일 이름(최종결과) path: path.join(__dirname, 'dist'), // 폴더 경로 publicPath: '/dist', // webpack-dev-server 세팅시 필요 }, }; // entry, module, plugins, output이 주 설정 나머지는 부가적인 설정 | cs |
[TicTacToe.html]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>틱택토</title> </head> <body> <!-- 2차원배열을 이해하기가 가장 좋은 예제가 틱택토 엑셀이나 DB같은 것이 2차원배열을 이용하는데, 2차원배열을 잘 다루는것이 중요하다 Vue에서 배열을 인덱스로 값을 변경하면 보여지는 화면에서는 반영이 되지 않음. 그러나 배열의 메서드를 사용하면 화면에 반영은 됨 vuex는 중앙데이터관리실이라고 생각하면 됨 컴포넌트 연결이 10개 이상이 되버리면 최하위가 최상위 데이터를 가져오기 위해서는 엄청난 v-bind가 필요한데, 그 문제점을 해결하기 위해 나타난것이 vuex --> <div id="root"></div> <script src ="dist/app.js"></script> </body> </html> | cs |
[EventBus.js]
1 2 3 4 5 6 7 | // 이벤트를 중앙에 관리한다고 생각하면 됨 // 루트컴포넌트안에서 모든 데이터 처리를 할 수 있다는 것이 장점으로 루트 컴포넌트안에 다 몰아 넣어서 자식 컴포넌트애들한테 필요한걸 줌 // 단점은 루트컴포넌트안에 엄청 길어진다는 단점이 존재 import Vue from 'vue'; export default new Vue(); | cs |
[TicTacToe.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | <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'; import EventBus from './EventBus'; export default { components: { TableComponent, }, data(){ return{ tableData: [ ['', '', ''], ['', '', ''], ['', '', ''], ], turn: 'O', winner: '', }; }, methods: { onChangData(){ // this.tableData[1][0] = 'X'; 작동하지 않음 //Vue.set(this.tableData[1], 0, 'X'); -> 작동하는 방법 1 this.$set(this.tableData[1], 0, 'X'); // Vue.set과 동일 }, onClickTd(rowIndex, cellIndex) { console.log(rowIndex, cellIndex); this.$set(this.tableData[rowIndex], cellIndex, this.turn); let win = false; if (this.tableData[rowIndex][0] === this.turn && this.tableData[rowIndex][1] === this.turn && this.tableData[rowIndex][2] === this.turn) { win = true; } if (this.tableData[0][cellIndex] === this.turn && this.tableData[1][cellIndex] === this.turn && this.tableData[2][cellIndex] === this.turn) { win = true; } if (this.tableData[0][0] === this.turn && this.tableData[1][1] === this.turn && this.tableData[2][2] === this.turn) { win = true; } if (this.tableData[0][2] === this.turn && this.tableData[1][1] === this.turn && this.tableData[2][0] === this.turn) { win = true; } if (win) { // 이긴 경우: 3줄 달성 this.winner = this.turn; this.turn = 'O'; this.tableData = [['', '', ''], ['', '', ''], ['', '', '']]; } else { // 무승부 let all = true; // all이 true면 무승부라는 뜻 this.tableData.forEach((row) => { // 무승부 검사 row.forEach((cell) => { if (!cell) { all = false; } }); }); if (all) { // 무승부 this.winner = ''; this.turn = 'O'; this.tableData = [['', '', ''], ['', '', ''], ['', '', '']]; } else { this.turn = this.turn === 'O' ? 'X' : 'O'; } } }, }, created(){ EventBus.$on('clickTd', this.onClickTd) // 사용자 정의 이벤트를 정의할 수 있다. } } </script> <style> table{ border-collapse: collapse; } td{ border: 1px solid black; width: 40px; height: 40px; text-align: center; } </style> | cs |
[TableComponent.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <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 { props: { tableData: Array, }, components: { TrComponent, } } </script> <style scoped> </style> | cs |
[TrComponent.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <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 { props:{ rowData: Array, rowIndex: Number, }, data(){ return{ }; }, components:{ TdComponent, } } </script> | cs |
[TdComponent.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <template> <td @click="onClickTd">{{cellData}}</td> </template> <script> import EventBus from './EventBus'; export default { props: { cellData: String, rowIndex: Number, cellIndex: Number, }, methods: { onClickTd() { if(this.cellData) return; EventBus.$emit('clickTd', this.rowIndex, this.cellIndex); } } }; </script> | cs |
깃허브 주소 : https://github.com/ksy90101/Vue.js/tree/master/vue-Webgame-inflearn/Chapter07-EventBus
[main.js]
1 2 3 4 | import Vue from 'vue'; import TicTacToe from './TicTacToe'; new Vue(TicTacToe).$mount('#root'); | cs |
[package.json]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | { "name": "tic-tac-toe", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "bulid": "webpack --watch", "dev": "webpack-dev-server --hot" }, "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.10", "vue-loader": "^15.7.2", "vue-template-compiler": "^2.6.10", "vuex": "^3.1.2", "webpack": "^4.41.2", "webpack-cli": "^3.3.10" }, "devDependencies": { "css-loader": "^3.2.0", "vue-style-loader": "^4.1.2", "webpack-dev-server": "^3.9.0" } } | cs |
[webpack.config.js]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | const VueLoaderPlugin = require('vue-loader/lib/plugin'); const path = require('path'); // 경로 불러오는 것 module.exports = { // 노드에 모듈을 만듬, 이안에 웹팩 모든 설정을 넣으면 됨. mode:'development', // 배포할건지 개발한건지 devtool: 'eval', // 개발할때는 eval, 배포할때는 hid,den-source-map resolve: { extensions: ['.js', '.vue'], // 이걸 하면 import할때 확장자를 넣어줄 필요가 없다. }, entry: { // 스크립트를 모인것 중에서 대표적인 스크립트 app : path.join(__dirname, 'main.js'), // app은 하나로 합친 스크립트 이름 }, module: { // 웹팩의 핵심 // entry로 처리하다가 이상한거 나오면 loader를 실행 rules: [{ // 합칠때 어떻게 합칠지를 설정해주는 것 test: /\.vue$/, // .vue로 끝나는 파일은 vue-loader를 사용하겠다, 정규표현식 loader: 'vue-loader', // npm i vue-loader },{ test: /\.css$/, use: [ // loader와 user는 똑같은 기능 'vue-style-loader', 'css-loader', ] }], }, plugins: [ new VueLoaderPlugin(), ], output: { filename: '[name].js', // 출력할 파일 이름(최종결과) path: path.join(__dirname, 'dist'), // 폴더 경로 publicPath: '/dist', // webpack-dev-server 세팅시 필요 }, }; // entry, module, plugins, output이 주 설정 나머지는 부가적인 설정 | cs |
[TicTacToe.html]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>틱택토</title> </head> <body> <!-- 2차원배열을 이해하기가 가장 좋은 예제가 틱택토 엑셀이나 DB같은 것이 2차원배열을 이용하는데, 2차원배열을 잘 다루는것이 중요하다 Vue에서 배열을 인덱스로 값을 변경하면 보여지는 화면에서는 반영이 되지 않음. 그러나 배열의 메서드를 사용하면 화면에 반영은 됨 vuex는 중앙데이터관리실이라고 생각하면 됨 컴포넌트 연결이 10개 이상이 되버리면 최하위가 최상위 데이터를 가져오기 위해서는 엄청난 v-bind가 필요한데, 그 문제점을 해결하기 위해 나타난것이 vuex --> <div id="root"></div> <script src ="dist/app.js"></script> </body> </html> | cs |
[store.js]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | // vuex는 store를 여러개 만들어도 됨 // 여기서 데이터를 중앙관리할수 있도록 도와줌 // export vs export default의 차이는 export default는 import할때 이름을 마음대로 지정할수 있고 또 마음대로 불러 올수 있는데 // export는 중괄호를 묶어서 가져와야 함 그리고 여러개 부를수도 있음 import Vuex from 'vuex'; import Vue from 'vue'; Vue.use(Vuex); // vue와 vuex를 연결해줘야 함 export const SET_WINNER = 'SET_WINNER'; // 뮤테이션을 변수로 빼는것, 이렇게 많이 사용 export const CLICK_CELL = 'CLICK_CELL'; // import { SET_WINNER, CLICK_CELL} from './store'; export const CHANGE_TURN = 'CHANGE_TURN'; export const RESET_GAME = 'RESET_GAME'; export const NO_WINNER = 'NO_WINNER'; export default new Vuex.Store({ // import store from './store'; state:{ tableData: [ ['', '', ''], ['', '', ''], ['', '', ''], ], turn: 'O', winner: '', },// vue의 data와 비슷 getters: { turnMessage(state){ return state.turn + '님이 승리하셨습니다.'; }, }, // vue의 computed와 비슷 mutations: { [SET_WINNER](state, winner){ // 뮤테이션은 대문자로 적는것이 규칙 state.winner = winner; }, [CLICK_CELL](state, { row, cell }){ // data -> row, cell이 들어있음 Vue.set(state.tableData[row] ,cell ,state.turn); }, [CHANGE_TURN](state){ state.turn = state.turn === 'O' ? 'X' : 'O'; }, [RESET_GAME](state){ state.turn = 'O'; state.tableData = [ ['', '',' '], ['', '',' '], ['', '',' '], ]; }, [NO_WINNER](state){ state.winner = ''; } }, // state를 수정할때 사용, 동기적으로 actions: { }, // 비동기를 사용할때 또는 여러 뮤테이션을 연달아 실행할 때 }); | cs |
[TicTacToe.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | <template> <div> <div>{{turn}}님의 턴입니다.</div> <table-component> <!-- 다른 컴포넌트에 태그를 보내는 곳(slot) --> <tr v-for="(rowData, rowIndex) in tableData" :key="rowIndex"> <td @click="onClickTd(rowIndex, cellIndex)" v-for="(cellData, cellIndex) in rowData" :key="cellIndex">{{cellData}}</td> </tr> </table-component> <div v-if="winner">{{winner}}님의 승리!</div> </div> </template> <script> // [0, 1, 2, 3, 4 ,12, 7, 8, 9, 10, 13, 156] // 0 1 2 3 4 5 6 7 8 9 10, 11, 12 // 배열이 값이 수정되거나 값이 계속 늘어날 경우 :key로 index를 사용하는 것이 좋다 // 랜더링을 할때 화면을 다시 그려야 할지 말지를 결정하는 것이 key(즉, 인덱스가 추가되면 그 인덱스에 대해서만 화면에 새로 그려짐) // 근데, 중간값이 삭제가 되면 뒤의 값들에 인덱스가 삭제되기 때문에 삭제 된 후부터 다시 그려버린다(이럴때는 단점이 될 것이다) import { mapState } from 'vuex'; import store, { CHANGE_TURN, CLICK_CELL, NO_WINNER, RESET_GAME, SET_WINNER } from './store'; import TableComponent from './TableComponent'; export default { store, components: { TableComponent, }, data() { return { data: 1, } }, computed: { ...mapState(['winner', 'turn', 'tableData']), // winner() { // return this.$store.state.winner; // }, // turn() { // return this.$store.state.turn; // }, }, methods: { onClickTd(rowIndex, cellIndex) { console.log(this.cellData); if (this.tableData[rowIndex][cellIndex]) return; this.$store.commit(CLICK_CELL, { row: rowIndex, cell: cellIndex }); let win = false; if (this.tableData[rowIndex][0] === this.turn && this.tableData[rowIndex][1] === this.turn && this.tableData[rowIndex][2] === this.turn) { win = true; } if (this.tableData[0][cellIndex] === this.turn && this.tableData[1][cellIndex] === this.turn && this.tableData[2][cellIndex] === this.turn) { win = true; } if (this.tableData[0][0] === this.turn && this.tableData[1][1] === this.turn && this.tableData[2][2] === this.turn) { win = true; } if (this.tableData[0][2] === this.turn && this.tableData[1][1] === this.turn && this.tableData[2][0] === this.turn) { win = true; } if (win) { // 이긴 경우: 3줄 달성 this.$store.commit(SET_WINNER, this.turn); this.$store.commit(RESET_GAME); } else { // 무승부 let all = true; // all이 true면 무승부라는 뜻 this.tableData.forEach((row) => { // 무승부 검사 row.forEach((cell) => { if (!cell) { all = false; } }); }); if (all) { // 무승부 this.$store.commit(NO_WINNER); this.$store.commit(RESET_GAME); } else { this.$store.commit(CHANGE_TURN); } } } } }; </script> <style> table { border-collapse: collapse; } td { border: 1px solid black; width: 40px; height: 40px; text-align: center; } </style> | cs |
[TableComponet.vue]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <template> <table> <slot></slot> <!-- 부모가 보낸 태그를 받는 법 --> </table> </template> <script> import TrComponent from './TrComponent' export default { components: { TrComponent, }, computed: { tableData() { return this.$store.state.tableData; }, // turnMessage(){ // getters의 값을 가져오는 것 // return this.$stroe.getters.turnMessage; // } }, } </script> <style scoped> </style> | cs |
깃허브 주소 : https://github.com/ksy90101/Vue.js/tree/master/vue-Webgame-inflearn/Chapter07-Vuex
출처
'Vue.js Note > [인프런] 웹 게임을 만들며 배우는 Vue(동영상 강의)' 카테고리의 다른 글
[인프런] 웹 게임을 만들며 배우는 Vue 9. Vue Router (0) | 2019.11.19 |
---|---|
[인프런] 웹 게임을 만들며 배우는 Vue 8. 지뢰찾기 (0) | 2019.11.19 |
[인프런] 웹 게임을 만들며 배우는 Vue 6. 로또 추점기 (0) | 2019.11.14 |
[인프런] 웹 게임을 만들며 배우는 Vue 5. 가위바위보 (0) | 2019.11.14 |
[인프런] 웹 게임을 만들며 배우는 Vue 4. 반응속도체크 (0) | 2019.11.12 |