Vue.js
목차
- Vue.js란?
- Vue.js를 사용하는 이유
- Vue.js 시작하기
-
Vue.js 설치
-
Vue 디렉토리 구조
- Component 생성 및 데이터 바인딩
-
Component 간의 데이터 주고받기
-
Vue의 LifeCycle
1. Vue.js란?
나는 Vue에 대해서는 그냥 가볍고 쉽게 배울 수 있고 간결하게 코드를 짤 수 있는 javascript 프레임워크라고만 알고 있었지만 조금만 찾아보면 이런 정의를 내리는 글을 많이 볼 수 있다.
- javascript 프레임워크
- MVVM(Model-View-ViewModel) 패턴을 따르며 어플리케이션 로직과 UI분리를 위해 설계
- 데이터 바인딩과 화면단위를 컴포넌트 형태로 제공, 관련 API 지원
- Angular에서 지원하는 양방향 데이터 바인딩을 동일하게 제공, React의 단방향 데이터흐름을 사용하여 컴포넌트간 통신.
-MVVM : Model-View-ViewModel 화면 앞단의 화면 동작관련 로직과 뒷단의 DB데이터 처리 및 서버 로직을 분리하고 뒷단에서 넘어온 데이터를 Model에 담아 View로 넘겨주는 중간지점
-양방향 데이터 바인딩 : 화면에 표시되는 값과 프레임워크의 모델 데이터 값이 동기화되어 한쪽이 변경되면 다른 한 쪽도 자동으로 변경됨.
-단방향 데이터 바인딩 : 컴포넌트 단방향 통신. 컴포넌트 간 데이터를 전달할 때 항상 상위 컴포넌트에서 하위 컴포넌트 방향으로 전달하게끔 구조화.
덧붙여 Vue는 코딩하는 법이 정해져 있어서 협업 할 때 코딩 스타일을 맞추는 데에도 큰 이점이 있다. 실제로 협업 시 틀에 맞추어 코드를 짜는 것은 굉장히 큰 장점이다.
이렇게 단순하고 쉬운 장점이 있으며 다른 라이브러리와 기능적 차이가 없는 화면을 만들 수 있기 때문에 점점 Vue의 인기는 높아지고 있다. 그리고 이런 인기에 힘입어 꾸준히 업데이트가 되고 있다.
그리고 Vue는 공식 문서가 굉장히 잘 되어 있기 때문에 뷰알못일 때 공식문서로부터 많은 도움을 받았다.
2. Vue.js 시작하기
-
Vue는 Node.js 기반 Javascript 라이브러리이기 때문에 Node.js가 깔려있어야 한다. 최신버전 설치를 추천함.
-
작업공간 디렉토리를 생성하여 VS code 나 Web Storm 같은 에디터로 오픈하고 터미널을 열어 명령어를 입력한다.
npm i -g @vue/cli
그럼 이제 뷰가 깔린 거임 ㅎ
-
명령어를 통해 vue 프로젝트를 생성한다.
vue create [프로젝트명]
버전을 선택해준다 ( Vue 2 사용함 ). 근데 우리는 다양한 기능 옵션을 넣을 거라서 맨 아래에 있는 Manually select features 를 선택함.
이렇게 선택하고 Enter
Vue 2 버전 선택
y-y-y-맨위-맨위- 맨아래-n 선택
열심히 깔리고 있당..
이렇게 디렉토리가 자동으로 생성되면 vue 프로젝트 생성 끝!
터미널에 npm run serve 명령어를 입력하면 로컬 서버가 올라가고
위와 같은 화면을 확인할 수 있다.
vue 프로젝트 내부의 디렉토리와 파일들을 간단히 살펴보면 나중에 프로젝트를 할 때 정말 많은 도움이 되기 때문에 뷰를 처음 시작 할 때 꼭 알아두면 좋다.
- main.ts
**//main.ts**
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
우리가 사용할 Vue 인스턴스를 생성하고 그 안에 router, store, 렌더링 할 vue를 설정한다. 새로운 라이브러리를 설정할 때 해당 파일에 연결하여 설정하여 사용한다.
- App.vue & router/index.ts
**//App.vue**
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
<router-view/>
</div>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
</style>
**//router/index.ts**
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import HomeView from '../views/HomeView.vue'
//vue와 VueRouter연결
Vue.use(VueRouter)
//사용할 route생성 및 설정
const routes: Array<RouteConfig> = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
//router에 route를 등록하고 설정
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
router: 웹사이트에서 url경로를 따라 컴포넌트들을 보여주는 라이브러리. 웹사이트의 주소에 따라 해당 컴포넌트들을 렌더링.
- views/Home.vue & views/About.vue /component/Helloworld
**//views/Home.vue**
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
@Component({
components: {
HelloWorld,
},
})
export default class HomeView extends Vue {}
</script>
**//views/AboutVue.vue**
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
**//components/HelloWorld.vue**
<template>
<div class="hello">
<h1></h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class HelloWorld extends Vue {
@Prop() private msg!: string;
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
views 폴더는 사용자가 보이는 뷰들을 관리하는 폴더이다.
Home과 About Vue 파일을 보면 Component들을 Import 해서 사용하고 있다.
화면에 그려지는 View들을 조각조각 나눠 Component로 관리할 수 있다.
- store/index.js
**//store/index.ts**
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
Store 디렉터리는 vuex라이브러리를 선택하면 생성되는 디렉토리이다. vuex는 간단하게 한 저장 공간을 만들고 상태 관리 하고 싶은 State를 선언하여 여러 컴포넌트들이 접근해 사용할 수 있게 하는 라이브러리이다.
- asset
public한 이미지나 파일들을 asset 디렉토리에 넣어 관리한다.
3. 컴포넌트 생성 및 데이터 바인딩
화면의 영역을 일정한 단위로 쪼개어 재활용가능한 형태로 관리하는 것이 컴포넌트이다.
컴포넌트로 화면 레이아웃을 공통으로 사용할 수도 있고 Button과 같은 UI를 독립적인 Component UI로 만들어 사용할 수 있다.
뷰 컴포넌트를 생성해보자.
src/components디렉토리 하위에 Buttons.vue 파일을 생성한다.
**//components/Buttons.vue**
<template>
<div>
<p :style="color"></p>
<button @click="clicks"> click </button>
</div>
</template>
<script lang="ts">
import {Component, Vue} from "vue-property-decorator";
@Component({})
export default class Buttons extends Vue{
counts = 0;
color = 'color: blue';
clicks(){
this.counts++;
}
}
</script>
<style scoped>
</style>
Buttons.vue 컴포넌트의 구성은 크게 html <template> <script> <style>
로 구성되어있다.
html <template>
내부에는 화면을 구성하는 html태그를 작성한다. 등록한 컴포넌트를 이 곳에서 사용할 수 있다.
html <script>
내부에는 자바스크립트 기능을 작성한다. 컴포넌트를 등록하고 데이터와 메서드를 정의한다.
html <style>
에는 css를 작성한다.
Html 내부에 데이터를 넣는 것을 데이터 바인딩이라고 한다. Vue 에서는 이런 기호를 사용해서 데이터 바인딩을 할 수 있다. 그 전에 바인딩 할 데이터를 먼저
HTML 속성도 바인딩이 가능하다. style=””, class=””, disabled=”” 등의 속성도 속성 이름 앞에 : 기호를 붙여 정의된 데이터를 바인딩 할 수 있다.
이렇게 생성한 컴포넌트를 사용하려면 등록을 해야 한다.
HomeView.vue로 가서 우리가 만든 Buttons.vue 컴포넌트를 등록해보자.
**//views/HomeView.vue**
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<Buttons/>
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
import Buttons from "@/components/Buttons.vue";
@Component({
components: {
HelloWorld,
Buttons,
},
})
export default class HomeView extends Vue {}
</script>
우선 우리가 생성한 컴포넌트를 import 해 준 뒤 @Component 데코레이터를 사용하여 components 안에 컴포넌트를 등록할 수 있다. 컴포넌트를 등록해야 안에서
화면에서 Buttons 컴포넌트에서 생성한 button을 확인할 수 있다. HomeView.vue에 버튼을 끼워넣는 것으로 이해하면 쉽다. 컴포넌트 내부에 컴포넌트를 끼워넣을 수 있다. 이 때 컴포넌트를 등록하는 컴포넌트를 부모 컴포넌트라 하고 끼워지는 컴포넌트를 자식 컴포넌트라고 한다.
부모와 자식 컴포넌트 사이에는 서로 데이터, 이벤트등을 주고 받을 수 있는데 이러한 기능은 특정한 문법을 사용해야 한다.
Props는 “부모 컴포넌트에서 자식 컴포넌트에게 데이터를 전달”
Emit은 “자식 컴포넌트에서 부모 컴포넌트에게 데이터를 전달” 한다.
이 때 전달 키와 받는 키가 동일해야 하며 받는 키를 등록해주어야 한다.
우선 부모 컴포넌트에서 자식컴포넌트로 데이터를 전달하려면 Props를 사용한다.
**//HomeView.vue**
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<Buttons
:color="color"
/>
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
import Buttons from "@/components/Buttons.vue";
@Component({
components: {
HelloWorld,
Buttons,
},
})
export default class HomeView extends Vue {
color = 'color: green';
}
</script>
**//Buttons.vue**
<template>
<div>
<p :style="color"></p>
<button @click="clicks"> click </button>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";
@Component({})
export default class Buttons extends Vue{
@Prop() color: string;
counts = 0;
clicks(){
this.counts++;
}
}
</script>
<style scoped>
</style>
@Prop 데코레이터를 사용하여 부모 컴포넌트에서 넘겨받은 데이터를 선언한다. 이 때 데이터 key 값과 타입을 함께 선언해준다.
부모 컴포넌트로부터 받은 데이터가 style 속성에 바인딩되어 글씨 색이 바뀌었다.
자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달하기 위해서는 emit을 사용한다.
**//Buttons.vue**
<template>
<div>
<p :style="color"></p>
<button @click="clicks"> click </button>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";
@Component({})
export default class Buttons extends Vue{
@Prop() color: string;
counts = 0;
clicks(){
this.counts++;
this.$emit('colorChange','color:red');
}
}
</script>
<style scoped>
</style>
**//HomeView.vue**
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<Buttons
:color="color"
@colorChange="colorChange"
/>
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
import Buttons from "@/components/Buttons.vue";
@Component({
components: {
HelloWorld,
Buttons,
},
})
export default class HomeView extends Vue {
color = 'color: green';
colorChange(data){
this.color = data;
}
}
</script>
this.$emit( [key값], [전달할 데이터] );
emit은 부모 컴포넌트에서 이벤트를 발생시킬 수 있다.
click 이벤트 발생 시 emit으로 부모 컴포넌트의 메서드가 호출되어 글자의 색이 바뀐다.
템플릿 내부에서 사용할 수 있는 v-if, v-for 문법에 대해서 잠깐 다뤄보자.
v-if는 뷰에서 조건에 따라 요소를 보여주고 숨길 수 있는 문법이다.
**//Buttons.vue**
<template>
<div>
<p :style="color"></p>
<div v-if="show"> 보입니다. </div>
<div v-else> 안 보입니다. </div>
<button @click="clicks"> click </button>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";
@Component({})
export default class Buttons extends Vue{
@Prop() color: string;
counts = 0;
show= false;
clicks(){
this.counts++;
this.show = !this.show;
this.$emit('colorChange','color:red');
}
}
</script>
클릭을 하게 되면 show의 값이 바뀌면서 출력되는 div가 달라진다. div에 v-if로 조건을 걸어주었기 때문이다. v-else는 if~else 문의 else와 같은 기능을 한다. v-if조건이 충족되지 않을 시 v-else가 걸려 있는 요소가 화면에 출력된다.
v-for는 뷰에서 반복문을 사용하기 위한 문법이다.
//Buttons.vue
<template>
<div>
<p :style="color"></p>
<div v-if="show"> 보입니다. </div>
<div v-else> 안 보입니다. </div>
<button @click="clicks"> click </button>
<p v-for="item in playList" :key="item"> </p>
<hr/>
<p v-for="idx in 4" :key="item"> </p>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";
@Component({})
export default class Buttons extends Vue{
@Prop() color: string;
counts = 0;
show= false;
playList = [
'Attention',
'Hype Boy',
'Cookie',
'Hurt'
]
clicks(){
this.counts++;
this.show = !this.show;
this.$emit('colorChange','color:red');
}
}
</script>
<style scoped>
</style>
v-for은 리스트를 이용하여 리스트 내부 객체에 대한 반복문을 돌릴 수 있고 숫자만으로도 반복문을 돌릴 수 있다. key값은 각 항목을 구분할 수 있는 유일한 값이어야 한다. key값이 변하면서 재렌더링 여부를 파악하기 때문이다.
Vue Component는 생성 후 소멸될 때까지 생명주기가 있다. Vue를 사용해서 개발을 했을 때 한 화면에 정말 다양하고 많은 Component들을 만들어서 사용하게 되는데 이것을 효율적으로 관리하기 위해 생명주기를 잘 지켜야 한다.
크게 4가지 상황으로 나누어 보면
- Creation(생성) 단계
- Mounting(장착) 단계
- Updating(수정) 단계
- Destrucion(소멸) 단계
- 컴포넌트를 보여줄 때 create -> mount 이 단계로 생성. create는 데이터생성, mount는 index.html 파일에 장착.
- 데이터가 바뀌어서 컴포넌트가 재렌더링될 때는 update 단계를 거치며
- 다른페이지로 이동하거나 그럴 때 컴포넌트가 삭제될 때는 unmount 라는 단계를 거친다.