티스토리 뷰
Vue
회사에서 front 업무(?)도 살짝 담당하게 되어 Vue.js를 공부하게 되었다.
아주 옛날에 배웠던 것 같긴 한데.. 아무것도.. 생각이 나지 않는다... Haha.h...a...
그리하여 Inflearn Vue.js 시작하기 - Age of Vue.js 강의를 듣게 되었고, 업무를 하면서 틈틈이 참고하고자 간략하게 내용들을 정리해 보았다.
아주 쉽고 빠르게 Vue.js 세계를 이해시켜주신 장기효님께 감사를.. 👏🏻👏🏻👏🏻
What is the Vue
View
: 눈에 보이는 화면 (화면의 요소는 HTML)- HTML은 DOM을 이용해서 javascript로 조작 -> Reactivity
DOM Listeners
: View에서 사용자 이벤트는 Vue의 DOM Listeners로 청취Modal
: Vue에서 청취한 이벤트를 통해 javascript에 있는 데이터를 변경하거나 특정 로직을 실행- javascript를 통해 데이터가 변경되었을 경우
Data Bindings
동작하여 View에 반영
- javascript를 통해 데이터가 변경되었을 경우
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>
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>
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
.
'Web' 카테고리의 다른 글
Java / Spring Framework / Spring Boot Release (0) | 2023.01.03 |
---|---|
[Troubleshooting] Enum Class(싱글톤)의 동시성 이슈 (0) | 2022.11.21 |
Refactoring With IntelliJ Summary (0) | 2022.07.08 |
[NoSQL] MongoDB 탐구하기 (0) | 2022.03.27 |
[Message Queue] Apach Kafka 탐구하기 (0) | 2022.03.22 |