ch5. 양방향 데이터 바인딩과 기타 디렉티브
v-model: 템플릿 변수(예 - ref)와 컨트롤 값(input의 value) 간 양방향 데이터 바인딩
- v-bind:value와 v-input을 조합하여 v-model로 간단히 표현
v-model 수식어
- lazy: input 대신 change 이벤트로 양방향 데이터 바인딩 수정
- number: 입력값을 숫자로 취급
- trim: 입력값의 전후 공백 제거
Input 이벤트와 Change 이벤트 비교
- onInput: input 마다 이벤트 발생 -> v-model
- onChange: input이 blur 되면 이벤트 발생 -> v-model.lazy
- 예시
<template>
<div>
<div>
<h2>v-model</h2>
<input v-model="textOnInput" type="text" />
<p>입력 문자열: {{ textOnInput }}</p>
</div>
<div>
<h2>v-model.lazy</h2>
<input v-model.lazy="textOnChange" type="text" />
<p>입력 문자열: {{ textOnChange }}</p>
</div>
<div>
<h2>v-mode.trim</h2>
<input v-model.trim="trimmedText" type="text" />
<p>입력 문자열: {{ trimmedText }}</p>
</div>
</div>
</template>
<script setup lang="ts">
const textOnInput = ref("");
const textOnChange = ref("");
const trimmedText = ref("");
</script>
v-html: html 문자열을 그대로 표시
- html 문자열을 element 컨텐츠로 넣음
- XSS 공격에 취약하여 사용하지 않는 것이 좋음
- 예시
<template>
<div>
<div>{{ htmlStr }}</div>
<div v-html="htmlStr"></div>
</div>
</template>
<script setup lang="ts">
const htmlStr = ref(`<a href="https://www.naver.com/">네이버<a/>`);
</script>
v-once: 데이터 바인딩을 한 번만 수행
- 상태값 변화에 따라 UI를 바꾸지 않음
v-cloak: 렌더링이 끝날 때까지 머스태시 구문 숨김
- Nuxt의 <ClientOnly>와 비슷한듯
ch6. 제어 디렉티브
조건 분기 사용 시 <template> 태그를 사용하기(<div> 를 쓰고 싶지 않을 때)
- React의 <fragment> 에 key 넣는 것처럼 불필요한 div를 피할 수 있음
<template>
<div>
<p>
점수는 {{ randomNumber }}점
<template v-if="randomNumber > 80">
으로 매우 우수합니다
<span style="color: red">대단해요!</span>
</template>
</p>
</div>
</template>
<script setup lang="ts">
const randomNumber = computed(() => Math.round(Math.random() * 100));
</script>
v-show: 렌더링은 하되, display: none 효과 부여
- 특정 컴포넌트를 빈번하게 노출 전환할 때 사용하면 좋다(렌더링 자체는 되기 때문에 표시 전환 비용이 낮음)
- <template> 태그에는 v-show 사용 불가
<template>
<div>
<div>
v-show 사용
<p v-show="showOrNot">조건이 부합하여 표시</p>
</div>
<div>
v-if 사용
<p v-if="showOrNot">조건이 부합하여 표시</p>
</div>
</div>
</template>
<script setup lang="ts">
const randomNumber = computed(() => Math.round(Math.random() * 100));
const showOrNot = computed(() => randomNumber.value >= 80);
</script>
v-for 객체 순회
- 배열 뿐만 아니라 객체 리터럴도 v-for 로 순회할 수 있다
- value, key, index 순으로 참조하면 된다
- v-for 사용 시 index는 key에 적합하지 않다(대상 배열이 바뀌면 렌더링 시 바뀔 수 있으므로 완전히 고유하지 않음)
- 예시1
<template>
<div>
<ul>
<li v-for="(val, key, idx) in cocktailRef" :key="val">
{{ idx + 1 }}: ID가 {{ key }}인 칵테일은 {{ val }}
</li>
</ul>
</div>
</template>
<script setup lang="ts">
const cocktailRef = ref({
2345: "화이트 레이디",
4412: "블루 하와이",
6792: "뉴욕",
});
</script>
- 예시2
< dl > : description list -> 주로 용어 정의를 위해 사용 e.g. 사전
< dt >: description term
< dd >: description detail
<template>
<dl>
<template v-for="(val, key, idx) in cocktailRef" :key="key">
<dt>{{ idx + 1 }}. {{ key }}</dt>
<dd>{{ val }}</dd>
</template>
</dl>
</template>
<script setup lang="ts">
const cocktailRef = ref({
id: 2345,
name: "White Lady",
price: 1200,
recipe: "진 30ml + 쿠앵트로 15ml + 레몬과즙 15ml",
});
</script>
v-for Map 순회
- Map 객체를 v-for 로 순회할 수 있다
- v-for 내에서 key, value 순으로 참조한다
<template>
<div>
<ul>
<li v-for="[id, cocktailName] in cocktailListRef" :key="id">
ID가 {{ id }}인 칵테일은 {{ cocktailName }}
</li>
</ul>
</div>
</template>
<script setup lang="ts">
const cocktailList = new Map<number, string>();
cocktailList.set(2345, "White Lady");
cocktailList.set(4412, "Blue hawaii");
cocktailList.set(6792, "New York");
console.log(cocktailList);
const cocktailListRef = ref(cocktailList);
</script>
카운터 변수를 활용한 v-for 순회
- 카운터 변수는 인덱스와 다르게 1부터 시작하여 n 번을 반복한다
<template>
<ul>
<li v-for="r in 5" :key="r">
반지름이 {{ r }} 인 원의 둘레 길이: {{ 2 * r * Math.PI }}
</li>
</ul>
</template>
ch7. 스크립트 블록 활용
감시자(watcher)
- watchEffect
- watch
watchEffect: 반응형 변수의 변화를 모니터링
- 콜백함수 내부의 모든 반응형 변수를 감지하여, 반응형 변수의 값이 변경되면 콜백함수 실행
- 최초에 1번 콜백 함수가 실행됨
watch: 감시 대상을 명시
- 값의 변화를 감지할 반응형 변수를 콜백함수 앞에 명시
- 기본값은 최초 1번 콜백 함수를 실행하지 않음, { immediate: true } 옵션을 주면 watchEffect처럼 즉시 실행시킬 수 있음
- 변경 전후의 값을 알 수 있음
라이프사이클 훅
<template>
<div>
<div>세로 {{ heightRef }}, 가로 {{ widthRef }}인 사각형의 면적은 {{ area }}</div>
<button @click="change">값을 변경</button>
</div>
</template>
<script setup lang="ts">
import type { DebuggerEvent } from "vue";
const heightRef = ref(Math.ceil(Math.random() * 10));
const widthRef = ref(Math.ceil(Math.random() * 10));
const area = computed(() => heightRef.value * widthRef.value);
const change = () => {
heightRef.value = Math.ceil(Math.random() * 10);
widthRef.value = Math.ceil(Math.random() * 10);
};
onBeforeMount(() =>
console.log(`beforeMount called: ${heightRef.value} * ${widthRef.value}`),
);
onMounted(() => console.log(`mounted called: ${heightRef.value} * ${widthRef.value}`));
onBeforeUpdate(() =>
console.log(`beforeUpdate called: ${heightRef.value} * ${widthRef.value}`),
);
onUpdated(() => console.log(`updated called: ${heightRef.value} * ${widthRef.value}`));
onRenderTracked((event: DebuggerEvent) => {
console.log(`renderTracked called: ${heightRef.value} * ${widthRef.value}`);
console.log("🚀 ~ onRenderTracked ~ event:", event);
});
onRenderTriggered((event: DebuggerEvent) => {
console.log(`renderTriggered called: ${heightRef.value} * ${widthRef.value}`);
console.log("🚀 ~ onRenderTriggered ~ event:", event);
});
</script>
beforeCreate: Vue 애플리케이션의 시작 직후, 초기화 처리 전
created: Vue 애플리케이션 초기화 처리 후
beforeMount: 컴포넌트의 해석 처리 후 결정한 DOM을 렌더링하기 직전
mounted: DOM 렌더링이 완료되고 표시 상태가 된 시점, 즉, mounted 상태가 된 시점
beforeUpdate: 반응형 데이터가 변경되고 DOM을 리렌더링하기 직전
updated: DOM의 리렌더링이 완료된 시점
beforeUnmount: 컴포넌트의 DOM의 숨김 처리를 시작하기 직전
unmounted: 컴포넌트의 DOM의 숨김 처리가 완료된 시점, 즉 unmounted 상태가 된 시점
errorCaptured: 하위 컴포넌트를 포함하여 에러를 감지한 시점
renderTracked: 반응형 변수에 처음 접근이 발생한 시점
- 각 반응형 변수에 접근될 때마다 콜백이 실행됨 (예 - 반응형 변수 갯수가 n개이면 n번 콜백이 실행됨)
renderTriggered: 반응형 변수가 변경된 것을 감지하고 해당 변수에 접근이 있는 시점
activated: 컴포넌트가 더 이상 대기 상태가 아니게 된 시점
deactivated: 컴포넌트가 대기 상태가 된 시점
DebuggerEvent: Vue에서 디버깅용으로 지원하는 이벤트 객체
- 주요 props
effect: ReactiveEffect, target: RefImpl
2) Vue 애플리케이션이 초기화되면 스크립트 블록 내부의 코드가 실행되고 ref, computed 변수 및 함수 등이 준비됨
3) 템플릿 블록의 코드를 분석하여 DOM 구조 결정
4) DOM 렌더링
5) mounted: 화면에 보이는 상태
6) 리렌더링 끝나면 다시 mounted 상태로 돌아감
<script> 내부의 setup(): Vue3의 Composition API에서 반응형 값을 사용하기 위해 꼭 명시해야 함
- 명시하지 않으면 Options API 방식을 사용하고 defineComponent 메서드 내부의 setup() 내부에 작성해야 함