4 뷰 라이프사이클

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

4.1 vue-devtools와 기타 정보들

  • 크롬 확장프로그램에서 설치 (vue.js devtools)
    • vue 성능 체크 (몇 프레임인지)
    • 그리고 몇번 렌더링되는지
    • 이 모든 것들을 체크해볼 수 있다. (체크해보자.)

0. 컴포넌트 이름짓는 방법?

  • 컴포넌트 이름은 무조건 두 단어 이상으로 짓는게 필수
  • 한 단어로 지으면 기존에 이미 존재하는 태그들과 겹칠 수 있음
    • table이라는 컴포넌트가 있다면 이 table 컴포넌트가 HTML의 table을 가리키는지 vue-componenttable을 가리키는지 애매함
    • 그래서 vue component는 웬만하면 2단어 이상으로 작성
    • vue에서 필수라고 적어놓음, 공식문서에.

1. 지난번 webpack dev server 사용할 때, dist/app.js 결과물이 없는데 잘만 실행됨. 왜?

  • network 패널보면 dist/app.js를 제대로 가지고오고 있음. 없는데. 어떻게?
  • webpack dev server는 웹팩의 아웃풋을 파일로 내보내서 저장하는 것이 아니라 메모리에 저장.
  • 그래서 파일이 실제로 존재하는게 아님. 가상 메모리에 존재.
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',
  },
}

위의 publicPath/dist라고 적어줘야지 메모리상에도 dist/app.js 위치에 생긴다.

2. :class와 :style

  • 데이터 중심으로 사고해라
  • :class, :style은 객체 형식을 지원한다.
  • 자바스크립트에서 -는 마이너스를 뜻하기 때문에 -이 들어간 CSS 속성은 대문자로 작성한다.
  • background-imagebackgroundImage

3. sprite image

  • http2가 나온 요즘에는 스프라이트 이미지를 안 써도 되긴함
  • 옛날에는 여러 이미지 동시 요청보내는게 매우 무거운 작업이었음
  • 그래서 이렇게 이미지를 하나로 합치는 것을 많이 했었음

4. computed

  • 데이터 옆에 뭔가 부수적인 것들이 달려서 화면에 적용될 때
  • 해당 식들을 그대로 template 태그 안에 쓰지말고 computed 활용 (caching)

5. vue 라이프사이클

  1. beforeCreate
  2. Created
  3. beforeMount
  4. mounted
  5. beforeUpdate
  6. updated
  7. beforeDestroy
  8. destroyed
  • 컴포넌트의 data, computed, method 등을 다 읽고 template과 연결 // 여기까지가 created
    • 다 해놨는데 화면에만 안 붙인게 created이다.
    • 즉, 자바스크립트 상에서만 존재하는게 created
  • 마지막에 화면에다 붙여주고난 이후에 mounted가 된다.
    • 화면에까지 존재하는게 mounted

6. vue devtools 분석

  • 항상 vue devtools 를 보며 개발해야된다.
  • datacomputed가 실시간으로 바뀌는 것이 보인다.
  • frame이 60 이하로 내려가면 시각적으로 버벅임이 느껴진다. 최대한 60 이상으로 되도록 성능 최적화에 신경을 써야된다.
  • component render에서 각 라이프사이클 함수들이 몇번 호출되는지 나온다.

7. async는 왜 쓰나요?

  • await 쓰려고 사용한다.
  • 콜백 지옥 등을 벗어나 코드 작성을 깔끔하게 할 수 있다.
async function test () {
  await new Promise(); // 이렇게 프로미스 인스턴스 앞에 await를 쓸 수 있다.
}

4.1.1 가위바위보 (뷰 라이프사이클)

RockScissorsPaper.vue


<template>
  <div>
    <!-- `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${imgCoord} 0` 이렇게 데이터에 추가적인 문자열이 붙어있으면 캐싱을 못한다. -->
    <!-- 이런 경우에 computed를 사용. caching을 자동으로 해주는 것이 성능에 좋다. -->
    <!-- <div id="computer" :style="{ backgroundImage: `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${imgCoord} 0` }"></div> -->
    <div id="computer" :style="computedStyleObject"></div>
    <div>
      <button type="button" @click="onClickButton('바위')">바위</button>
      <button type="button" @click="onClickButton('가위')">가위</button>
      <button type="button" @click="onClickButton('보')"></button>
    </div>
    <div>{{ result }}</div>
    <div>현재 {{ score }}점</div>
  </div>
</template>

<script>
  // 아래 imgCoord data에 바로 0, -142px, -284px 값을 주면 다른 작업자가 응? 이게 뭘 뜻하는거지? 하면서 헷갈려 할 수 있다.
  // 이렇게 명시적으로 적어주는 것이 좋다.
  const rspCoords = {
    rock: '0',
    scissor: '-142px',
    paper: '-284px',
  }
  
  const scores = {
    scissor: 1,
    rock: 0,
    paper: -1,
  }
  
  const computerChoice = (imgCoord) => {
    return Object.entries(rspCoords).find(function (v) {
      return v[1] === imgCoord;
    })[0]
  }
  
  let interval = null;
  
  export default {
    name: 'RockScissorsPaper',
    data() {
      return {
        imgCoord: rspCoords.rock,
        result: '',
        score: 0,
      }
    },
    computed: {
      computedStyleObject() {
        return { backgroundImage: `url(https://en.pimg.jp/023/182/267/1/23182267.jpg) ${this.imgCoord} 0` }
      },
    },
    beforeCreate() {

    },
    created() {
      // 만들어지긴 했지만 화면에 보이기 전
    },
    beforeMount() {

    },
    mounted() {
      // 화면에 보이고 난 후
      this.changeHand();
    },
    beforeUpdate() {

    },
    updated() {
        
    },
    beforeDestroy() {
      // mounted 에서 실행한. 지금처럼 setInterval 같은..
      // 해당 컴포넌트가 사라진 이후에도 이때 실행된 setInterval은 계속 실행된다.
      // 가위바위보 컴포넌트가 사라졌는데도 해당 setInterval이 계속 실행되면 문제가 생긴다. - 쓸데없는 작업을 계속하는 거니깐 성능상의 문제.
      // 이런게 메모리 Leak, 메모리 누수이다.
      clearInterval(interval); // 해당 코드는 destroyed 에서 해도되지만, 보통 beforeDestroy 에서 많이한다.
    },
    destroyed() {

    },
    methods: {
      changeHand() {
        interval = setInterval(() => {
          if (this.imgCoord === rspCoords.rock) {
            this.imgCoord = rspCoords.scissor;
          } else if (this.imgCoord === rspCoords.scissor) {
            this.imgCoord = rspCoords.paper;
          } else if (this.imgCoord === rspCoords.paper) {
            this.imgCoord = rspCoords.rock;
          }
        }, 100)
      },
      onClickButton(choice) {
        clearInterval(interval);
        const myScore = scores[choice];
        const cpuScore = scores[computerChoice(this.imgCoord)];
        const diff = myScore - cpuScore;
        if (diff === 0) {
          this.result = '비겼습니다.';
        } else if ([-1, 2].includes(diff)) {
          this.result = '이겼습니다.';
          this.sore += 1;
        } else {
          this.result = '졌습니다.';
          this.score -= 1;
        }
        setTimeout(() => {
          this.changeHand();
        }, 1000)
      },
    }
  }
</script>

<style scoped>
  #computer {
    width: 142px;
    height: 200px;
    background-position: 0 0;
  }
</style>