본문 바로가기
💻CODING/react. vue

[vue] vue 리팩토링: 공통 컴포넌트화(api 통해 뉴스 사이트 구현 ver.6)

by 코딩하는 갓디노 2021. 1. 23.

 

오픈 API를 통하여 블로그 형식의 뉴스 사이트를 
vue로 구현하는 예제(ver.6)입니다.
- 리팩토링 -



 

 

예제는 인프런의 캡틴판교, "장기효"님의 Vue.js 완벽 가이드 - 실습과 리팩토링으로 배우는 실전 개념을
들으면서 공부한 내용입니다.

 

리팩토링 컴포넌트 공통화

페이지에서 사용되는 컴포넌트들의 패턴이 중복되어 사용되었을때
이를 공통 컴포넌트화 시킴으로써 코드를 간편화, 구조화 시킵니다.

 

구현 내용

News/Ask/Jobs 각 페이지를
ListItem.vue에서 컴포넌트를 시킨 후 
각각의 페이지에서 ListItem.vue를 import 시킵니다. 

 

리팩토링 before

vue > NewsView.vue

<template>
  <div>
    <ul class="new-list">
      <li class="post" v-for="item in askItems" v-bind:key="item.id">
        <div class="points">{{ item.points }}</div>
        <div>
          <div class="new-title">
            <a v-bind:href="item.url" target="_blank">{{
              item.title
            }}</a>
          </div>
          <small class="link-text"
            >{{ item.time_ago }} by
            <router-link class="link-text" v-bind:to="`/user/${item.user}`">{{
              item.user
            }}</router-link></small
          >
        </div>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  created() {
    this.$store.dispatch("FETCH_NEWS");
  },
};
</script>

<style>
.new-list {
  margin: 0;
  padding: 0;
}
.post {
  list-style: none;
  display: flex !important;
  align-items: center;
  border-bottom: 1px solid #eee;
}
.points {
  width: 80px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #42b883;
}
.new-title {
  margin: 0;
}
.link-text {
  color: #828282;
}
</style>

 

리팩토링 after

· vue > ListItem.vue 파일 생성하여 NewsView.vue의 전체 코드를 copy
· 각 컴포넌트에서 다른 부분은 분기처리(created, template)
· 각 컴포넌트에서는 ListItem.vue를 import

 

created() before

각 컴포넌트

//ListView.vue
export default {
  created() {
    this.$store.dispatch("FETCH_NEWS");
  },
};

//AskView.vue
export default {
  created() {
    this.$store.dispatch("FETCH_ASK");
  },
};

//JobsView.vue
export default {
  created() {
    this.$store.dispatch("FETCH_JOBS");
  },
};

 

created() after

각 this.$store.dispatch("FETCH_XXX"); 를 this.$router에 있는것으로 분기처리를 합니다. 

routes에서 각 컴포넌트별로 name 지정(this.$route.name으로 불러올수 있음)

routes > index.js

routes: [
    { path: '/news', name: 'news', component: NewsView },
    { path: '/ask', name: 'ask', component: AskView },
    { path: '/jobs', name: 'jobs', component: JobsView },
  ]

 

components > ListItem.vue

<script>
export default {
  name: "list-item",
  created() {
    //this.$store.dispatch("FETCH_NEWS");
    //this.$router에 있는것으로 분기처리 함
    //console.log(this.$route);
    const name = this.$route.name;
    if (name === "news") {
      return this.$store.dispatch("FETCH_NEWS");
    } else if (name === "ask") {
      return this.$store.dispatch("FETCH_ASK");
    } else if (name === "jobs") {
      return this.$store.dispatch("FETCH_JOBS");
    }
  }
};
</script>

 

<template> before

//NewsView.vue
    <ul class="new-list">
      <li class="post" v-for="item in this.$store.state.news"" v-bind:key="item.id">
        <div class="points">{{ item.points }}</div>
        <div>
          <div class="new-title">
            <a v-bind:href="item.url" target="_blank">{{
              item.title
            }}</a>
          </div>
          <small class="link-text"
            >{{ item.time_ago }} by
            <router-link class="link-text" v-bind:to="`/user/${item.user}`">{{
              item.user
            }}</router-link></small
          >
        </div>
      </li>
    </ul>

//AskView.vue
 	<ul class="new-list">
      <li class="post" v-for="item in this.$store.state.news" v-bind:key="item.id">
        <div class="points">{{ item.points }}</div>
        <div>
          <div class="new-title">
            <router-link v-bind:to="`item/${item.id}`" target="_blank">{{
              item.title
            }}</router-link>
          </div>
          <small class="link-text"
            >{{ item.time_ago }} by
            <router-link class="link-text" v-bind:to="`/user/${item.user}`">{{
              item.user
            }}</router-link></small
          >
        </div>
      </li>
    </ul>
    
//JobsView.vue
 	<ul class="new-list">
      <li
        class="post"
        v-for="item in this.$store.state.jobs"
        v-bind:key="item.id"
      >
        <div class="points">{{ item.type }}</div>
        <div>
          <div class="new-title">
            <a v-bind:href="item.url" target="_blank">{{ item.title }}</a>
          </div>
          <small class="link-text"
            >{{ item.time_ago }} by <a :href="item.url" target="_blank">{{ item.domain }}</a>
          </small
          >
        </div>
      </li>
    </ul>

 

<template> after

NewsView.vue

<template>
  <div>
    <list-item />
  </div>
</template>



 

 

ListItem의 <template>, computed after

기존의 this.$store.state.news / this.$store.state.ask /this.$store.state.jobs를
listItems 하나로 정의 한 후,
computed 속성으로 this.$route.name을 이용하여 분기처리

· this.$store.state.news -> listItems 변경
· computed에 listItems 속성 추가
· this.$route.name을 이용하여 분기 처리
· v-if/v-else를 이용하여 분기 처리

 

components > ListItem.vue <template>

<template>
  <div>
    <ul class="new-list">
   	//기존의 this.$store.state.news(ask/jobs)를 listItems로 정의
      <li class="post" v-for="item in listItems" v-bind:key="item.id">
        <div class="points">{{ item.points || item.type }}</div>
        <div>
          <div class="new-title">
            <a v-bind:href="item.url" target="_blank" v-if="item.domain">{{
              item.title
            }}</a>

            <router-link
              v-else=""
              v-bind:to="`item/${item.id}`"
              target="_blank"
              >{{ item.title }}</router-link
            >
          </div>

          <small class="link-text"
            >{{ item.time_ago }} by
            <router-link
              class="link-text"
              v-bind:to="`/user/${item.user}`"
              v-if="item.user"
              >{{ item.user }}</router-link
            >
            <a :href="item.url" target="_blank" v-else>{{ item.domain }}</a>
          </small>
        </div>
      </li>
    </ul>
  </div>
</template>

 

components > ListItem.vue <script>

computed: {
    listItems() {
      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;
      }
    },
  },
반응형

댓글