아웅 정리할수록 복잡하고 더 헷갈리네ㅎ
이런 것들 언젠가 편하게 사용하겠지?ㅜ
아무튼 common, slot, controlled, renderless 디자인 패턴에 대해 알아보자
컴포넌트 디자인 패턴
Common
- 간단하고 기본적인 컴포넌트 등록과 컴포넌트 통신
- AppContent, Appheader 컴포넌트를 불러옴
- 이벤트, props 활용
before | after |
|
버튼을 클릭하면 내용이 바뀌도록 구현해보자
// CommonView.vue
<template>
<div>
<app-header :name="userName"></app-header>
<app-content :items="items" @evol="evolItems"></app-content>
</div>
</template>
<script>
import AppHeader from '../components/AppHeader.vue';
import AppContent from '../components/AppContent.vue';
export default {
components: {
AppHeader,
AppContent,
},
data() {
return {
userName: '포켓몬',
items: ['피카츄', '꼬부기', '파이리'],
}
},
methods: {
evolItems() {
this.items = ['라이츄', '어니부기', '리자돈'];
},
},
}
</script>
import한 AppHeader 컴포넌트와 AppContent 컴포넌트 간 데이터를
이벤트, props로 주고받고 있다.
// AppHeader.vue
<template>
<header>
<h1>{{ name }}</h1>
</header>
</template>
<script>
export default {
props: {
name: String,
}
}
</script>
AppHeader에서는 userName을 props로 받아서 그대로 표시
// AppContent.vue
<template>
<div>
<ul>
<li v-for="item, index in items" :key="index">
{{ item }}
</li>
</ul>
<button @click="$emit('evol')">진화</button>
</div>
</template>
<script>
export default {
props: {
items: {
type: Array,
// 필수값 정의
required: true,
},
},
}
</script>
AppContent에서는 버튼이 있는데
클릭하면 evol 이벤트를 전송
// CommonView.vue
...
<app-content :items="items" @evol="evolItems"></app-content>
...
methods: {
evolItems() {
this.items = ['라이츄', '어니부기', '리자돈'];
},
},
CommonView 에서 evol 이벤트 발생 시 호출할 메서드를 통해 내용이 바뀌게 된다.
Slot
- 마크업 확장이 가능한 컴포넌트
- 확장이 유연하다
- slotComponent에 배치된 slot들을 상위 컴포넌트에서 정의한다.
slot을 활용해서 위 구조처럼 개발해보자
// SlotComponent.vue
<template>
<div>
<div>
<slot name="slotTest">
empty
</slot>
</div>
<div>
<slot name="slotRouterTest">
empty
</slot>
</div>
<div>
<slot name="slotEmptyTest">
empty
</slot>
</div>
</div>
</template>
<script>
export default {
}
</script>
slot을 정의한 컴포넌트
각 slot 별로 name도 정의했고 현재는 empty 라는 텍스트만 있다.
이제 상위 컴포넌트에서 slot을 채워보자
// 상위 컴포넌트
<template>
<div>
<SlotComponent>
<!-- slotTest라는 slot을 정의 -->
<template slot="slotTest">
<div>텍스트</div>
</template>
<!-- slotRouterTest라는 slot을 정의 -->
<router-link slot="slotRouterTest" :to="`/.`">
링크
</router-link>
</SlotComponent>
</div>
</template>
<script>
import SlotComponent from '../components/SlotComponent.vue'
export default {
components: {
SlotComponent,
}
}
</script>
<style></style>
상위 컴포넌트에서 slot들을 재정의한다.
하나는 template을, 하나는 router-link를
마지막 하나는 재정의하지 않았다.
Controlled
- 하위에서 관리해야 했던 데이터를 상위에서 관리하게 하는 방법
- props로 전달하지 않고 v-model 활용
- 컴포넌트에 데이터 의존성 분리
v-model은 양방향 데이터 바인딩을 지원한다.
즉, 화면의 데이터와 뷰 인스턴스의 데이터가 일치한다.
또한 v-model 은 @input이벤트와 :value로 이루어져있다.
props와 v-model의 데이터를 전달을 비교해보자
일단 props로 전달해보자
<template>
<div>
<div>
props 사용
<check-box-2 :checked2="checked2" @renew="renewItems"></check-box-2>
checked2는 {{ checked2 }}
</div>
</div>
</template>
<script>
import CheckBox2 from '../components/CheckBox2.vue';
export default {
components: {
CheckBox2
},
data() {
return {
checked2: false,
}
},
methods: {
renewItems() {
this.checked2 = !this.checked2
}
}
}
</script>
우선 하위 컴포넌트에게 checked2 데이터를 보내고 있다.
보내지 않으면 데이터가 비겠지
<template>
<input type="checkbox" @click="toggleCheckBox">
</template>
<script>
export default {
// v-model이 :value로 내려옴
props: {
checked2: Boolean
},
methods: {
toggleCheckBox() {
// 하위에서 상위로 이벤트 보내기
this.$emit('renew');
}
}
}
</script>
하위에서는 props로 데이터를 받아야한다.
그리고 버튼 클릭시 이벤트를 보내고 부모에서 해당 이벤트를 받으면
checked2 데이터를 변경하는 메서드를 수행해야 한다.
이제 v-model를 사용해보자
<template>
<div>
<div>
v-model 사용
<check-box v-model="checked"></check-box>
checked는 {{ checked }}
</div>
</div>
</template>
<script>
import CheckBox from '../components/CheckBox.vue';
export default {
components: {
CheckBox,
},
data() {
return {
checked: false,
}
},
}
</script>
v-model은 양방향 디렉티브니까 데이터를 따로 보내지 않아도 된다.
<template>
<input type="checkbox" :value="value" @click="toggleCheckBox">
</template>
<script>
export default {
// v-model이 :value로 내려옴
props: ['value'],
methods: {
toggleCheckBox() {
// 하위에서 상위로 데이터 올리기
this.$emit('input', !this.value);
}
}
}
</script>
자식에서 :value를 통해 props로 받고,
버튼을 클릭하면 데이터를 input 이벤트가 발생한다.
이를 통해 자식에서 관리해야 했던 데이터를 부모에서 관리하게 하여
컴포넌트 데이터 의존성을 분리할 수 있다.
Renderless
- 데이터 처리 컴포넌트
- 템플릿 표현식이 없다!
- 그저 스크립트로 로직만 있는 컴포넌트
- 데이터 제공만하는 컴포넌트
render
render는 template를 내부적으로 구현하는 함수
createElement를 기본 인자로 받는다.
즉, 태그 명, 속성, 태그를 인자로 받는다.
코드로 이해해보장
<template>
<div>
<!-- 1. url 전달 -->
<fetch-renderless url="url.com">
<!-- 2. slot-scope -->
<div slot-scope="{ response, loading}">
<div v-if="!loading">
{{ response }}
</div>
<div v-else>
로딩중
</div>
</div>
</fetch-renderless>
</div>
</template>
<script>
import FetchRenderless from '../components/FetchRenderless.vue'
export default {
components: {
FetchRenderless
},
}
</script>
FetchRenderless 컴포넌트에 props로 url을 전달
slot-scopt를 통해 하위 컴포넌트의 데이터(response, loading) 접근!
// FetchRenderless.vue
<script>
import axios from 'axios';
export default {
props: ['url'],
data() {
return {
response: null,
loading: true,
}
},
created() {
axios.get(this.url)
.then(response => {
this.response = response.data;
this.loading = false;
})
.catch(error => {
alert('[ERROR] fetching the data', error);
console.log(error);
});
},
// render 함수
render() {
// scopedSlots : 하위 컴포넌트의 데이터(response, loading)에 접근할 수 있음
return this.$scopedSlots.default({
response: this.response,
loading: this.loading,
});
},
}
</script>
FetchRenderless 컴포넌트는 template 없이 그저 비즈니스 로직으로만 이루어져 있다.
'🧠 저장 > Vue' 카테고리의 다른 글
vue2 watch와 computed의 차이점 (0) | 2024.01.24 |
---|---|
vue2 네비게이션 가드 간단 정리 (0) | 2024.01.21 |
mixin 간단 정리 (0) | 2024.01.09 |
하이오더컴포넌트(HOC) 간단 정리 (0) | 2024.01.03 |
eventBus 간단 정리 (0) | 2023.12.31 |