728x90
반응형
컴포넌트 공통화 리팩토링
페이지에서 공통적으로 사용되는 부분들을 컴포넌트로 만드는 작업. 하나의 피드를 컴포넌트로 만들어 컴포넌트를 반복적으로 뿌리는 것이 더 효율적이다.
리스트 아이템 컴포넌트 공통화
아이템 리스트를 뿌려줄 컴포넌트를 공통화를 하여 NewsView
, AskView
, JobsView
에 공통적으로 작성된 코드를 정리할 수 있다.
구현하기
1. src/components/ListItem.vue
생성
<template>
<div>
<ul class="news-list">
<li v-for="item in listItems" class="post">
<!-- 포인트 영역 -->
<div class="points">{{ item.points || 0 }}</div>
<!-- 기타 정보 영역 -->
<div>
<!-- 타이틀 영역 -->
<p class="news-title">
<!-- v-if v-else 사용 -->
<!-- item.domain이 있으면 NewsView, JobsView -->
<template v-if="item.domain">
<a v-bind:href="item.url">{{ item.title }}</a>
</template>
<!-- item.domain이 없으면 AskView -->
<template v-else>
<router-link v-bind:to="`item/${item.id}`">{{ item.title }}</router-link>
</template>
</p>
<small class="link-text">
{{ item.time_ago }} by
<!-- item.user가 있으면 유저 상세페이지 이동 -->
<router-link
v-if="item.user"
v-bind:to="`/user/${item.user}`"
class="link-text"
>{{ item.user }}</router-link>
<!-- item.user가 없으면 item.url로 이동 -->
<a v-bind:href="item.url" v-else>{{ item.domain }}</a>
</small>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
created() {
// router name에 따라 가져올 데이터 분기처리
const name = this.$route.name;
if (name === "news") {
this.$store.dispatch("FETCH_NEWS");
} else if (name === "ask") {
this.$store.dispatch("FETCH_ASK");
} else if (name === "jobs") {
this.$store.dispatch("FETCH_JOBS");
}
},
computed: {
listItems() {
// router name에 따라 state 속성 분기처리
const name = this.$route.name;
if (name === "news") {
return this.$store.state.news;
} else if (name === "ask") {
return this.$store.state.ask;
} else if (name === "jobs") {
return this.$store.state.jobs;
}
},
},
};
</script>
<style scoped>
.news-list {
margin: 0;
padding: 0;
}
.post {
list-style: none;
display: flex;
align-items: center;
border-bottom: 1px solid #eee;
}
.points {
width: 80px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
color: #42b883;
}
.news-title {
margin: 0;
}
.link-text {
color: #828282;
}
</style>
※ router name은 src/routes/index.js
에 VueRouter의 routes
속성 구현 시 name
값 선언하여 사용
2. NewsView.vue
, AskView.vue
, JobsView.vue
코드 정리
<template>
<div>
<list-item></list-item>
</div>
</template>
<script>
import ListItem from '../components/ListItem.vue';
export default {
components: {
ListItem,
}
};
</script>
※ NewsView, AskView, JobsView 코드 동일. ListItem으로 공통 컴포넌트화를 하였고 각 페이지에 필요한 데이터에 대한 분기처리도 되어있기 때문
사용자 프로필 컴포넌트 공통화
NewsView
, AskView
, ItemView
에서 접근 가능한 UserView
에 표시할 컴포넌트를 공통화
구현하기
1. src/components/UserProfile.vue
생성
<template>
<div>
<section>
<div class="user-container">
<div>
<i class="fas fa-user"></i>
</div>
<div class="user-description">
<div>User Id</div>
<div class="time">User Created</div>
</div>
</div>
</section>
</div>
</template>
<script>
export default {};
</script>
<style scoped>
.user-container {
display: flex;
align-items: center;
padding: 0.5rem;
}
.fa-user {
font-size: 2.5rem;
}
.user-description {
padding-left: 8px;
}
.time {
font-size: 0.7rem;
}
</style>
2. src/views/UserView.vue
수정
<template>
<div>
<user-profile></user-profile>
</div>
</template>
<script>
import UserProfile from '../components/UserProfile.vue';
export default {
components: {
UserProfile,
},
computed: {
userInfo() {
return this.$store.state.user;
},
},
created() {
const userName = this.$route.params.id;
this.$store.dispatch("FETCH_USER", userName);
},
};
</script>
※ 데이터 처리 흐름 차이
1. UserProfile
에서 computed
로 접근
// UserProfile.vue
<template>
<div class="user-description">
<div>{{ userInfo.id }}</div>
<div class="time">{{ userInfo.created }}</div>
</div>
</template>
<script>
export default {
computed: {
userInfo() {
return this.$store.state.user;
},
},
};
</script>
// UserView.vue
<template>
<div>
<user-profile></user-profile>
</div>
</template>
<script>
import UserProfile from '../components/UserProfile.vue';
export default {
components: {
UserProfile,
},
created() {
const userName = this.$route.params.id;
this.$store.dispatch("FETCH_USER", userName);
},
};
</script>
2. UserView
에서 props
로 전달
// UserProfile.vue
<template>
<div class="user-description">
<div>{{ info.id }}</div>
<div class="time">{{ info.created }}</div>
</div>
</template>
<script>
export default {
props: {
info: Object,
},
};
</script>
// UserView.vue
<template>
<div>
<!-- info라는 이름으로 props속성 이용 데이터 전달 -->
<user-profile :info="userInfo"></user-profile>
</div>
</template>
<script>
import UserProfile from '../components/UserProfile.vue';
export default {
components: {
UserProfile,
},
computed: {
userInfo() {
return this.$store.state.user;
},
},
created() {
const userName = this.$route.params.id;
this.$store.dispatch("FETCH_USER", userName);
},
};
</script>
3. 동작 방식
slot을 이용한 사용자 프로필 컴포넌트 구현
컴포넌트의 재사용성을 높이기 위해 slot
을 이용하여 UserProfile
컴포넌트를 구현
구현하기
1. src/components/UserProfile.vue
수정
<template>
<div>
<section>
<div class="user-container">
<div>
<i class="fas fa-user"></i>
</div>
<div class="user-description">
<slot name="username">
<!-- 상위 컴포넌트에서 정의할 영역 -->
</slot>
<div class="time">
<slot name="time">
<!-- 상위 컴포넌트에서 정의할 영역 -->
</slot>
<slot name="karma">
<!-- 상위 컴포넌트에서 정의할 영역 -->
</slot>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
export default {
props: {
info: Object,
},
};
</script>
<style scoped>
.user-container {
display: flex;
align-items: center;
padding: 0.5rem;
}
.fa-user {
font-size: 2.5rem;
}
.user-description {
padding-left: 8px;
}
.time {
font-size: 0.7rem;
}
</style>
2. ItemView
, UserView
수정
// ItemView.vue
<template>
<div>
<!-- 사용자 상세 정보 -->
<section>
<user-profile :info="fetchedItem">
<!-- slot 사용 -->
<router-link slot="username" :to="`/user/${fetchedItem.user}`">{{ fetchedItem.user }}</router-link>
<template slot="time">{{ 'Posted ' + fetchedItem.time_ago }}</template>
</user-profile>
</section>
<section>
<h2>{{ fetchedItem.title }}</h2>
</section>
<!-- 질문 내용 -->
<section>
<div v-html="fetchedItem.content"></div>
</section>
</div>
</template>
// UserView.vue
<template>
<div>
<user-profile :info="userInfo">
<!-- slot 사용 -->
<div slot="username">{{ userInfo.id }}</div>
<span slot="time">{{ 'Joined ' + userInfo.created }}, </span>
<span slot="karma">{{ userInfo.karma }}</span>
</user-profile>
</div>
</template>
728x90
반응형
'Dev > Vue.js' 카테고리의 다른 글
[Vue.js] Hacker News - 데이터 호출과 UX (0) | 2020.09.25 |
---|---|
[Vue.js] HackerNews - Mixin과 하이 오더 컴포넌트 (0) | 2020.09.24 |
[Vue.js] Hacker News - 라우터 실전 (0) | 2020.09.21 |
[Vue.js] Hacker News - 스토어 구현 (0) | 2020.09.18 |
[Vue.js] Hacker News - API 구현 (0) | 2020.09.17 |
댓글