05 뷰 고급 기능 익히기
- computed: html 엘리먼트 값이 어떻게 변경되는 살펴보면서
필요한 연산작업을 도와줌
- methods: 뷰 인스턴스를 포함해 사용하는 함수를 말한다.
이벤트 핸들러를 사용해 마우스 클릭과 같은 이벤트 발생시
실행되는 로직이 많이 사용한다.
- 컴포넌트 html 엘리먼트 만들기: 기본 엘리먼트 외에 자신만의
엘리먼트를 만들어 쓰는 모듈을 말함
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<style>
.fruit-style {
border: 1px solid #ccc;
background-color: white;
padding-left:1em;
}
</style>
</head>
<body>
<div id="main">
<h1>{{ sTitle }}</h1>
<favorite-fruits></favorite-fruits>
<favorite-fruits></favorite-fruits>
</div>
</body>
<script>
Vue.component('favorite-fruits', {
data: function () {
return {
aFruits: [{ sFruit_name: '사과'},
{ sFruit_name: '오렌지'},
{ sFruit_name: '수박'}]}
},
template:`
<div>
<div v-for='item in aFruits' class='fruit-style'>
<p> 좋아하는 과일 : {{ item.sFruit_name}}</p>
</div>
<br>
</div>`
});
new Vue({
el:'#main',
data: {
sTitle: '안녕하세요'
}
});
</script>
</html>
- props: 컴포넌트에서 전달되는 속성값을 말하며 문자열이나 객체의
배열 형식으로 되어 있음
- 상탯값 관리와 Vuex:
state: 공유한 상태값 데이터의 정의
mutations: setters의 의미로 이해, 외부에서 동기 방식으로 저장할때 사용
getters: state의 데이터값을 외부에서 읽어 올 때 사용
actions: 외부의 api 실행 같은 비동기 실행을 관리할 때 사용
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/vuex"></script>
</head>
<body>
<div id="app">
<h1> 안녕하세요! </h1>
<com-counter msg="카운터1"></com-counter>
<com-counter msg="카운터2"></com-counter>
</div>
<script>
const store = new Vuex.Store({
state:{
count: 0
},
mutations: {
fnIncData: function (state) {
return state.count++
},
fnDecData: state => state.count--
},
getters: {
fnGetData(state) {
return state.count;
},
},
actions: {
async fnDecData({
commit
}, state) {
const result = await api.fnDecrement();
if(result == true) commit('fnDecData')
}
}
})
const api = {
fnDecrement() {
return new Promise((resolve)=> {
setTimeout(() => {
resolve(true);
}, 1000);
});
},
};
Vue.component('com-counter', {
props: ['msg'],
template:`
<div>
<h2>{{ msg }}</h2>
<p>카운터: {{ fnGetCount }} </p>
<button @click="fnIncCount">+1 증가</button>
<button @click="fnDecCount">-1 감소(원격 API 실행)</button>
<hr>
</div>`,
computed: {
fnGetCount() {
return store.getters.fnGetData;
}
},
methods: {
fnIncCount() {
store.commit('fnIncData')
},
fnDecCount() {
store.dispatch('fnDecData')
}
}
})
var gApp = new Vue({
el: '#app',
store
})
</script>
</body>
</html>
내비게이션과 라우터
라우터: 페이지끼리 이동할수 있는 기능
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<h1>안녕</h1>
<router-view></router-view>
<hr>
<p>라우터 사용:
<router-link to ="/main">메인 페이지 이동</router-link>
<router-link to ="/sub">메인 페이지 이동</router-link>
</p>
</div>
<script>
const tmMain = {
template: '<h2> 메인 페이지 입니다.</h2>'
}
const tmSub = {
template: '<h2> 서브페이지 입니다 </h2>'
}
const rtRoutes = [{
path: '/main',
component: tmMain
},
{
path: '/sub',
component: tmSub
}]
const router = new VueRouter({
routes: rtRoutes
})
var gApp = new Vue({
el: '#app',
router
})
</script>
</body>
</html>
프로젝트 만들기
// App.vue
<template>
<div id="app">
<h1>안녕</h1>
<router-view></router-view>
<hr>
<p>라우터 사용:
<router-link to ="/main">메인 페이지 이동</router-link>
<router-link to ="/sub">서브 페이지 이동</router-link>
</p>
</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>
// main_page.vue
<template lang="html">
<div>
<h2>메인 페이지</h2>
<button @click="fnSubPage">서브페이지 이동</button>
</div>
</template>
<script>
export default {
methods: {
fnSubPage() {
this.$router.push('./sub')
}
}
}
</script>
// sub_page.vue
<template lang="html">
<div>
<h2>서브 페이지</h2>
<button @click="fnMainPage">메인페이지 이동</button>
</div>
</template>
<script>
export default {
methods: {
fnMainPage() {
this.$router.push('./main')
}
}
}
</script>
06 뷰티파이 기초
vuetify: 뷰 자바스크립트 프레임워크에 멀티리얼 디자인을
사용할 수 있는 컴포넌트 프레임워크
특징
- 구글 머티리얼 스펙 지원
- 80개 이상의 시맨틱 머터리얼 디자인 컴포넌트
- 빠른속도
- 쉬운 학습
엡바 영역: v-app-bar
본문 영역: v-content
여백 자동 설정: v-container
바닥글 영역: v-footer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no, minimal-ui">
<link href="https://fonts.googleapis.com/css?fmaily=Roboto:100,300,400,500,700,900|Material+icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font3.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
</head>
<body>
<div id="app">
<v-app>
<v-app-bar app>
<v-app-bar-nav-icon></v-app-bar-nav-icon>
<v-toolbar-title>header입니다.</v-toolbar-title>
</v-app-bar>
<v-content>
안녕하세요
<v-container>Contents입니다.</v-container>
</v-content>
<v-footer>
<div>Footer입니다</div>
</v-footer>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify()
})
</script>
</body>
</html>
앱바 스타일:
v-app-bar 엘리먼트 사용
- 색상 : (
primary:파란색,
secondary:짙은 회색,
accent: 콘플라워 블루,
error: 빨간색,
info:바다색,
success:초록색,
warning:노란색
)
dark 속성을 지정하면 글자색을 흰색으로
바꾼 후 테마와 글자의 명도를 자동 조정
fixed 속성 스크롤에 영향받지 않고 앱바의 위치를 고정
앱바 메뉴 아이콘: 앱바 영역이 지정되면 그 안에 메뉴 아이콘
제목 등을 넣을 수 가 있음
v-app-bar-nav-icon,
제목은 v-toolbar-title
앱바 정렬 및 아이콘:
앱바의 메뉴 아이콘과 텍스트를 오른쪽 정렬로 넣고 싶다면
v-spacer 엘리먼트 하나넣어 구분
구글 제공하는 아이콘을 사용하고 싶다면 v-btn 엘리먼트
버튼을 먼저 넣고 아이콘의 공식 이름을 지정
본문영역과 자동 여백:
v-content 단독으로 쓰이면 사용할수 있는 모든 영역을 본문으로 잡음
v-container 안에 있는 모든 엘리먼트를 대상으로 화면크기에 맞는 적절한
여백을 자동으로 부여함
항상 v-content를 먼저사용하고 그다음에 v-container를 레이아웃에
맞춰서 사용한다.
서체 크기와 종류 설정:
타이포그래피 스타일은 display, headline, title, subheading, body, caption
엘리먼트 여백:
m 바깥쪽 여백
p 안쪽 여백
(t top, b bottom, l left, r right, x left right, y top bottom)
바닥글 스타일:
푸터 v-footer 엘리먼트를 사용
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no, minimal-ui">
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@3.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.2.x/dist/vuetify.min.css" rel="stylesheet">
</head>
<body>
<div id="app">
<v-app>
<v-app-bar app color="primary" dark fixed>
<v-app-bar-nav-icon></v-app-bar-nav-icon>
<v-toolbar-title>마스터 페이지</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</v-app-bar>
<v-content>
<v-content>
<h1 class="display-1 my-5">안녕하세요</h1>
<h1 class="body-2 my-5">마스터 페이지</h1>
<v-divider></v-divider>
<h1 class="display-3 my-4">안녕하세요</h1>
<p class="body-1 my-4">마스터 페이지</p>
</v-content>
</v-content>
<v-footer color="secondary" dark fixed>
<div class="mx-auto">copylight ©</div>
</v-footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify()
})
</script>
</body>
</html>