🔷 computed()
🔷 computed 기본 예시
const todos = ref([
{text: 'Vue 공부'},
{text: 'Vue 실습'},
{text: 'Spring 복습'},
]);
이런 데이터가 있을 때
<p>{{todos.length > 0 ? '아직 남았다' : '퇴근!'}}</p>
콧수염 구문으로 템플릿에 쓸 수도 있지만
const restOfTodos = computed(() => {
return todos.value.length > 0 ? '아직 많이 남았다' : '퇴근';
});
이걸 템플릿에 리턴으로 받으면 템플릿이 더 단순해진다.
🔷 computed 특징
const restOfTodos = computed(() => {
return todos.value.length > 0 ? '아직 남았다' : '퇴근!'
})
.value
로 참조 할 수 있음 (템플릿에서는 .value
생략가능)🔷 computed 속성 대신 method로도 동일한 기능을 정의할 수 있다.
const getRestOfTodos = function () {
return todos.value.length > 0 ? '아직 남았다' : '퇴근!'
}
💡 두 가지 접근 방식은 실제로 완전히 동일하다.
🔷 computed와 method 차이
🔷 Cache (캐시)
💡 웹 페이지의 캐시 데이터
페이지 일부 데이터를 브라우저 캐시에 저장 후 같은 페이지에 다시 요청 시 모든 데이터를 다시 응답 받는 것이 아니라 캐시 된 데이터를 사용하여 더 빠르게 웹 페이지를 렌더링한다.
🔷 computed와 method의 적절한 사용처
computed
method
💡 computed는 의존된 데이터가 변경되면 자동으로 업데이트 되고 method는 호출해야만 실행됨을 기억하자.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<h2>남은 할일</h2>
<p>{{todos.length > 0 ? '아직 남았다' : '퇴근!'}}</p>
<hr>
<h2>계산된 속성</h2>
<p>{{restOfTodos}}</p>
<hr>
<h2>메서드 호출</h2>
<p>{{getRestOfTodos()}}</p>
</div>
<script>
const { createApp, ref, computed } = Vue;
const app = createApp({
setup() {
const todos = ref([
{text: 'Vue 공부'},
{text: 'Vue 실습'},
{text: 'Spring 복습'},
]);
const restOfTodos = computed(() => {
console.log("computed")
return todos.value.length > 0 ? '아직 많이 남았다' : '퇴근해 이제...';
});
const getRestOfTodos = () => {
console.log("method")
return todos.value.length > 0 ? '넌 퇴근 못한다니까' : '퇴근하라고.......';
};
return {
todos,
restOfTodos,
getRestOfTodos,
};
},
});
app.mount("#app");
</script>
</body>
</html>
배열 내 모든 todo가 삭제되면?
❗computed의 반환 값은 변경하지 말 것❗
▪ computed의 반환 값은 의존하는 데이터의 파생된 값
▪ 일종의 snapshot 이며 의존하는 데이터가 변경될 때 마다 새 snapshot이 생성된다.
▪ snapshot 을 변경하는 것은 의미가 없으므로 계산된 반환 값은 읽기 전용으로 취급되어야 하며 변경되어서는 안됨...
▪ 대신 새 값을 얻기 위해서는 의존하는 데이터를 업데이트 해야 한다.
❗computed 사용 시 원본 배열 변경하지 말 것❗
▪ computed 에서 reverse() 및 sort() 사용 시 원본 배열을 변경하기 때문에 복사본을 만들어서 진행 해야 함
🔷 v-if
🔷 예시
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<p v-if="isSeen">true일 때 보임</p>
<p v-else>false일 때 보임</p>
<button @click="isSeen = !isSeen">변환</button>
<hr>
<div v-if="name === 'Alice'">Alice 입니다.</div>
<div v-else-if="name === 'Bella'">Bella 입니다.</div>
<div v-else-if="name === 'Bob'">Bob 입니다.</div>
<div v-else-if="name === 'Bzeromo'">Bzeromo 입니다.</div>
<div v-else>아무도 아닙니다.</div>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const isSeen = ref(true);
const name = ref('Bzeromo');
return {
isSeen,
name,
};
},
});
app.mount("#app");
</script>
</body>
</html>
변환에 따라 true/false로 바뀐다. 정확히 말하면 두 div가 번갈아가며 렌더링된다.
그리고 조건에 따라 렌더링될 div가 결정된다.
🔷 여러 요소에 대한 v-if 적용
<template v-if="name === 'Cathy'">
<div>Cathy입니다</div>
<div>나이는 30살입니다</div>
</template>
🔷 HTML <template> element
💡 실제로 template 태그는 렌더링 후 확인해보면 보이지 않는다.
🔷 v-show
<div v-show="isSeen">이게 보여?</div>
🔷 v-if vs v-show
💡 무언가를 매우 자주 전환해야 하는 경우에는 v-show를,
실행 중에 조건이 변경되지 않는 경우에는 v-if를 권장한다.
🔷 v-for
alias
)을 제공<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<div v-for="p in myArr">
{{p.name}} / {{p.age}}
</div>
<hr>
<!-- p, index 순서가 매우 중요! -->
<div v-for="(p, index) in myArr">
{{index}} : {{p.name}} / {{p.age}}
</div>
<hr>
<div v-for="value in myObj">
{{value}}
</div>
<hr>
<div v-for="(value, key) in myObj">
{{key}} : {{value}}
</div>
<hr>
<!-- 객체에서 값, 키, 인덱스 순 -->
<div v-for="(value, key, index) in myObj">
{{index}} - {{key}} : {{value}}
</div>
<hr>
<!-- 템플릿으로 여러 요소 for 적용 -->
<ul>
<template v-for="item in myArr">
<li>{{item.name}}</li>
<li>{{item.age}}</li>
</template>
</ul>
<hr>
<!-- 중첩된 for문 -->
<ul v-for="item in myInfo">
<li v-for="friend in item.friends">
{{item.name}}는 {{friend}}와 친구라고 생각합니다.
</li>
</ul>
<hr>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const myArr = ref([
{ name: "Alice", age: 20 },
{ name: "Bella", age: 21 },
]);
const myObj = ref({
name: "Cathy",
age: 30,
});
// nested v-for
const myInfo = ref([
{ name: "Alice", age: 20, friends: ["Bella", "Cathy", "Dan"] },
{ name: "Bella", age: 21, friends: ["Alice", "Cathy"] },
]);
return {
myArr,
myObj,
myInfo,
};
},
});
app.mount("#app");
</script>
</body>
</html>
🔷 v-for with key
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- 순서를 일관되게 유지(객체에 유니크함을 부여)하기 위해 id를 key에 바인딩하여 사용 (정석) -->
<!-- 인덱스는 데이터를 넣고 뺌에 따라 변할 수 있기 때문에 키로 사용할 수 없다! -->
<div v-for="item in items" :key="item.id">
{{item.name}}
</div>
</div>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
let id = 0;
const items = ref([
{ id: id++, name: "Alice" },
{ id: id++, name: "Bella" },
]);
return {
items,
};
},
});
app.mount("#app");
</script>
</body>
</html>
🔷 v-for with v-if
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<h3>완료된 일</h3>
<ul>
<li v-for="todo in completeTodos" :key="todo.id">
{{todo.name}}
</li>
</ul>
<hr>
<h3>미완료된 일</h3>
<ul>
<!-- 2. template을 통해 for와 if의 순서를 바꿔 우선순위 영향 안받게 하기 -->
<template v-for="todo in todos" :key="todo.id">
<li v-if="!todo.isComplete">
{{todo.name}}
</li>
</template>
</ul>
</div>
<script>
const { createApp, ref, computed } = Vue;
const app = createApp({
setup() {
let id = 0;
const todos = ref([
{ id: id++, name: "복습", isComplete: true },
{ id: id++, name: "예습", isComplete: false },
{ id: id++, name: "저녁식사", isComplete: true },
{ id: id++, name: "노래방", isComplete: false },
]);
//1. computed를 통해 미리 필터링 후 보내기
const completeTodos = computed(() => {
return todos.value.filter((todo) => {
return todo.isComplete;
})
})
return {
todos,
completeTodos,
};
},
});
app.mount("#app");
</script>
</body>
</html>
❗ Vue Style Guide에 따라 필수(Essential)적이다.
🔷 watch()
variable
: 감시하는 변수newValue
: 감시하는 변수가 변화된 값, 콜백 함수의 첫번째 인자oldValue
: 콜백 함수의 두번째 인자<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!--1. 감시하는 변수에 변화가 생겼을 때 기본 동작 확인하기-->
<p>{{count}}</p>
<button @click="count++">증가</button>
<hr>
<!--2. 감시하는 변수에 변화가 생겼을 때 연관 데이터 업데이트 하기
-->
<input type="text" v-model="message"/>
<p>{{messageLength}}</p>
</div>
<script>
const { createApp, ref, watch } = Vue;
const app = createApp({
setup() {
const count = ref(0);
const message = ref('');
const messageLength = ref(0);
//첫번째 인자: 감시할 대상, 두번째 인자: 콜백함수(변화한 값, 변화 전 값)
const countWatch = watch(count, (newValue, oldValue) => {
console.log(`new : ${newValue} / old: ${oldValue}`);
});
const messageWatch = watch(message, (newValue, oldValue) => {
messageLength.value = newValue.length;
})
//watch는 반환하지 않아도 괜찮다
return {
count,
message,
messageLength,
};
},
});
app.mount("#app");
</script>
</body>
</html>
🔷 computed와 watchers는 데이터의 변화를 감지하고 처리한다는 공통점이 있지만 다음과 같은 차이를 보인다.
Computed | Watchers | |
---|---|---|
동작 | 의존하는 데이터 속성의 계산된 값을 반환 | 특정 데이터 속성의 변화를 감시하고 작업을 수행 |
사용 목적 | 템플릿 내에서 사용되는 데이터 연산용 | 데이터 변경에 따른 특정 작업 처리용 |
사용 예시 | 연산 된 길이, 필터링 된 목록 계산 등 | 비동기 API 요청, 연관 데이터 업데이트 등 |
❗ computed와 watch 모두 의존(감시)하는 원본 데이터를 직접 변경하지 않는다!
🔷 Vue 인스턴스의 생애주기 동안 특정 시점에 실행되는 함수
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Vue</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<button @click="count++">증가</button>
<p>{{count}}</p>
<p>{{message}}</p>
</div>
<script>
const { createApp, ref, onMounted, onUpdated } = Vue;
const app = createApp({
setup() {
const count = ref(0);
const message = ref(null);
//이미지 API 요청 메서드 생성 후
//1. Vue 컴포넌트 인스턴스가 초기 렌더링 및 DOM 요소 생성이 완료된 후 특정 로직을 수행하기
onMounted(() => {
//이미지 호출 API 메서드 호출
console.log("연결 완료")
});
//▪ 2. 반응형 데이터의 변경으로 인해 컴포넌트의 DOM이 업데이트된 후 특정 로직 수행하기
onUpdated(() => {
message.value = "update";
console.log("update");
});
return {
count,
message,
};
},
});
app.mount("#app");
</script>
</body>
</html>
Vue는 Lifecycle Hooks에 등록된 콜백 함수들을 인스턴스와 자동으로 연결함
인스턴스 생애 주기의 여러 단계에서 호출되는 다른 Hooks도 있으며, 가장 일반적으로 사용되는 것은 onMounted
, onUpdated
, onUnmounted
어후... 외울게 많다...