3 반응속도 체크 게임

source: categories/study/vue-principle/vue-principle_3.md

3.1 반응속도 체크와 webpack watch

3.2 v-bind 와 vue style

3.3 webpack-dev-server

3.4 반응속도 체크 게임 완성하기

3.5 computed와 v-show, template

  • 웹팩 로더의 역할을 잘 알고계셔야 합니다.
  • 파일에 자바스크립트가 아닌 것들이 추가될 때마다 rules에다 loader를 추가해주면 그 부분을 웹팩이 알아서 자바스크립트로 바꿔서 한번에 아웃풋으로 만들어준다.
    • .vue 파일은 자바스크립트 파일이 아니지만 vue-loadervue 파일을 발견할 때마다 자바스크립트 파일로 내부적으로 전환해주기 때문에 한방에 자바스크립트 파일로 합칠 수가 있다.
    • css, image, html 등 로더 설정만 해주면 자바스크립트 파일로 합쳐준다.
    • 어떤 파일이든 파일 하나로 합쳐준다가 웹팩의 장점이다.

1. vue-loader 에러 (vue-loader만으로 vue 안에 style 처리 못한다)


npm i -D vue-style-loader css-loader

# OR

yarn add -D vue-style-loader css-loader

2. webpack에서 loader와 plugins의 차이점은 무엇인가요?

  • 로더: 자바스크립트 파일이 아니는 파일들을 자바스크립트 파일로 바꾸어 하나의 파일로 합쳐지게 해준다.
  • 플러그인: 부가적인 역할을 수행
    • 빌드한 파일을 내보내기 전에 압축할 수도 있고
    • html에 자동으로 스크립트 추가해줄 수도 있고
    • 아웃풋이 나오기 전에 추가적으로 몇가지 작업을 더 해주는 것이 플러그인

3. vue에서 sass 기능 사용?


yarn add -D sass sass-loader@10

  • sass-loader 10 버전 설치 이유
    • vue 2.x에선 11 이상의 sass-loader가 호환되지 않아서
    • 위와 같이 설치하면 자동으로 vue에서 scss를 인식
    • webpack.config.js에 이미 다 설정되어있기 때문

4. 새로고침마저 webpack이 자동으로 하게하려면?


npm i -D webpack-dev-server

# OR

yarn add -D webpack-dev-server


{
  "name": "response-check",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --watch",
    "dev": "webpack-dev-server --hot"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "2.6.10"
  },
  "devDependencies": {
    "vue-loader": "^15.7.0",
    "vue-template-compiler": "2.6.10",
    "webpack": "^4.35.2",
    "webpack-cli": "^3.3.5"
  }
}



const VueLoaderPlugin = require('vue-loader/lib/plugin');
const path = require('path');

// webpack 처리를 할 때 아래 객체를 사용한다.
// entry, module, plugins, output 4가지가 주된 설정, 나머지는 부가적인 설정
module.exports = {
  mode: 'development', // 개발중일 땐 development, 배포할 땐 production
  devtool: 'eval', // 개발할 땐 eval, 배포할 땐 hidden-source-map
                   // eval로 하면 웹팩 빌드 속도가 빨라진다.
                   // 이 모드별로 app.js에 나오는 내용이 달라진다.
  resolve: { // 확장자 처리 가능
    extensions: ['.js', '.vue'], // 해당 확장자들은 import 할 때 생략 가능
  },
  entry: {
    // app <- 하나로 합쳐질 파일의 이름 app.js
    app: path.join(__dirname, 'main.js'), 
  },
  module: { // webpack의 핵심
    rules: [ // javascript 파일을 합칠 때 어떤 방식으로 합칠건지를 명시
      {
        test: /\.vue$/,
        use: 'vue-loader', // use: 'vue-loader'라고 작성해도된다.
      },
      {
        test: '/\.css$/',
        use: [
          'vue-style-loader',
          'css-loader',
        ],
      }
    ],
  },
  plugins: [ // module 처리 이후 output 으로 내보내기 전에 한번 더 가공해주는 역할
    new VueLoaderPlugin(),
  ],
  output: {
    filename: '[name].js', // 위에 정의한 app 이라는 이름이 왼쪽 [name]에 매칭되어 들어온다.
    path: path.join(__dirname, 'dist'), // output 경로
    publicPath: '/dist',
  },
}



npm run dev

# OR

yarn dev

5. 계산식은 template 태그 안에 안 넣는 것이 좋다! 그렇게하려면? computed

  • datacomputed의 차이점
  • 그리고 computed를 사용하는 이유?
    • 아래 코드에서 result는 일반 데이터
    • 그런 일반 데이터를 가공해서 사용할 때 보통 computed 사용
  • computed를 사용하면 해당 값들이 cashing이 된다.
  • message 값이 바뀌면 해당 message가 들어간 template 부분이 다시 재렌더링된다.
  • message 값이 바뀌면 전체 template이 다시 그려지는데, 만약 computed로 넣지않고 계산식으로 template 안에 넣으면 해당 계산식도 다시 실행이된다.
  • message 값만 바뀌었는데 다른 부분들도 다시 그려지게된다. 계산식부분.
  • 이런걸 막기 위해서 computed로 작성. 캐싱돼서 값을 기억학있어서 다시 계산되지 않음. 시간단축, 속도 빠름. 성능상 중요한 문제.
  • 데이터들이 조금이라도 가공된다면 computed를 적용해야 성능상 좋음.
  • 엄청 중요함

6. v-show, v-if 차이점

  • v-showstyle="display:none"이 붙어있다.
  • v-show는 태그 자체는 존재
  • v-if는 태그도 존재하지 않음
  • 보통 v-if를 많이 사용

7. root 태그에 template 태그 못쓴다.

  • 리액트에선 fragment 태그 사용 가능한데..
  • 그럼 무조건 div로 감싸야되나?
  • 해결 방법이 있긴함. 7강에서 말해줄게. 근데 그 방법 사용 귀찮. 그냥 보통 div

3.1.1 웹팩

  1. entry: 엔트리로 들어가서
  2. module (loader(rules)): 모듈을 거쳐서
  3. plugins: 마지막으로 플러그인 까지 거쳐서
  4. output: 아웃풋으로 결과물이 나온다.

위와 같은 과정을 거친다.

  • mode, devtool과 같은 부수적인 옵션 말고는 큰 틀에서 달라지는건 없다.
  • 위 4가지가 가장 중요하다 (entry, module, plugins, output)

3.1.2 반응속도 게임

‘초록색이 되면 클릭하세요.'

  • 비동기 코드를 뷰와 같이 쓰는 것을 연습하기 위함

ResponseCheck.vue

<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Response Check</title>
</head>
<body>
  <div id="root"></div>
  <script src="./dist/app.js"></script>
</body>
</html>

<template>
  <div>
    <div id="screen" :class="state" @click="onClickScreen">{{ message }}</div>
    <template v-if="result.length">
      <div>평균 시간: {{ average }}ms</div>
      <button type="button" @click="onReset">리셋</button>
    </template>
  </div>
</template>

<script>
  // 화면과 관련이 없기 때문에 data 안에 안 넣음
  let startTime = 0;
  let endTime = 0;
  let timeout = null;
  
  export default {
    name: 'ResponseCheck',
    data() {
      return {
        result: [],
        state: 'waiting',
        message: '클릭해서 시작하세요.',
      }
    },
    computed: {
      average() {
        return this.result.reduce((a, b) => a + b, 0) / this.result.length || 0;
      },
    },
    methods: {
      onReset() {
        this.result = [];
      },
      onClickScreen() {
        if (this.state === 'waiting') {
          this.state = 'ready';
          this.message = '초록색이 되면 클릭하세요';
          timeout = setTimeout(() => {
            this.state = 'now';
            this.message = '지금 클릭!';
            startTime = new Date();
          }, Math.floor(Math.random() * 1000) + 2000); // 2 ~ 3초
        } else if (this.state === 'ready') {
          clearTimeout(timeout);
          this.state = 'waiting';
          this.message = '너무 성급하시군요! 초록색이 된 후에 클릭하세요';
        } else if (this.state === 'now') {
          endTime = new Date();
          this.state = 'waiting';
          this.message = '클릭해서 시작하세요';
          this.result.push(endTime - startTime);
        }
      }
    }
  }
</script>

<style scoped>
  #screen {
    width: 300px;
    height: 200px;
    text-align: center;
    user-select: none;
  }
  
  #screen.waiting {
    background-color: aqua;
  }
  
  #screen.ready {
    background-color: red;
    color: white;
  }
  
  #screen.now {
    background-color: greenyellow;
  }
</style>

import Vue from 'vue';
import ResponseCheck from './ResponseCheck';

new Vue(ResponseCheck).$mount('#root');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const path = require('path');

// webpack 처리를 할 때 아래 객체를 사용한다.
// entry, module, plugins, output 4가지가 주된 설정, 나머지는 부가적인 설정
module.exports = {
  mode: 'development', // 개발중일 땐 development, 배포할 땐 production
  devtool: 'eval', // 개발할 땐 eval, 배포할 땐 hidden-source-map
                   // eval로 하면 웹팩 빌드 속도가 빨라진다.
                   // 이 모드별로 app.js에 나오는 내용이 달라진다.
  resolve: { // 확장자 처리 가능
    extensions: ['.js', '.vue'], // 해당 확장자들은 import 할 때 생략 가능
  },
  entry: {
    // app <- 하나로 합쳐질 파일의 이름 app.js
    app: path.join(__dirname, 'main.js'), 
  },
  module: { // webpack의 핵심
    rules: [ // javascript 파일을 합칠 때 어떤 방식으로 합칠건지를 명시
      {
        test: /\.vue$/,
        use: 'vue-loader', // use: 'vue-loader'라고 작성해도된다.
      },
      {
        test: '/\.css$/',
        use: [
          'vue-style-loader',
          'css-loader',
        ],
      }
    ],
  },
  plugins: [ // module 처리 이후 output 으로 내보내기 전에 한번 더 가공해주는 역할
    new VueLoaderPlugin(),
  ],
  output: {
    filename: '[name].js', // 위에 정의한 app 이라는 이름이 왼쪽 [name]에 매칭되어 들어온다.
    path: path.join(__dirname, 'dist'), // output 경로
    publicPath: '/dist',
  },
}
{
  "name": "response-check",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --watch", // --watch  붙이면 파일들을 감시한다.
    "dev": "webpack-dev-server --hot"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "2.6.10"
  },
  "devDependencies": {
    "vue-loader": "^15.7.0",
    "vue-template-compiler": "2.6.10",
    "webpack": "^4.35.2",
    "webpack-cli": "^3.3.5"
  }
}