티스토리 뷰

Web

[Vue.js] Vue.js 시작하기

Aaron 2022. 7. 30. 00:26
반응형

Vue

회사에서 front 업무(?)도 살짝 담당하게 되어 Vue.js를 공부하게 되었다.

아주 옛날에 배웠던 것 같긴 한데.. 아무것도.. 생각이 나지 않는다... Haha.h...a...

그리하여 Inflearn Vue.js 시작하기 - Age of Vue.js 강의를 듣게 되었고, 업무를 하면서 틈틈이 참고하고자 간략하게 내용들을 정리해 보았다.

아주 쉽고 빠르게 Vue.js 세계를 이해시켜주신 장기효님께 감사를.. 👏🏻👏🏻👏🏻

What is the Vue

Result

  • View: 눈에 보이는 화면 (화면의 요소는 HTML)
    • HTML은 DOM을 이용해서 javascript로 조작 -> Reactivity
  • DOM Listeners: View에서 사용자 이벤트는 Vue의 DOM Listeners로 청취
  • Modal: Vue에서 청취한 이벤트를 통해 javascript에 있는 데이터를 변경하거나 특정 로직을 실행
    • javascript를 통해 데이터가 변경되었을 경우 Data Bindings 동작하여 View에 반영

Instance

  • 뷰 개발 시 필수로 생성이 필요한 코드
  • 인스턴스 생성 시 Vue 개발자 도구에서 Root 컴포넌트로 인식
var vm = new Vue({
  el: '#app',
  data: {
    message: 'hi'
  },
  methods: {
  },
  created: function() {
  }
});
  • el: app이라는 ID를 가진 태그를 찾아서 인스턴스를 붙여준다.
    • 태그에 인스턴스를 붙여주면 view의 기능과 속성을 조작 가능
  • data: 뷰의 반응성(Reactivity)이 반영된 데이터 속성
  • template : 화면에 표시할 요소 (HTML, CSS 등)
  • methods : 화면의 동작과 이벤트 로직을 제어하는 메서드
  • created : 뷰의 라이프 사이클과 관련된 속성
  • watch : data에서 정의한 속성이 변화했을 때 추가 동작을 수행할 수 있게 정의하는 속성

Components

화면의 영역을 영역별로 구분해서 코드로 관리하는 것

  • 화면의 영역을 구분하여 개발할 수 있는 뷰의 기능
  • 코드의 재사용성이 올라가고 빠른 화면 제작 가능
  • 전역 컴포넌트는 기본적으로 모든 인스턴스에 등록이 되어 있음
  • 지역 컴포넌트는 인스턴스마다 새로 생싱이 필요
  • 다만 서비스를 구현할 때는 하나의 인스턴스에 컴포넌트를 붙여 나가는 형식으로 진행
<body>
  <div id="app">
    <app-header></app-header>
    <app-footer></app-footer>
  </div>

  <div id="app2">
    <app-header></app-header>
    <app-footer></app-footer>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    // 전역 컴포넌트(실무에서는 plugin 이나 library 형태로 사용)
    // Vue.component('컴포넌트 이름', 컴포넌트 내용);
    Vue.component('app-header', {
      template: '<h1>Header</h1>'
    });  

    new Vue({
      el: '#app',
      // 지역 컴포넌트 등록 방식(실무에서 가장 많이 사용하는 방식)
      components: {
        // '컴포넌트 이름': 컴포넌트 내용,
        'app-footer': {
          template: '<footer>footer</footer>'
        }
      },
    });

    new Vue({
      el: '#app2',
      components: {
        'app-footer': {
          template: '<footer>footer</footer>'
        }
      }
    })
  </script>
</body>
</html>

통신 방식

  • 뷰 컴포넌트는 각각 고유한 데이터 유효 범위를 갖고 있음
  • 따라서, 컴포넌트 간에 데이터를 주고 받기 위해선 따라야 할 규칙이 존재
    • 상위에서 하위로는 데이터를 내려줌, 프롭스 속성
    • 하위에서 상위로는 이벤트를 올려줌, 이벤트 발생

props

  • 컴포넌트 간에 데이터를 전달할 수 있는 컴포넌트 통신 방법
  • 상위 컴포넌트에서 하위 컴포넌트로 내려보내는 데이터 속성
<body>
  <div id="app">
    <!-- <app-header v-bind:프롭스 속성 이름="상위 컴포넌트의 데이터 이름"></app-header> -->
    <app-header v-bind:propsdata="message"></app-header>
    <app-content v-bind:propsdata="num"></app-content>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    var appHeader = {
      template: '<h1>{{ propsdata }}</h1>',
      props: ['propsdata'] // 하위 컴포넌트의 프롭스 속성명
    }
    var appContent = {
      template: '<div>{{ propsdata }}</div>',
      props: ['propsdata'] // 하위 컴포넌트의 프롭스 속성명
    }

    new Vue({
      el: '#app',
      components: {
        'app-header': appHeader,
        'app-content': appContent
      },
      data: {
        message: 'hi',
        num: 10
      }
    })
  </script>
</body>
  • props 속성 사용을 위해 하위 컴포넌트의 컴포넌트 내용과

    var childComponent = {
      props: ['프롭스 속성 명']
    }
  • 상위 컴포넌트의 템플릿에 각각 코드를 추가

    <div id="app">
      <child-component v-bind:프롭스 속성 명="상위 컴포넌트의 data 속성"></child-component>
    </div>
    

Emit

  • 하위 컴포넌트에서 상위 컴포넌트로 통신
<body>
  <div id="app">
    <p>{{ num }}</p>
    <!-- <app-header v-on:하위 컴포넌트에서 발생한 이벤트 이름="상위 컴포넌트의 메서드 이름"></app-header> -->
    <!-- (2). pass 라는 이벤트가 하위 컴포넌트에서 올라왔을 때, 컴포넌트 태그(app-header)가 받아서 logText라는 메서드를 실행 -->
    <app-header v-on:pass="logText"></app-header>

    <app-content v-on:increase="increaseNumber"></app-content>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    var appHeader = {
      template: '<button v-on:click="passEvent">click me</button>',
      methods: {
        passEvent: function() {
          this.$emit('pass'); // (1). 클릭 시 상위 컴포넌트로 pass 이벤트 발생
        }
      }
    }
    var appContent = {
      template: '<button v-on:click="addNumber">add</button>',
      methods: {
        addNumber: function() {
          this.$emit('increase'); 
        }
      }
    }

    var vm = new Vue({
      el: '#app',
      components: {
        'app-header': appHeader,
        'app-content': appContent
      },
      methods: {
        logText: function() { // (3). 하위 컴포넌트에서 이벤트를 전달 받고 실행
          console.log('hi');
        },
        increaseNumber: function() {
          this.num = this.num + 1;
        }
      },
      data: {
        num: 10
      }
    });
  </script>
</body>

Components at the same level

같은 컴포넌트 레벨 간의 통신 방법

  • <body>
    <div id="app">
      <!-- <app-header v-bind:프롭스 속성 이름="상위 컴포넌트의 데이터 이름"></app-header> -->
      <app-header v-bind:propsdata="num"></app-header>
      <!-- (5) 상위 컴포넌트가 가지고 있는 데이터를 하위 컴포넌트로 전달 -->
    
      <app-content v-on:pass="deliverNum"></app-content> 
      <!-- (1) appContent의 버튼이 눌리면 passNum 메서드가 실행 -->
      <!-- (3) pass 이벤트가 발생되면 deliverNum 메서드 실행 -->
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      var appHeader = {
        template: '<div>header</div>',
        props: ['propsdata']
      }
      var appContent = {
        template: '<div>content<button v-on:click="passNum">pass</button></div>',
        methods: {
          passNum: function() { // (2) 상위 컴포넌트로 pass 이벤트와 인자로 데이터 전달
            this.$emit('pass', 10); 
          }
        }
      }
    
      new Vue({
        el: '#app',
        components: {
          'app-header': appHeader,
          'app-content': appContent
        },
        data: {
          num: 0
        },
        methods: {
          deliverNum: function(value) { // (4) 하위 컴포넌트로부터 받은 이벤트를 통해 메서드 실행
            this.num = value;
          }
        }
      })
    </script>
    </body>

Router

싱글 페이지 애플리케이션을 구현할 경우 사용하는 라이브러리

Install Vue Router

  • CDN
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router@3.5.3/dist/vue-router.js">
  • NPM
npm install vue-router

Router Example

<body>
  <div id="app">
    <div>
      <!-- <router-link>는 router compoent로 이동할 수 있는 링크를 제공 -->
      <router-link to="/login">Login</router-link>
      <router-link to="/main">Main</router-link>
    </div>
    <!-- router의 compoents를 표현 -->
    <router-view></router-view>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/vue-router@3.5.3/dist/vue-router.js"></script>
  <script>
    var LoginComponent = {
      template: '<div>login</div>'
    }
    var MainComponent = {
      template: '<div>main</div>'
    }

    // 라우터 인스턴스 생성
    var router = new VueRouter({
      mode: 'history',  // URL의 해쉬 값 제거 속성
      routes: [// 페이지의 라우팅 정보 (Array)
        { // 로그인 페이지 정보
          path: '/login', // 페이지의 url
          component: LoginComponent // 해당 url에서 표시될 컴포넌트
        },
        { // 메인 페이지 정보
          path: '/main',
          component: MainComponent
        }
      ]
    });

    new Vue({
      el: '#app',
      router: router, // 인스턴스에 라우터 인스턴스를 연결 (#app 안에서 router 유효)
    });
  </script>
</body>

Vue Router
라우터 네비게이션 가드

Axios

axios

  • 뷰에서 권고하는 HTTP 통신 라이브러리
  • Promise 기반(JS 비동기 처리 패턴)이며 상대적으로 다른 HTTP 통신 라이브러리들에 비해 문서화가 잘되어 있고 API가 다양

Install Axios

  • CDN
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  • NPM
npm install axios

Axios Example

<body>
  <div id="app">
    <button v-on:click="getData">get user</button>
    <div>
      {{ users }}
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        users: []
      },
      methods: {
        getData: function() { 
          var vm = this;
          axios.get('https://jsonplaceholder.typicode.com/users/')
            .then(function(response) {
              console.log(response.data);
              vm.users = response.data;
            })
            .catch(function(error) {
              console.log(error);
            });
        }
      }
    })
  </script>
</body>

자바스크립트 비동기 처리와 콜백 함수
자바스크립트 Promise 쉽게 이해하기
자바스크립트 async와 await
{JSON} Placeholder
자바스크립트의 동작원리: 엔진, 런타임, 호출 스택
HTTP 프로토콜 Part 1
Chrome DevTools

Template Syntax

뷰로 화면을 조작하는 방법

  • 템플릿 문법은 크게 데이터 바인딩과 디렉티브로 나뉨

데이터 바인딩

  • 뷰 인스턴스에서 정의한 속성들을 화면에 표시하는 방법
<div>{{ message }}</div>
new Vue({
  data: {
    message: 'Hello Vue.js'
  }
})

디렉티브

  • 뷰로 화면의 요소를 더 쉽게 조작하기 위한 문법
  • v-if, v-for, v-bind, v-on, v-model ...
<div id="app">
  <p>{{ doubleNum }}</p>

  Hello <span v-if="show">Vue.js</span>

  <ul>
    <li v-for="item in items">{{ item }}</li>
  </ul>

  <!-- id에 인스턴스의 data-uuid를 연결 -->
  <p v-bind:id="uuid" v-bind:class="name">{{ num }}</p>

  <!-- v-if는 DOM을 완전히 제거하고,v-show는 display: none -->
  <div v-if="loading">
    Loading...
  </div>
  <div v-else>
    test user has been logged in
  </div>

  <div v-show="loading">
    Loading...
  </div>

  <!-- v-model은 양방향 데이터 바인딩을 지원하는 속성 -->
  <input type="text" v-model="message">
      <p>{{ message }}</p>
  </div>

  <!-- v-on은 이벤트 발생 속성 -->
  <button v-on:click="logText">click me</button>
  <input type="text" v-on:keyup.enter="logText">
new Vue({
  el: '#app'
  data: {
    num: 10,
    uuid: 'aaron1234'
    namd: 'text-navy'
    loading: false,
    show: true,
    items: ['shirts', 'jeans', 'hats']
    message: ''
  },
  computed: {
    doubleNum: function() {
      return this.num * 2;
    }
  },
  methods: {
    logText: function() {
      console.log('clicked');
    }
  }
})

Computed

특정 데이터의 변경사항을 실시간으로 처리

  • 캐싱을 이용하여 데이터의 변경이 없을 경우 캐싱된 데이터 반환
<body>
  <div id="app">
    <p v-bind:class="errorTextColor">Hello</p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        isError: false
      },
      computed: {
        errorTextColor: function() {
          return this.isError ? 'warning' : null;
        }
      }
    });
  </script>
</body>

Watch

특정 데이터의 변화를 감지하여 자동으로 특정 로직을 수행해주는 속성

<body>
  <div id="app">
    {{ num }}
    <button v-on:click="addNum">increase</button>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        num: 10
      },
      watch: {
        num: function() { // num이 변경되면 logText 메서드 실행
          this.logText();
        }
      },
      methods: {
        addNum: function() {
          this.num = this.num + 1;
        },
        logText: function() {
          console.log('changed');
        }
      }
    })
  </script>
</body>

VS Computed

  • watch
    • (매번 실행되기 부담스러운) 무거운 로직에 주로 사용
    • ex. 데이터 요청에 적합
  • computed
    • 단순한 값 계산에 주로 사용
    • ex. validation, 간단한 텍스트, 연산에 적합
computed: {
  doubleNum: function() {
    return this.num * 2;
  }
},
watch: {
  num: function(newValue, oldValue) {
    this.fetchUserByNumber(newValue);
  }
},
methods: {
  fetchUserByNumber: function(num) {
    // console.log(num);
    axios.get(num);
  }
}

Vue CLI

command-line interface

뷰로 빠르게 프로젝트를 구성하고 프로토타이핑을 하고 싶을 경우 사용하는 도구

npm install -g @vue/cli
# OR
yarn global add @vue/cli

Introduce

package.json 환경설정에 맞게 실행(scripts 참고)

npm run serve # node package manager
  • package.json
// ...
"scripts": {
  "serve": "vue-cli-service serve",
  "build": "vue-cli-service build",
  "lint": "vue-cli-service lint"
},
// ...
  • main.js
import Vue from 'vue'
import App from './App.vue' // .vue(single file component) 컴포넌트를 import
// var App = { <- 와 동일한 역할
//   template: '<div>app</div>'
// }

Vue.config.productionTip = false

new Vue({
  render: h => h(App), // import한 App이라는 component를 render
  // components: { <- 와 동일한 역할
  //   'app': App
  // }
}).$mount('#app')
// new Vue({ <- 와 동일한 역할
//   el: '#app',
// })

Single File Conponent

  • xxx.vue
<template>
  <!-- HTML. element가 최상위에 하나 존재해야 함.-->
  <div>header</div>
</template>

<script>
export default {
  // Javascript - Instance option
  methids: {
    addNum: function() {
      // ...
    }
  }
}
</script>

<style>
</style>

Using Import

App.vue

<template>
  <div>
    {{ str }}
    <!-- <app-header v-bind:하위 컴포넌트에서 정의한 프롭스 속성 이름="상위 컴포넌트의 데이터 이름"></app-header> -->
    <!-- 상위 컴포넌트의 str 이라는 데이터 필드를 하위 컴포넌트의 프롭스 속성으로 내려보내는 과정 -->
    <!-- 하위 컴포넌트로부터 renew 이벤트를 받아서 renewStr 메서드 실행 -->
    <AppHeader v-bind:propsdata="str" v-on:renew="renewStr"></AppHeader> 
    <!--
    컴포넌트 명명법 종류
    <app-header></app-header>
    <AppHeader></AppHeader>
    <AppHeader/>>
     -->
  </div>
</template>

<script>
import AppHeader from './components/AppHeader.vue'; // 1. 컴포넌트 내용을 import해서 변수 삽입

export default {
  // 인스턴스 옵션 속성 or 컴포넌트 옵션 속성
  data: function() {
    return {
      str: 'Header'
    }
  },
  components: {
    AppHeader, // 2. import한 컴포넌트를 연결 ('app-header': AppHeader 와 동일)
  },
  methods: {
    renewStr: function() {
      this.str = 'hi';
    }
  }
}
</script>

AppHeader.vue

<template>
  <header>
    <!-- 상위 컴포넌트로 받은 데이터 출력 -->
    <h1>{{ propsdata }}</h1>
    <button v-on:click="sendEvent">send</button>
  </header>
</template>

<script>
export default {
  props: ['propsdata'], // 프롭스 속성 이름
  methods: {
    sendEvent: function() {
      this.$emit('renew'); // 상위 컴포넌트로 이벤트 전달
    }
  }
}
</script>

vuejs cli

Example

사용자 입력 폼

<template>
  <!-- button의 submit 동작 시 submitForm 메서드 호출-->
  <form v-on:submit.prevent="submitForm">
    <div>
      <label for="username">id: </label>
      <input id="username" type="text" v-model="username">
    </div>
    <div>
      <label for="password">pw: </label>
      <input id="password" type="password" v-model="password">
    </div>
    <button type="submit">login</button>
  </form>
</template>

<script>
import axios from 'axios';

export default {
  data: function() {
    return {
      username: '',
      password: '',
    }
  },
  methods: {
    submitForm: function() {
      // event.preventDefault(); -> form 의 새로고침/이동 동작을 막기 위해 사용했던 것을 v-on:submit.prevent 로 해결
      var url = 'https://jsonplaceholder.typicode.com/users';
      var data = {
        username: this.username,
        password: this.password
      }
      axios.post(url, data)
        .then(function(response) {
          console.log(response);
        })
        .catch(function(error) {
          console.log(error);
        });
    }
  }
}
</script>

Reference

Vue.js 입문서

Cracking Vue.js

vuejs.org

.

Vue.js 공식 문서

Vue.js 스타일 가이드

Vue.js Cookbook

Vuex 공식 문서

VueRouter 공식 문서

Vue CLI 공식 문서

반응형
댓글
최근에 올라온 글
최근에 달린 댓글
링크
Total
Today
Yesterday