1. Vue 시작하기
<script src="./js/vue.js"></script>
<!-- 1. Vue 인스턴스 생성 시 반드시 옵션 객체를 전달해야 함 -->
<!-- 2. 컨테이너 내부 코드는 HTML 규칙을 따르며, Vue 전용 문법이 추가됨 -->
<!-- 3. 컨테이너 코드 = Vue 템플릿 -->
<!-- 4. Vue 인스턴스와 컨테이너는 1:1 관계 -->
<!-- 5. 실제 프로젝트에서는 단일 인스턴스 + 컴포넌트 구조 -->
<!-- 6. {{ xxx }} 는 JS 표현식이며, data 속성에 접근 가능 -->
<!-- 7. data 변경 시 해당 데이터를 사용하는 템플릿이 자동 갱신됨 -->
<div id="root">
<h1>hello, {{name}}</h1>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
name: '홍길동'
}
});
</script>
2. 템플릿 문법
<!--
1. 보간법(Interpolation): 태그 본문 내용 해석
- {{ xxx }} 형식, data 속성 접근 가능
2. 디렉티브(Directive): 태그 속성, 이벤트 바인딩 등
- v-bind:href='xxx' 또는 :href='xxx' 로 축약
-->
<div id="root">
<h1>보간법 예시: {{name}}</h1>
<a v-bind:href="site.url">{{site.name}} 방문하기 1</a>
<a :href="site.url">{{site.name}} 방문하기 2</a>
</div>
<script>
new Vue({
el: '#root',
data: {
name: '홍길동',
site: {
name: 'Example',
url: 'https://example.com'
}
}
});
</script>
3. 데이터 바인딩
<!--
1. 단방향 바인딩 (v-bind): data → 페이지
2. 양방향 바인딩 (v-model): data ↔ 페이지
- 주로 form 요소(input, select 등)에 사용
- v-model:value 는 v-model 로 축약 가능 (value가 기본값)
-->
<div id="root">
단방향: <input type="text" v-bind:value="username">
양방향: <input type="text" v-model="username">
</div>
<script>
new Vue({
el: '#root',
data: { username: '기본값' }
});
</script>
4. el과 data의 두 가지 선언 방식
<!--
el 선언 방식:
1. new Vue 시 el 속성 직접 지정
2. 인스턴스 생성 후 $mount() 호출
data 선언 방식:
1. 객체 형태
2. 함수 형태 (컴포넌트에서 필수, Vue 인스턴스에서도 권장)
주의: Vue가 관리하는 함수에서 화살표 함수 사용 시 this가 Vue 인스턴스를 가리키지 않음
-->
<div id="root">
<h1>{{message}}</h1>
</div>
<script>
// el 방식 1
const app = new Vue({
el: '#root',
data: { message: '안녕하세요' }
});
// el 방식 2
// app.$mount('#root');
// data 방식 1 (객체) - 인스턴스에서만 가능
// data: { message: '안녕하세요' }
// data 방식 2 (함수) - 권장, 컴포넌트에서 필수
// data: function() {
// console.log(this); // Vue 인스턴스
// return { message: '안녕하세요' };
// }
</script>
5. MVVM 모델
/*
M: Model - data 내 데이터
V: View - 템플릿 코드
VM: ViewModel - Vue 인스턴스
특징:
1. data의 모든 속성은 vm에 자동 등록
2. vm의 속성과 Vue 프로토타입 속성은 템플릿에서 직접 사용 가능
*/
6. Object.defineProperty() 이해
<script>
let ageValue = 18;
let person = { name: '김철수', gender: '남성' };
Object.defineProperty(person, 'age', {
get() {
console.log('age 속성 읽기 요청');
return ageValue;
},
set(newValue) {
console.log('age 속성 변경 요청:', newValue);
ageValue = newValue;
}
});
console.log(person.age); // getter 호출
person.age = 25; // setter 호출
</script>
7. 데이터 프록시 기초
<script>
let source = { x: 100 };
let proxy = { y: 200 };
Object.defineProperty(proxy, 'x', {
get() { return source.x; },
set(value) { source.x = value; }
});
</script>
8. Vue의 데이터 프록시
/*
Vue는 vm 객체를 통해 data 속성의 읽기/쓰기를 프록시함
장점: data에 직접 접근하지 않고 vm을 통해 편리하게 조작 가능
원리: Object.defineProperty()로 data의 모든 속성을 vm에 추가하고,
각 속성에 getter/setter를 설정하여 data 객체를 간접 제어
*/
9. 이벤트 처리
9.1 기본 사용법
<div id="root">
<h2>{{siteName}}에 오신 것을 환영합니다</h2>
<button v-on:click="showInfo">정보 보기</button>
<button @click="showDetail(1, $event)">상세 보기</button>
</div>
<script>
new Vue({
el: '#root',
data() { return { siteName: 'MySite' }; },
methods: {
showInfo(event) {
alert('안녕하세요!');
},
showDetail(id, event) {
console.log('ID:', id, 'Event:', event);
alert('상세 정보입니다.');
}
}
});
</script>
9.2 이벤트 수식어
<!--
.prevent : 기본 동작 방지
.stop : 이벤트 버블링 중단
.once : 한 번만 실행
.capture : 캡처 모드 사용
.self : event.target이 자기 자신일 때만 실행
.passive : 기본 동작을 즉시 실행, 콜백 완료 대기 없음
※ 수식어는 체이닝 가능: @click.prevent.stop="handler"
-->
<div id="root">
<h2>{{siteName}}에 오신 것을 환영합니다</h2>
<a href="https://example.com" @click.prevent="showInfo">링크 이동 방지</a>
<div class="outer" @click="outerClick">
<button @click.stop="innerClick">버블링 방지</button>
</div>
<button @click.once="showInfo">한 번만 실행</button>
</div>
9.3 키보드 이벤트
<!--
주요 키 별칭: enter, delete, esc, space, tab(keydown 전용),
up, down, left, right
시스템 키: ctrl, alt, shift, meta (keyup 시 특수 동작)
사용자 정의 키: Vue.config.keyCodes.키명 = 키코드
-->
<div id="root">
<h2>{{siteName}}에 오신 것을 환영합니다</h2>
<input type="text" @keyup.enter="handleEnter" placeholder="Enter 키 입력">
</div>
<script>
new Vue({
el: '#root',
data() { return { siteName: 'MySite' }; },
methods: {
handleEnter(e) {
console.log('입력값:', e.target.value);
}
}
});
</script>
10. 계산된 속성 (Computed)
<!--
계산된 속성: 기존 데이터로 새 속성을 생성
원리: Object.defineProperty()의 getter/setter 활용
get 실행 시점:
1) 최초 접근 시
2) 의존 데이터가 변경될 때
장점: 메서드 대비 캐싱으로 효율적
※ 수정이 필요하면 setter 구현 필요
-->
<div id="root">
이름: <input type="text" v-model="firstName"><br>
성: <input type="text" v-model="lastName"><br>
전체 이름: <span>{{fullName}}</span>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
firstName: '길동',
lastName: '홍'
},
computed: {
fullName: {
get() {
return this.firstName + ' ' + this.lastName;
},
set(value) {
const parts = value.split(' ');
this.firstName = parts[0];
this.lastName = parts[1] || '';
}
}
}
});
</script>
10.1 계산된 속성 축약형
// 읽기 전용일 때 getter만 정의
computed: {
fullName() {
return this.firstName + ' ' + this.lastName;
}
}
11. 감시 속성 (Watch)
<!--
감시 속성: 데이터 변경을 감지하고 콜백 실행
감시 대상은 반드시 data에 존재해야 함
선언 방식:
1) new Vue 시 watch 옵션 사용
2) vm.$watch() 메서드 사용
-->
<div id="root">
<h2>오늘 날씨는 {{weatherInfo}}</h2>
<button @click="toggleWeather">날씨 전환</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: { isHot: true },
computed: {
weatherInfo() {
return this.isHot ? '덥습니다' : '시원합니다';
}
},
methods: {
toggleWeather() {
this.isHot = !this.isHot;
}
}
// watch 옵션 방식
// watch: {
// isHot: {
// immediate: true,
// handler(newVal, oldVal) {
// console.log('isHot 변경됨:', newVal, oldVal);
// }
// }
// }
});
// $watch 메서드 방식
vm.$watch('isHot', {
immediate: true,
handler(newVal, oldVal) {
console.log('isHot 변경됨:', newVal, oldVal);
}
});
</script>
11.1 깊은 감시
<!--
기본적으로 watch는 객체의 내부 변경을 감지하지 않음
deep: true 옵션으로 객체의 모든 중첩 수준 감시 가능
Vue 자체는 내부 변경을 감지하지만, watch는 기본값이 얕은 감시
-->
<div id="root">
<h2>오늘 날씨는 {{weatherInfo}}</h2>
<button @click="toggleWeather">날씨 전환</button>
<h3>a 값: {{numbers.a}}</h3>
<button @click="numbers.a++">a 증가</button>
</div>
<script>
new Vue({
el: '#root',
data: {
isHot: true,
numbers: { a: 1, b: 2 }
},
watch: {
isHot: {
handler(newVal, oldVal) {
console.log('isHot 변경:', newVal, oldVal);
}
},
numbers: {
deep: true,
handler() {
console.log('numbers 객체 변경됨');
}
}
}
});
</script>
11.2 감시 속성 축약형
// deep, immediate가 필요 없을 때
watch: {
isHot(newVal, oldVal) {
console.log('isHot 변경:', newVal, oldVal);
}
}
// $watch 축약형
vm.$watch('isHot', function(newVal, oldVal) {
console.log('isHot 변경:', newVal, oldVal);
});
12. Watch와 Computed 비교
/*
- computed는 watch로 구현 가능하지만, watch는 computed로 불가능한 작업이 있음
- watch는 비동기 작업에 적합
원칙:
1. Vue가 관리하는 함수는 일반 함수로 작성 (this가 Vue 인스턴스)
2. Vue가 관리하지 않는 함수(타이머, Ajax, Promise 등)는 화살표 함수로 작성
(this가 Vue 인스턴스를 유지하도록)
*/
13. 스타일 바인딩
<!--
class 바인딩:
- 문자열: 클래스명을 동적으로 결정
- 객체: 여러 클래스를 조건부로 적용
- 배열: 여러 개의 동적 클래스
style 바인딩:
- 객체: { fontSize: '40px' } 형태
- 배열: 여러 스타일 객체를 결합
-->
<div id="root">
<div class="basic" :class="currentClass" @click="changeClass">{{title}}</div>
<div class="basic" :class="classArray">배열 스타일</div>
<div class="basic" :class="classObject">객체 스타일</div>
<div class="basic" :style="styleObject">인라인 스타일</div>
</div>
<script>
new Vue({
el: '#root',
data: {
title: '테스트',
currentClass: 'normal',
classArray: ['happy', 'sad', 'normal'],
classObject: { active: false, highlight: true },
styleObject: { fontSize: '40px', color: 'blue' }
},
methods: {
changeClass() {
const options = ['happy', 'sad', 'normal'];
this.currentClass = options[Math.floor(Math.random() * 3)];
}
}
});
</script>
14. 조건부 렌더링
<!--
v-if: 조건부로 DOM 요소를 생성/제거 (전환 빈도 낮을 때 사용)
v-show: CSS display로 숨김/표시 (전환 빈도 높을 때 사용)
v-if는 v-else-if, v-else와 함께 사용 가능 (구조 유지 필수)
template 태그로 여러 요소를 그룹화 가능 (v-if만 지원, v-show는 미지원)
-->
<div id="root">
<h2>현재 n 값: {{n}}</h2>
<button @click="n++">n 증가</button>
<!-- v-show 예제 -->
<h2 v-show="n % 2 === 0">n은 짝수입니다</h2>
<!-- v-if 체인 예제 -->
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div>
<div v-else>기타</div>
<!-- template과 v-if 조합 -->
<template v-if="n === 1">
<p>첫 번째 항목</p>
<p>두 번째 항목</p>
</template>
</div>
15. 리스트 렌더링
<!--
v-for: 배열, 객체, 문자열, 숫자 범위를 반복 렌더링
문법: v-for="(item, index) in items" :key="uniqueId"
key 속성은 Vue의 DOM 재사용 알고리즘에 중요
-->
<div id="root">
<h2>사용자 목록</h2>
<ul>
<li v-for="user in userList" :key="user.id">
{{user.name}} - {{user.age}}
</li>
</ul>
<h2>차량 정보 (객체 반복)</h2>
<ul>
<li v-for="(val, key) in car" :key="key">
{{key}}: {{val}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
userList: [
{ id: '001', name: '김철수', age: 28 },
{ id: '002', name: '이영희', age: 32 },
{ id: '003', name: '박민수', age: 25 }
],
car: {
brand: '현대',
model: '소나타',
year: '2023'
}
}
});
</script>
15.1 key 속성의 중요성
/*
key는 가상 DOM의 식별자 역할
변경 감지 시 신규/기존 가상 DOM 비교에 사용
비교 규칙:
- 동일 key 발견 시: 내용이 같으면 기존 DOM 재사용, 다르면 새 DOM 생성
- 새 key만 발견 시: 새 DOM 생성 후 추가
index를 key로 사용할 때 문제:
- 역순 추가/삭제 등 순서 변경 시 불필요한 DOM 갱신 발생
- 입력 필드가 포함된 경우 잘못된 DOM 갱신 가능
권장 사항:
- 고유 식별자(id, 전화번호 등)를 key로 사용
- 단순 표시용 리스트이고 순서 변경이 없으면 index도 허용
*/
16. 리스트 필터링
<div id="root">
<h2>사용자 목록</h2>
<input type="text" v-model="keyword" placeholder="이름 검색">
<ul>
<li v-for="user in filteredUsers" :key="user.id">
{{user.name}} - {{user.age}} - {{user.gender}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
keyword: '',
userList: [
{ id: '001', name: '김철수', age: 28, gender: '남' },
{ id: '002', name: '이영희', age: 32, gender: '여' },
{ id: '003', name: '박민수', age: 25, gender: '남' }
]
},
computed: {
filteredUsers() {
return this.userList.filter(user =>
user.name.includes(this.keyword)
);
}
}
});
</script>
17. Vue의 데이터 변경 감지 원리
<!--
1. Vue는 data의 모든 계층을 감시
2. 객체 감시: setter를 통해 이루어지며, new Vue 시 전달된 데이터만 감시
- 이후 추가된 속성은 반응형이 아니므로 Vue.set() 또는 vm.$set() 사용
3. 배열 감시: 배열 메서드를 래핑(push, pop, shift, unshift, splice, sort, reverse)
- 인덱스 직접 변경은 감지되지 않음 → Vue.set() 또는 splice() 사용
4. Vue.set()과 vm.$set()은 vm이나 data의 루트 속성에는 사용 불가
-->
<div id="root">
<h1>학생 정보</h1>
<button @click="student.age++">나이 증가</button>
<button @click="addGender">성별 추가</button>
<button @click="addHobby">취미 추가</button>
<button @click="updateFirstHobby">첫 번째 취미 변경</button>
<h3>{{student.name}} - {{student.age}} - {{student.gender}}</h3>
<h3>취미</h3>
<ul>
<li v-for="(hobby, idx) in student.hobbies" :key="idx">{{hobby}}</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
student: {
name: 'Tom',
age: 20,
hobbies: ['독서', '음악', '운동']
}
},
methods: {
addGender() {
this.$set(this.student, 'gender', '남성');
},
addHobby() {
this.student.hobbies.push('코딩');
},
updateFirstHobby() {
Vue.set(this.student.hobbies, 0, '여행');
}
}
});
</script>
18. 폼 데이터 수집
<!--
input[type="text"]: v-model은 value 수집
input[type="radio"]: v-model은 value 수집, value 속성 필수
input[type="checkbox"]:
- value 미설정: checked (boolean)
- value 설정 & v-model 초기값이 배열: value 배열
- value 설정 & v-model 초기값이 비배열: checked (boolean)
v-model 수식어:
.lazy: change 이벤트 후 동기화
.number: 숫자로 변환
.trim: 앞뒤 공백 제거
-->
<div id="root">
<form @submit.prevent="submitForm">
계정: <input type="text" v-model.trim="form.account"><br>
비밀번호: <input type="password" v-model="form.password"><br>
나이: <input type="number" v-model.number="form.age"><br>
성별:
남 <input type="radio" value="male" v-model="form.gender">
여 <input type="radio" value="female" v-model="form.gender"><br>
취미:
독서 <input type="checkbox" value="reading" v-model="form.hobbies">
게임 <input type="checkbox" value="gaming" v-model="form.hobbies">
운동 <input type="checkbox" value="sports" v-model="form.hobbies"><br>
도시:
<select v-model="form.city">
<option value="">선택하세요</option>
<option value="seoul">서울</option>
<option value="busan">부산</option>
</select><br>
기타: <textarea v-model.lazy="form.notes"></textarea><br>
<input type="checkbox" v-model="form.agree"> 동의합니다
<button type="submit">제출</button>
</form>
</div>
<script>
new Vue({
el: '#root',
data: {
form: {
account: '',
password: '',
age: null,
gender: '',
hobbies: [],
city: '',
notes: '',
agree: false
}
},
methods: {
submitForm() {
console.log(JSON.stringify(this.form));
alert('제출 완료!');
}
}
});
</script>
19. 내장 디렉티브
<!--
v-text: 텍스트 내용 렌더링 (기존 내용 대체)
v-html: HTML 구조 렌더링 (XSS 위험 주의!)
v-cloak: Vue 인스턴스 생성 전까지 숨김 (네트워크 지연 시 {{}} 노출 방지)
v-once: 최초 렌더링 후 캐싱 (데이터 변경 무시)
v-pre: 컴파일 생략 (순수 HTML로 처리)
-->
<div id="root">
<div v-text="message"></div>
<div v-html="htmlContent"></div>
<div v-once>초기값: {{counter}}</div>
<div v-pre>{{ 이 부분은 컴파일되지 않음 }}</div>
</div>
<script>
new Vue({
el: '#root',
data: {
message: '텍스트 예시',
htmlContent: '<strong>굵은 텍스트</strong>',
counter: 100
}
});
</script>
20. 사용자 정의 디렉티브
<!--
선언 방식:
- 지역: directives 옵션 사용
- 전역: Vue.directive() 사용
콜백:
- bind: 디렉티브와 요소가 바인딩될 때
- inserted: 요소가 DOM에 삽입될 때
- update: 템플릿이 재컴파일될 때
참고: 디렉티브명은 kebab-case 사용, 사용 시 v- 접두사 추가
-->
<div id="root">
<h2>현재 n 값: <span v-text="n"></span></h2>
<h2>10배 값: <span v-big="n"></span></h2>
<button @click="n++">증가</button>
<hr>
<input type="text" v-focus:value="n">
</div>
<script>
// 전역 디렉티브
Vue.directive('focus', {
bind(el, binding) {
el.value = binding.value;
},
inserted(el) {
el.focus();
},
update(el, binding) {
el.value = binding.value;
}
});
new Vue({
el: '#root',
data: { n: 1 },
directives: {
// 함수형 축약
big(el, binding) {
el.innerText = binding.value * 10;
},
// 객체형
focus: {
bind(el, binding) {
el.value = binding.value;
},
inserted(el) {
el.focus();
},
update(el, binding) {
el.value = binding.value;
}
}
}
});
</script>
21. 생명 주기 (Lifecycle)
<!--
생명 주기 훅: Vue가 특정 시점에 호출하는 함수
훅 이름은 고정, 내용은 개발자 정의
this는 Vue 인스턴스 또는 컴포넌트 인스턴스
-->
<div id="root">
<h2 :style="{ opacity: opacityLevel }">Vue 학습 중...</h2>
</div>
<script>
new Vue({
el: '#root',
data: { opacityLevel: 1 },
mounted() {
this.timer = setInterval(() => {
this.opacityLevel -= 0.01;
if (this.opacityLevel <= 0) this.opacityLevel = 1;
}, 16);
},
beforeDestroy() {
clearInterval(this.timer);
}
});
</script>
21.1 주요 생명 주기 훅 활용
<!--
mounted: Ajax 요청, 타이머 시작, 이벤트 바인딩 등 초기화 작업
beforeDestroy: 타이머 제거, 이벤트 해제 등 정리 작업
vm.$destroy() 호출 시:
- Vue DevTools에서 정보 사라짐
- 사용자 정의 이벤트는 사라지지만, 네이티브 DOM 이벤트는 유지
- destroy 후 데이터 변경해도 템플릿 갱신되지 않음
-->
<div id="root">
<h2 :style="{ opacity: opacityLevel }">Vue 학습 중...</h2>
<button @click="stopAnimation">애니메이션 중지</button>
</div>
<script>
new Vue({
el: '#root',
data: { opacityLevel: 1 },
methods: {
stopAnimation() {
this.$destroy();
}
},
mounted() {
this.timer = setInterval(() => {
this.opacityLevel -= 0.01;
if (this.opacityLevel <= 0) this.opacityLevel = 1;
}, 16);
},
beforeDestroy() {
clearInterval(this.timer);
}
});
</script>
22. 컴포넌트 정의
// 컴포넌트: 애플리케이션의 기능 단위를 코드와 리소스로 묶은 집합
23. 비단일 파일 컴포넌트
<!--
Vue.extend(options)로 컴포넌트 생성
new Vue(options)와 유사하나 차이점:
- el 속성 불필요 (vm이 관리)
- data는 반드시 함수 (재사용 시 데이터 격리)
등록 방식:
- 지역: components 옵션
- 전역: Vue.component('name', component)
사용: <component-name></component-name>
-->
<div id="root">
<my-header></my-header>
<user-profile></user-profile>
</div>
<div id="root2">
<my-header></my-header>
</div>
<script>
// 컴포넌트 생성
const headerComponent = Vue.extend({
template: `<div><h2>헤더 컴포넌트: {{title}}</h2></div>`,
data() { return { title: '메인 헤더' }; }
});
const profileComponent = Vue.extend({
template: `<div><h3>사용자: {{name}}, 나이: {{age}}</h3></div>`,
data() { return { name: '김철수', age: 30 }; }
});
// 전역 등록
Vue.component('my-header', headerComponent);
new Vue({
el: '#root',
components: { 'user-profile': profileComponent }
});
new Vue({ el: '#root2' });
</script>
23.1 주의사항
/*
컴포넌트명:
- 단일 단어: school / School (권장: PascalCase)
- 여러 단어: my-school / MySchool (kebab-case 권장)
- HTML 예약어 회피 (h2, button 등)
컴포넌트 태그:
- <school></school> (닫는 태그 필수, 단일 태그는 비권장)
축약형: const comp = Vue.extend(options) → const comp = options
*/
23.2 VueComponent 생성자
/*
1. Vue.extend()는 VueComponent 생성자 반환
2. <school></school> 작성 시 Vue가 내부적으로 new VueComponent() 실행
3. Vue.extend() 호출마다 새로운 VueComponent 생성자 생성
4. this 바인딩:
- 컴포넌트 내 data, methods, watch, computed → VueComponent 인스턴스
- new Vue() 내 → Vue 인스턴스
VueComponent 인스턴스 = vc (컴포넌트 인스턴스)
Vue 인스턴스 = vm
*/
23.3 중요 상속 관계
/*
VueComponent.prototype.__proto__ === Vue.prototype
→ vc가 Vue.prototype의 속성/메서드에 접근 가능
*/
24. Render 함수
/*
Vue 버전 차이:
- vue.js: 코어 + 템플릿 컴파일러 포함 (완전판)
- vue.runtime.js: 코어만 포함 (런타임판)
런타임 버전은 템플릿 컴파일러가 없으므로, template 대신 render 함수 사용
render(createElement) {
return createElement('h1', '안녕하세요!');
}
축약형: render: h => h('h1', '안녕하세요!')
*/
25. vue.config.js 설정
/*
vue inspect > output.js: 기본 설정 확인
vue.config.js: 프로젝트 설정 커스터마이징
상세: https://cli.vuejs.org/zh
*/
26. ref 속성
/*
ref: 요소/컴포넌트 참조 (id의 Vue 버전)
- HTML 요소: 실제 DOM 요소 반환
- 컴포넌트: 컴포넌트 인스턴스(vc) 반환
접근: this.$refs.xxx
동적 ref: :ref="variable" → this.$refs[variable]
*/
<button ref="myBtn" @click="focusInput">포커스</button>
<input ref="myInput">
methods: {
focusInput() {
this.$refs.myInput.focus();
}
}
27. props 옵션
<!--
props: 부모 컴포넌트가 자식에게 데이터 전달
전달: <Child :name="value" :age="number"></Child>
선언 방식:
1. 배열: props: ['name', 'age']
2. 객체(타입 제한): props: { name: String, age: Number }
3. 객체(상세 설정):
props: {
name: { type: String, required: true },
age: { type: Number, default: 20 },
email: { type: String, default: '' }
}
중요: props는 읽기 전용
수정이 필요한 경우 data에 복사 후 변경
-->
<Student name="이영희" sex="여성" :age="25"></Student>
// 컴포넌트 내 props 선언
props: {
name: {
type: String,
required: true
},
age: {
type: Number,
default: 20
},
sex: {
type: String,
default: '남성'
}
}
28. mixin (혼합)
/*
mixin: 여러 컴포넌트의 공통 옵션을 추출하여 재사용
정의:
{
data() { ... },
methods: { ... },
created() { ... }
}
사용:
- 전역: Vue.mixin(mixinObject)
- 지역: mixins: [mixinObject]
*/
// mixin.js
export const commonMixin = {
methods: {
greet() {
console.log('안녕하세요, ' + this.name + '님!');
}
},
created() {
console.log('컴포넌트 생성됨!');
}
};
// 컴포넌트에서 사용
import { commonMixin } from './mixins';
export default {
mixins: [commonMixin],
data() {
return { name: '홍길동' };
}
};
29. 플러그인
/*
플러그인: Vue에 기능을 추가하는 도구
install 메서드를 가진 객체
정의:
const myPlugin = {
install(Vue, options) {
// 전역 필터
Vue.filter('capitalize', value => value.toUpperCase());
// 전역 디렉티브
Vue.directive('highlight', { bind(el) { el.style.color = 'red'; } });
// 전역 mixin
Vue.mixin({ created() { console.log('플러그인 mixin'); } });
// 인스턴스 메서드
Vue.prototype.$myMethod = function() { console.log('커스텀 메서드'); };
}
};
사용: Vue.use(myPlugin);
*/