1️⃣ UI를 컨트롤하는 디렉티브 - UserInterface
1. 선언적 렌더링
- Vue.js 데이터바인딩의 가장 기본
- 데이터 바인딩의 가장 기본적인 형태: 이중 중괄호 {{ }} 문법을 사용한 텍스트 보간법
- 이중 중괄호는 데이터를 HTML이 아닌 일반 텍스트로 해석. 실제 HTML을 출력하려면 v-html 디렉티브를 사용
2. Class와 Style 바인딩
1) HTML Class 바인딩
- HTML 클래스 바인딩을 하기 위해선 v-bind: class (축약형 - :class)를 사용
- 객체로 바인딩 되며 클래스를 동적으로 토글하기 위해 객체를 :class에 전달할 수 있음
<div :class="{ active: isActive }"></div>
- isActive: state 영역 즉, data 영역에 선언한 임의의 변수이며 이 변수는 true와 flase 값을 가짐
2) HTML Style 바인딩
- v-bind 디렉티브를 활용하여 클래스 바인딩과 동일하고 객체로 바인딩함
- :style은 HTML Element의 style 속성에 해당하는 자바스크립트 객체에 대해 바인딩을 지원
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
- 해당 프로퍼티 속성은 카멜케이스로 작성
<template>
<div>{{ rawHtml }}</div>
<div>{{ rawHtml2 }}</div>
<!-- html처럼 사용하기 위해서 -->
<h1 v-html="rawHtml2"></h1>
<h2 v-bind:class="{ active: isActive }">클래스 바인딩 테스트입니다.</h2>
<!-- 축약형 -->
<h2 :class="{ active: isActive }">클래스 바인딩 테스트입니다.</h2>
<button @click="change">버튼</button>
<h3 style="color: red; font-size: 24px">스타일 바인딩 테스트입니다.</h3>
<h3 :style="{ color: fontColor, fontSize: fontSize + 'px' }">
스타일 바인딩 테스트입니다.
</h3>
</template>
<script>
export default {
data() {
return {
rawHtml: "이것은 텍스트입니다.",
rawHtml2: '<span style="color: red">이것은 빨간색이어야 합니다.</span>',
isActive: false,
fontColor: "#888888",
fontSize: 48,
};
},
methods: {
change() {
this.isActive = !this.isActive;
},
},
};
</script>
<style scoped>
h2.active {
color: green;
}
</style>
3. 조건부 렌더링
1) v-if / v-else-if / v-else
- 조건부로 블록을 렌더링하는 데 사용
- 블록은 디렉티브 표현식이 truthy값을 반환하는 경우에만 렌더링 됨
- 전환 비용이 더 높음 -> 실행 중에 조건이 변경되지 않을 경우에 사용
2) v-show
- v-if와 사용법이 크게 다르지 않음
- 다만, v-show가 있는 엘리먼트는 항상 렌더링 되어 DOM에 남아있다는 것이 차이점
- v-show는 엘리먼트의 display CSS 속성만 전환이 됨
- 초기 렌더링 비용이 더 높음 -> 매우 자주 전환해야 하는 경우에 사용
<template>
<div>
<div v-if="isVisible" class="red"></div>
<div v-if="isVisible" class="blue"></div>
<div v-else class="black"></div>
<div v-if="count > 1" class="red"></div>
<div v-else class="blue"></div>
<button @click="count++">증가</button>
<button @click="count--">감소</button>
// v-show는 false여도 다 렌더링 됨
<div v-show="isVisible" class="red"></div>
<div v-show="!isVisible" class="blue"></div>
// v-if는 ture인 것만 렌더링 됨
<div v-if="isVisible" class="black"></div>
</div>
</template>
<script>
export default {
data() {
return {
isVisible: false,
count: 0,
};
},
};
</script>
4. 리스트 렌더링
- 배열 데이터를 기반으로 동일한 구조의 UI를 반복호출하는 기능
- v-for 디렉티브 사용
- 배열을 리스트로 렌더링 할 수 있음
- item in items 형식의 특별한 문법이 필요 (item: 배열 내 반복되는 엘리먼트의 별칭 / items: 선언한 배열 데이터)
- 객체의 속성을 반복하는 데 사용 가능
- 순회순서: 해당 객체를 Object.keys()를 호출한 결과에 기반
<template>
<div>
<li v-for="item in sampleArray" :key="item">{{ item }}</li>
<li v-for="user in otherArray" :key="user.id">
{{ user.id }} / {{ user.name }}
</li>
</div>
</template>
<script>
export default {
data() {
return {
sampleArray: ["a", "b", "c", "d"],
otherArray: [
{ id: 0, name: "John" },
{ id: 1, name: "Kim" },
{ id: 2, name: "Lee" },
{ id: 3, name: "Park" },
],
};
},
};
</script>
<style lang="scss" scoped></style>
2️⃣ Data를 컨트롤하는 디렉티브
1. 이벤트 핸들링
1) 인라인 핸들러
- 이벤트가 트리거 될 때, 실행되는 인라인 JavaScript 기능
- 어떤 기능을 동작하는 코드가 HTML Element 내에 직접 할당되는 것을 의미
2) 메서드 핸들러
- 컴포넌트 script 부분에 정의된 메서드(함수)를 이벤트 핸들러에 할당해주는 방식
<template>
<div>
<button v-on:click="count++">인라인 핸들러</button>
<h1>{{ count }}</h1>
<button v-on:click="changeName">메서드 핸들러</button>
<h1>{{ name }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
name: "Vue.js",
};
},
methods: {
changeName() {
this.name = "변경된 텍스트 데이터입니다.";
},
},
};
</script>
<style lang="scss" scoped></style>
2. Computed
- 정의: 함수처럼 코드를 작성하지만, return 시키는 데이터를 사용하기 때문에 데이터 취급을 하는 공통적으로 사용되는 로직 혹은 복잡한 로직을 미리 처리하여 반복된 로직처리를 방지하는 계산된 형태의 데이터를 만드는 속성
- 사용 이유: 너무 많은 연산을 스크립트 혹은 템플릿 HTML 안에서 처리하면 코드가 비대해지고, 유지보수가 어렵다는 치명적인 단점이 있음
- 연산이 복잡한 형태라면 계산된 데이터 형태로 만드는 computed 속성을 사용해서 해결할 수 있음
<template>
<h1>{{ text }}</h1>
<h1>changeText 함수 호출 값: {{ changeText() }}</h1>
<h1>changeText 함수 호출 값: {{ changeText() }}</h1>
<h1>changeText 함수 호출 값: {{ changeText() }}</h1>
<h2>{{ computedText }}</h2>
<h2>{{ computedText }}</h2>
<h2>{{ computedText }}</h2>
</template>
<script>
export default {
data() {
return {
text: "Computed 테스트 데이터 문구입니다.",
};
},
// methods 부분에 선언된 함수와 동일한 로직일 때,
// 캐싱 기능이 없는 methods는 호출될 때마다 console 값이 출력 됨
// 반면에, computed는 캐싱 기능이 있기 때문에 methods와 어떤 차이점이 있는지 주의 깊게 살펴보는 것이 포인트
computed: {
computedText() {
console.log("Computed 기능을 생성하였습니다.");
return this.text.split("").reverse().join("");
},
},
methods: {
changeText() {
console.log("함수 호출");
console.log(this.text);
return this.text.split("").reverse().join("");
},
},
};
</script>
<style lang="scss" scoped></style>
- 똑같이 3번 호출하였음에도 methods는 3번 다 호출 computed는 한 번만 호출
3. Watch
- 어떤 데이터를 감시하고 있다가 그 데이터의 값이 변했을 때, 그것을 감지하여 그와 관련된 함수, 로직, 연산 등 다양한 기능을 추가적으로 활용할 수 있도록 도와주는 속성
- 데이터 변경에 대한 응답으로 비동기식 또는 시간이 많이 소요되는 조작을 수행하려는 경우에 가장 유용
- 예) 게시판 페이지를 변경하거나, 선택한 페이지에 대한 리스트만 불러올 경우, 변경된 페이지번호만 감지하여 해당 데이터만 호출할 때 사용
<template>
<button @click="changeMessage">{{ message }}</button>
</template>
<script>
export default {
data() {
return {
message: "안녕하세요. Vue.js Watch 기능 테스트 오리지널 문구",
};
},
// message라는 변수가 바뀌는 것을 지켜보고 있다가 changeMessage()를 통해 바뀌게 되면 alert창을 띄워줌
// 데이터 뿐만 아니라 computed로 계산된 형태의 데이터도 watch로 감지할 수 있음
// 보통 게시판에서 한 컬럼을 선택하였을 때, 고유한 id 값이 바뀜을 감지하고
// 이때, 그 id 값에 따른 상세 데이터를 호출할 때 주로 사용함
watch: {
message() {
window.alert("message 변수에 담긴 데이터가 변경되었습니다.");
},
id() {
// 해당 상세데이터를 조회하는 api 호출
},
},
methods: {
changeMessage() {
console.log("함수 호출");
this.message = "변경된 message 데이터입니다.";
},
},
};
</script>
<style lang="scss" scoped></style>
4. Props와 Emits
Props | Emits |
상위 컴포넌트 -> 하위 컴포넌트 | 하위 컴포넌트 -> 상위 컴포넌트 |
OptionsAPI: props:{} 사용 CompositionAPI: defineProps 사용 |
OptionsAPI: this.$emits 사용 CompositionAPI: defineEmits 사용 |
Prop 받은 데이터의 타입 설정 | 첫 번째 인자: 이벤트 이름 두 번째 인자: 보낼 데이터 |
1) Options API - Props
<template>
<div>
<ChildComponent
v-bind:sendProps1="title"
v-bind:sendProps2="createdAt"
v-bind:sendProps3="obj"
/>
</div>
</template>
<script>
import ChildComponent from "./components/ChildComponent.vue";
export default {
components: {
ChildComponent,
},
data() {
return {
title: "부모 컴포넌트에서 선언된 데이터입니다.",
createdAt: 2024,
obj: {
id: 2024,
name: "John",
},
};
},
};
</script>
<style lang="scss" scoped></style>
- ChildComponent.vue
<template>
<div>{{ sendProps1 }}</div>
<div>{{ sendProps2 }}</div>
<div>{{ sendProps3.id }}</div>
<div>{{ sendProps3.name }}</div>
</template>
<script>
export default {
props: {
sendProps1: String,
sendProps2: Number,
sendProps3: Object,
},
data() {
return {};
},
};
</script>
<style lang="scss" scoped></style>
2) CompositionAPI - Props
<template>
<div>
<ChildComponent
:sendProps1="title"
:sendProps2="createdAt"
:sendProps3="obj"
/>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from "vue";
import ChildComponent from "./components/ChildComponent.vue";
interface Obj {
id: number;
name: string;
}
const title = ref<string>("부모컴포넌트에서 선언된 데이터입니다.");
const createdAt = ref<number>(2024);
const obj = reactive<Obj>({
id: 2024,
name: "John",
});
</script>
<style lang="scss" scoped></style>
- compositionAPI 에서는 setup을 꼭 추가해줘야 한다.
- './components/ChildComponent.vue' 모듈 또는 해당 형식 선언을 찾을 수 없습니다.
- 갑자기 이런 오류가 나타나서 구글링 해봤더니 개발도구인 VSCode에서 개발에 제공되는 내부 타입스크립트 플러그인과 뷰와 관련된 타입스크립트의 중첩으로 발생하는 문제였다.
https://lts0606.tistory.com/663
[Vuejs] 타입스크립트(Typescript) 적용, 모듈 또는 해당 형식 선언을 찾을 수 없습니다.
* 개발도구 : vs코드(VSCode) * 운영체제 : 윈도우 뷰에서 타입스크립트(typescript)를 적용해 보았는데 아무 행위도 안했는데 만났던 오류 입니다. 난 그저 단지 설치하고 소스코드를 눌러서 보려고 했
lts0606.tistory.com
이 블로그에서 하신 대로 했더니 바로 해결!
- ChildComponent.vue
<template>
<div>{{ props.sendProps1 }}</div>
<div>{{ props.sendProps2 }}</div>
<div>{{ props.sendProps3.id }}</div>
<div>{{ props.sendProps3.name }}</div>
</template>
<script setup lang="ts">
interface Obj {
id: number;
name: string;
}
interface Props {
sendProps1: String;
sendProps2: Number;
sendProps3: Obj;
}
// import 할 필요 없음
const props = defineProps<Props>();
</script>
<style lang="scss" scoped></style>
<CompositionAPI에서 타입스크립트를 활용하는 방법>
① Prop 받은 데이터 형식에 맞게 타입을 지정해준다.
② Prop 받은 데이터 이름 자체에 미리 선언해둔 타입을 설정해준다.
interface Obj {
id: number;
name: string;
}
interface Props {
sendProps1: String;
sendProps2: Number;
sendProps3: Obj;
}
③ defineProps, toRefs 내장함수를 통해 Prop 받은 데이터를 활용하여 템플릿 부분에 호출하여 사용한다.
- defineProps는 import 할 필요 X
- toRefs는 import 해야함
const props = defineProps<Props>();
const { sendProps1, sendProps2, sendProps3 } = toRef(props);
3) CompositionAPI - Emits
- ChildComponent.vue
<template>
<button @click="sendEvent">자식컴포넌트에서 만든 버튼</button>
</template>
<script setup lang="ts">
import { ref } from "vue";
const data = ref<string>("자식 컴포넌트에서 선언된 데이터입니다.");
const emit = defineEmits(["send-event"]);
const sendEvent = () => {
emit("send-event", data.value);
};
</script>
<style lang="scss" scoped></style>
- App.vue
<template>
<div>
부모컴포넌트 레이아웃
<ChildComponent @send-event="parentEvent" />
</div>
</template>
<script setup lang="ts">
import ChildComponent from "./components/ChildComponent.vue";
const parentEvent = (event: string) => {
console.log(event);
};
</script>
<style lang="scss" scoped></style>
4) OptionsAPI - Emits
- ChildComponent.vue
<template>
<button @click="sendEvent">자식컴포넌트에서 만든 버튼</button>
</template>
<script>
export default {
data() {
return {
text: "자식 컴포넌트에서 선언된 데이터입니다.",
};
},
methods: {
sendEvent() {
this.$emit("send-event", this.text);
},
},
};
</script>
<style lang="scss" scoped></style>
- App.vue
<template>
<div>
부모컴포넌트 레이아웃
<ChildComponent @send-event="parentEvent" />
</div>
</template>
<script>
import ChildComponent from "./components/ChildComponent.vue";
export default {
components: {
ChildComponent,
},
methods: {
parentEvent(event) {
console.log(event);
},
},
};
</script>
<style lang="scss" scoped></style>
5. v-model
1) 양방향 데이터 바인딩
- 'Props와 Emits의 기능이 동시에 진행된다.'
2) input의 기능에 따라
- 보통 v-model은 input 태그의 inputValue 값과 연관지어 많이 사용함
<template>
<div>
<!-- 영어로 입력받을 때 -->
<input type="text" v-model="inputValue1" />
<!-- 한글을 입력받을 땐 이 방법이 권장됨 -->
<input
type="text"
:value="inputValue2"
@input="inputValue2 = $event.target.value"
/>
</div>
<div>{{ inputValue1 }}</div>
<div>{{ inputValue2 }}</div>
</template>
<script>
export default {
data() {
return {
inputValue1: "",
inputValue2: "",
};
},
};
</script>
<style lang="scss" scoped></style>
'VUE' 카테고리의 다른 글
[VUE3] defineEmits vs defineExpose (0) | 2024.05.04 |
---|---|
[VUE] npm ERR! gyp verb check python checking for Python executable "python2" in the PATHnpm ERR! gyp verb `which` failed Error: not found: python2 (0) | 2024.04.22 |
[VUE] VUEX 알아보기 (0) | 2024.04.21 |
[VUE] 카테고리 필터링 기능 / vue transition 적용하기 (0) | 2024.04.19 |
[VUE] 1. vite로 Vue 시작하기 / npm cache 오류 해결 / 라이프사이클 훅 학습하기 (0) | 2024.04.09 |