Vue.js 사용 안한지 오래되서, 한번 문법 정리하는 시간을 갖음.
한번 문법 정리 후, Codepen을 이용하여 예제 샘플 나열해서 정리해둬야겠다…
필수요소
Link : https://kr.vuejs.org/v2/guide/installation.html 링크 학습
설치방법
npm
cli
Link: https://cli.vuejs.org/
번들러
번들러는 Vue.js + Webpack 템플릿 보면 알 수 있을 듯
시작하기
선언적 렌더링
템플릿 안 변수 삽입
1
| <div id="app">{{ message }}</div>
|
1 2 3 4 5 6
| var app = new Vue({ el: `#app`, data: { message: `Hello, World` } })
|
v-bind
= 단방향 바인딩 (v-dom -> dom)
1 2 3 4 5
| <div id="app-2"> <span v-bind:title="message"> 텍스트 </span> </div>
|
1 2 3 4 5 6
| var app2 = new Vue({ el: `#app-2`, data: { message: `데이터: ${new Date()}` } })
|
조건문 / 반복문
조건문 v-if
1 2 3 4 5
| <div id="app-3"> <p v-if="seen"> seen의 bool값에 따라 보일지 결정됨 </p> </div>
|
1 2 3 4 5 6
| var app = new Vue({ el: `#app-3`, data: { seen: true } })
|
반복문 v-for
1 2 3 4 5 6 7
| <div id="app-4"> <ol> <li v-for="todo in tods"> {{ todo.text }} </li> </ol> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| var app4 = new Vue({ el: `#app-4`, data: { todos: [ {text: `Text1`}, {text: `Text2`}, {text: `Text3`} ] } })
|
사용자 입력 핸들링 v-on:event
, v-model
v-on
1 2 3 4 5 6
| <div id="app-5"> <p>{{message}}</p> <button v-on:click="onclickBtn"> 버튼 </button> </div>
|
1 2 3 4 5 6 7 8 9 10 11
| var app5 = new Vue({ el: `#app-5`, data: { message: `First Message.` }, methods: { onclickBtn: function () { this.message = `Message Change!` } } })
|
v-model
= 양방향 바인딩
1 2 3 4
| <div id="app-6"> <p>{{ message }}</p> <input v-model="message"> </div>
|
1 2 3 4 5 6
| var app6 = new Vue({ el: `#app-6`, data: { message: `Hello` } })
|
컴포넌트 이용법
1 2 3 4 5 6
| Vue.component(`todo-item`, { props: [`todo`] template: `<li>{{ todo.text }}</li>` })
var app = new Vue(...)
|
1 2 3 4 5 6 7 8 9
| <div id="app-7"> <ol> <todo-item v-for="item in list" v-bind:todo="item" v-bind:key="item.id" ></todo-item> </ol> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Vue.component(`todo-item`, { props: [`todo`], template: `<li>{{ todo.text }}</li>` })
var app7 = new Vue({ el: `#app-7`, data: { list: [ {id: 0, text: `text1`}, {id: 1, text: `text2`}, {id: 2, text: `text3`} ] } })
|
Vue 인스턴스
vue 인스턴스 만들기
속성과 메소드
1 2 3 4 5 6 7 8 9 10 11 12 13
| var data = {a: 1}
var vm = new Vue({ data: data })
vm.a === data.a
vm.a = 2 data.a
data.a = 3 vm.a
|
Object.freeze()
1 2 3 4 5 6 7 8 9 10 11
| var obj = { foo: `bar` }
Object.freeze(obj)
new Vue({ el: `#app`, data: obj })
|
1 2 3 4 5 6
| <div id="app"> <p>{{ foo }}</p> <button v-on:click="foo = 'baz'"> Change </button> </div>
|
$
유용한 속성 및 메소드
1 2 3 4 5 6 7 8 9 10 11 12
| var data = {a: 1} var vm = new Vue({ el: `#example`, data: data })
vm.$data === data vm.$el === document.querySelector(`#example`)
vm.$watch(`a`, (newVal, oldVal) => { })
|
인스턴스 라이프사이클 훅
1 2 3 4 5 6 7 8 9 10
| new Vue({ data: { a: 1 }, created: function() { console.log(`a = ${this.a}`) }, })
|
beforeCreate
가장 먼저 실행되는 훅
data, events(vm.$on, $once, $off, $emit) 세팅되지 않음
created
data와 events가 활성화된 시점 ~ 템플릿과 가상돔은 마운트·렌더링 되지 않음
beforeMount
렌더링 일어나기 직전
mounted
컴포넌트, 템플릿, 돔에 접근가능한 시점, 모든 하위 컴포넌트 마운트를 보장하지는 않음
자식부터 차근차근 생성 후 부모가 생성되기 때문에 주의 (무조건, 순서대로 되지 않을 수도 있음)
beforeUpdate
dom이 새로 업데이트되기 직전 시점
updated
재 렌더링 이후, 실행된다. 잘못 작성한다면 무한루프에 빠질 수 있음
beforeDestroy
뷰 인스턴스 제거되기 직전 시점, 컴포넌트의 원래 형태를 그대로 유지된 상태
이벤트 리스너를 제거하기 좋은 시점
destroyed
인스턴스 제거 후 시점, vue의 모든 디렉티브 바인딩과 리스너가 제거
템플릿 문법
interpolation
v-once
1
| <span v-once>한번 이후, 변경되지 않음 {{ msg }}</span>
|
v-html
1 2
| # rawHtml = `<span style="color:red">HTML</span>` <span v-html="rawHtml"></span>
|
데이터 바인딩이 무시됨, Vue는 문자열 기반 템플릿 엔진은 아니여서, v-html
을 이용한 템플릿을 사용할 수는 없음
또한 XSS 취약점이 존재하기 때문에, 사용자 폼에서는 금지
v-bind:
1
| <div v-bind:id="dynamicId"></div>
|
1
| <button v-bind:disabled="isButtonDisabled">Button</button>
|
isButtonDisabled
가 null
, undefined
, false
면 속성으로 표시되지 않음
Javascript 표현식
1 2 3 4 5 6
| {{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split(``).reserve().join(``) }}
|
1
| <div v-bind:id="'last-' + id"></div>
|
디렉티브
동적 전달인자 (2.6.0+부터 생김)
1 2 3
| <a v-bind:[attribueName]="url">...</a>
<a v-on:[attribueName]="function">...</a>
|
대신 스페이스나 따옴표를 쓰지말 것, 또한 소문자만 쓰는 것이 좋음
1 2
| <a v-bind:['foo' + bar]="value"> ... </a>
|
preventDefault()
1
| <form v-on:submit.prevent="onSubmit"></form>
|
약어
v-bind 약어
1 2 3 4 5 6 7 8
| <a v-bind:href="url">...</a>
<a :href="url">...</a>
<a :[key]="url">...</a>
|
v-on 약어
1 2 3 4 5 6 7 8
| <a v-on:click="function">...</a>
<a @click="function">...</a>
<a @[event]="function">...</a>
|
computed와 watch
computed 배경
너무 많은 템플릿 내 표현식은 유지보수가 힘듬
1 2 3 4
| <div id="example"> {{ message.split('').reverse().join('') }} </div>
|
computed 예제
1 2 3 4
| <div id="example"> <p>원본: {{ message }}</p> <p>역순: {{ reversedMessage }}</p> </div>
|
1 2 3 4 5 6 7 8 9 10 11
| var vm = new Vue({ el: `#example`, data: { message: `안녕` }, computed: { reversedMessage: function() { return this.message.split(``).reverse().join(``) } } })
|
computed vs method
차이점: computed
는 종속 대상이 변경될 때만 함수를 실행하고, 변경되지 않으면 그 이전 계산했던 값을 돌려줌
Date.now()
처럼 아무곳에 의존되지 않은 값은 업데이트 되지 않음
이것과 비교하여 method
는 항상 함수를 재실행함
computed vs watch
1
| <div id="demo">{{ fullName }}</div>
|
비교(watch)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var vm = new Vue({ el: `#demo`, data: { firstName: `TAEUK`, lastName: `KANG`, fullName: `TAEUK KANG` }, watch: { firstName: function(val) { this.fullName = val + ` ` + this.lastName }, lastName: function(val) { this.fullName = this.firstName + ` ` + val } } })
|
computed
1 2 3 4 5 6 7 8 9 10 11 12
| var vm = new Vue({ el: `#demo`, data: { firstName: `TAEUK`, lastName: `KANG` }, computed: { fullName: function() { return `${this.firstName} ${this.lastName}` } } })
|
computed(setter)
computed는 기본적으로 getter함수이지만, setter로 지정하여 만들 수 있음
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| computed: { fullName: { get: function() { return `${this.firstName} ${this.lastName}` }, set: function(newValue) { var names = newValue.split(` `) this.firstName = names[0] this.lastName = names[names.length - 1] } } }
|
vm.fullName = TAEUK KANG
실행하면, 업데이트가 되는 것을 볼 수 있음
watch
비동기식 또는 시간이 많이 소요되는 조작을 수행하려는 경우에 가장 유용
1 2 3 4 5 6 7
| <div id="watch-example"> <p> yes/no ? <input v-model="question"> </p> <p>{{ answer }}</p> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| var watchExampleVM = new Vue({ el: `#watch-example`, data: { question: ``, answer: `대답 미입력` }, watch: { question: function(newQuestion) { this.answer = `입력 대기 중...` this.getAnswer() } }, methods: { getAnswer: _.debounce( function() { if (this.question.indexOf(`?`) === -1) { this.answer = `질문에 물음표를 넣어줘` return } this.answer = `생각 중...` var vm = this axios.get(`api url`).then(res => { vm.answer = _.capitalize(res.data.answer) }).catch(err => { vm.answer = `Err! ${err}` }) }, 500 ) } })
|
클래스와 스타일 바인딩
클래스 동적 토글
1
| <div v-bind:class="{ active isActive }"></div>
|
1 2 3 4
| <div class="static" v-bind:class="{ active: isActive, 'test-danger': hasError}"> </div>
|
1 2 3 4
| data: [ isActive: true, hasError: false ]
|
결과값:
1
| <div class="static active"></div>
|
굳이 인라인 스크립트를 쓰지 않아도 됨
1
| <div v-bind:class="classObject"></div>
|
1 2 3 4 5 6
| data: { classObject: { active: true, 'text-danger': false } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.is Active && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } }
|
배열
1
| <div v-bind:class="[activeClass, errorClass]"></div>
|
1 2 3 4
| data: { activeClass: `active`, errorClass: `text-danger` }
|
Result:
1
| <div class="active text-danger"></div>
|
삼항연산자
1
| <div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
|
1
| <div v-bind:class="[{active: isActive}, errorClass]"
|
컴포넌트
Vue 컴포넌트의 기존 클래스가 덮어쓰기 되지않고, 추가됨. 바인딩도 마찬가지
1 2 3
| Vue.component(`my-component`, { template: `<p class="foo bar">Hi</p>` })
|
1
| <my-component class="baz boo"></my-component>
|
Result:
1
| <p class="foo bar baz boo"></p>
|
인라인 스타일 바인딩
b-bind:style
속성 이름에 camelCase와 kebab-case (따옴표를 함께 사용해야 합니다)를 사용
1
| <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px'}"></div>
|
1 2 3 4
| data: { activeColor: `red`, fontSize: 30 }
|
데이터 밑 객체를 삽입하여 간결하게
1
| <div v-bind:style="styleObject"></div>
|
1 2 3 4 5 6
| data: { styleObject: { color: `red`, fontSize: `13px` } }
|
1 2
| <div v-bind:style="[baseStyles, overridingStyles]"></div>
|
자동 접두사
1
| <div v-bind:style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
|
그런데 그냥 PostCSS의 autoPrefixer을 쓰는게 편해보임.
조건부 렌더링
v-if
, v-else
1 2 3 4 5 6 7
| <h1 v-if="ok">Yes</h1> <h1 v-else>No</h1>
<div v-if="type === 'A'">A</div> <div v-else-if="type === 'B'">B</div> <div v-else-if="type === 'C'">C</div> <div v-else>Not A/B/C</div>
|
v-else
엘리먼트는 v-if
엘리먼트 또는 v-else-if
엘리먼트 바로 뒤에 있어야 합니다. 그렇지 않으면 인식할 수 없습니다.
<template>
의 v-if
1 2 3 4 5 6 7 8 9 10 11 12
| <template v-if="ok"> <h1>Title</h1> </template>
<template v-if="loginType === 'username'"> <label>사용자 이름</label> <input placeholder="사용자 이름을 입력하세요" key="username-input"> </template> <template v-else> <label>이메일</label> <input placeholder="이메일 주소를 입력하세요" key="email-input"> </template>
|
key
속성이란, “이 두 엘리먼트는 완전히 별개이므로 다시 사용하지 마십시오.”라고 알리는 행위
없으면 input
엘리먼트를 재사용함
v-show
1
| <h1 v-show="ok">안녕하세요!</h1>
|
v-if
와 다른 점은 항상 DOM에 남겨져 있고, css의 display 상태로만 감춰져있음
그러므로, 매우 자주 바뀌는 렌더링 비용은 v-show
를 이용하는 것을 권장
주의사항 v-for
우선순위
v-for
가 v-if
보다 높은 우선순위 권을 갖기 때문에 사용에 주의할 것
리스트 렌더링
1 2 3
| <ul id+"example-1"> <li v-for="(item, index) in items">{{ index }}: {{ item.message }}</li> </ul>
|
1 2 3 4 5 6 7 8
| var example1 = new Vue({ data: { items: [ { message: `item1`}, { message: `item2`} ] } })
|
in
대신 of
반복문도 사용 가능 (차이점은 일반적인 JS문법과 동일: in은 key값을 순회, of는 value값을 순회)
v-for
와 객체
1 2 3
| <ul id="v-for-object" class="demo"> <li v-for="(value, key, index) in object">{{index}}. {{key}}: {{value}}</li> </ul>
|
1 2 3 4 5 6 7 8 9 10
| new Vue({ el: `#v-for-object`, data: { object: { title: `제목`, author: `저자`, publishedAt: `2019-12-29` } } })
|
Result
1 2 3
| 0. title: 제목 1. author: 저자 2. publishedAt: 2019-12-29
|
주의점
객체 순서가 일관적이지 않음. Object.keys()
의 결과에 따름.
Maintaining State
이벤트 핸들링
폼 입력 바인딩
컴포넌트
컴포넌트 모아보기
컴포넌트 등록
Props
커스텀 이벤트
슬롯(slots)
동적 & 비동기 컴포넌트
Handling Edge Cases
트랜지션 & 애니메이션
진입/진출? 봐야알 듯
상태 트랜지션
재사용 & 컴포지션
믹스인
사용자 지정 디렉티브
Render Functions & JSX
플러그인
사용법
1 2 3 4 5
| Vue.use(MyPlugin)
new Vue({ // ...options })
|
Vue.use
는 자동으로 같은 플러그인 중복사용을 방지함 (한번만 설치됨)
작성법
추후 작성
필터
도구
싱글 파일 컴포넌트
단위 테스팅
Typescript
배포
스케일링 업
라우팅
상태 관리
서버사이드 렌더링
Security
내부
반응형