데일리 공부 기록

hands on vue3 - 제공된 소스에서 라우터 children 예제 풀어보기

탐훈 2023. 3. 29. 23:58
728x90

[목표]

지금까지 라우터로 만든 예제 중

/teams 가 있었고

/teams/:teamsId가 있었다.

 

라우터의 시점에서 보면 

두 URL은 서로 연관이 없다.

 

router의 children이라는 속성을 이용하여

연관된 URL을 묶어 놓자. 

 

퀴즈는

View Members를 클릭하면

그에 맞는 멤버들을

<router-view>를 통해서 보일 수 있도록 한다.

 

힌트

더보기

main.js를 다음과 같이 변경해라

import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router'
import TeamsList from './components/teams/TeamsList.vue';
import UsersList from './components/users/UsersList.vue';
import TeamMembers from './components/teams/TeamMembers.vue';
import NotFound from './components/NotFound/NotFound.vue';

import App from './App.vue';

const app = createApp(App)
const router = createRouter({
    history: createWebHistory(),
    routes:[{
        path: '/teams',
        component: TeamsList,
        children: [
            { path: ':teamsId', component: TeamMembers, props: true }
        ]
    },
    {
        path: '/users',
        component: UsersList,
    },
    // {
    //     path: '/teams/:teamsId',
    //     component: TeamMembers,
    //     props: true,
    // },
    {
        path: '/:anythingyouwant(.*)',
        component: NotFound
    },

],
    linkActiveClass: '커스텀클래스명'
});
app.use(router);

app.mount('#app');

 children은 

해당 URL 하위에 속해진다. 

그리고 주의해야할 점은 '/'가 붙지 않는다.

 

하위 URL로 이동할 때는

URL을 통해 이동하지 않는다.

신기한 특징이다. 

 

덩달아 렌더링도 다시 하지 않는다.

 


소스 디렉토리

수정전 소스

[TheNavigation.vue]

<template>
  <header>
    <nav>
      <ul>
        <li>
          <router-link to="/teams">Teams</router-link>
        </li>
        <li>
          <router-link to="/users">Users</router-link>
        </li>
      </ul>
    </nav>
  </header>
</template>

<style scoped>
header {
  width: 100%;
  height: 5rem;
  background-color: #11005c;
}

nav {
  height: 100%;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

li {
  margin: 0 2rem;
}

a {
  font: inherit;
  background: transparent;
  border: 1px solid transparent;
  cursor: pointer;
  color: white;
  padding: 0.5rem 1.5rem;
  display: inline-block;
}

a:hover,
a:active,
a.커스텀클래스명 {
  color: #f1a80a;
  border-color: #f1a80a;
  background-color: #1a037e;
}
</style>

[TeamMembers.vue]

<template>
  <section>
    <h2>{{ teamName }}</h2>
    <ul>
      <user-item
        v-for="member in members"
        :key="member.id"
        :id="member.id"
        :name="member.fullName"
        :role="member.role"
      ></user-item>
    </ul>
    <router-link to="/teams/t2">Go To Team2</router-link>
  </section>
</template>

<script>
import UserItem from '../users/UserItem.vue';

export default {
  inject: ['teams', 'users'],
  props: ['teamsId'],
  components: {
    UserItem
  },
  data(){
    return{
      members:[],
    };
  },
  methods:{
    changeData(){
      let result = this.teams.find((element) => {
        for(let i in element.members){
          return element.id == this.teamsId;
        }
      });       

      for(let i in result.members){
        this.users.find((element)=>{
          if(element.id == result.members[i]){
            this.members.push(element);
          }
        });
      }
      }
  },
  created(){
    this.changeData();
  },
  watch:{
    teamsId(){
      this.changeData();
    }
  }
};
</script>

<style scoped>
section {
  margin: 2rem auto;
  max-width: 40rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
  padding: 1rem;
  border-radius: 12px;
}

h2 {
  margin: 0.5rem 0;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
</style>

[TeamsItem.vue]

<template>
  <li>
    <h3>{{ name }}</h3>
    <div class="team-members">{{ memberCount }} Members</div>
    <router-link :to="viewMembers">View Members</router-link>
  </li>
</template>

<script>
export default {
  props: ['id','name', 'memberCount'],
  computed:{
    viewMembers(){
      let path = "/teams/" + this.id;
      return path;
    }
  }
};
</script>

<style scoped>
li {
  margin: 1rem 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
  border-radius: 12px;
  padding: 1rem;
}

li h3 {
  margin: 0.5rem 0;
  font-size: 1.25rem;
}

li .team-members {
  margin: 0.5rem 0;
}

a {
  text-decoration: none;
  color: white;
  display: inline-block;
  padding: 0.5rem 1.5rem;
  background-color: #11005c;
}

a:hover,
a:active {
  background-color: #220a8d;
}
</style>

[TeamsList.vue]

<template>
  <ul>
    <teams-item
      v-for="team in teams"
      :key="team.id"
      :id="team.id"
      :name="team.name"
      :member-count="team.members.length"
    ></teams-item>
  </ul>
</template>

<script>
import TeamsItem from './TeamsItem.vue';

export default {
  components: {
    TeamsItem,
  },
  inject: ['teams'],
};
</script>

<style scoped>
ul {
  list-style: none;
  margin: 2rem auto;
  max-width: 40rem;
  padding: 0;
}
</style>

[UserItem.vue]

<template>
  <li>
    <h3>{{ name }}</h3>
    <div class="role" :class="roleClass">{{ role }}</div>
  </li>
</template>

<script>
export default {
  props: ['name', 'role'],
  computed: {
    roleClass() {
      if (this.role === 'Engineer') {
        return 'role--engineer';
      }
      if (this.role === 'Consultant') {
        return 'role--consultant';
      }
      return null;
    },
  },
};
</script>

<style scoped>
li {
  margin: 1rem 0;
  border: 1px solid #ccc;
  padding: 1rem;
}

h3 {
  margin: 0.5rem 0;
}

.role {
  border-radius: 40px;
  background-color: #ccc;
  color: #252525;
  padding: 0.25rem 1rem;
  display: inline-block;
}

.role--engineer {
  background-color: blue;
  color: white;
}

.role--consultant {
  background-color: #af003a;
  color: white;
}
</style>

[UsersList.vue]

<template>
  <ul>
    <user-item v-for="user in users" :key="user.id" :name="user.fullName" :role="user.role"></user-item>
  </ul>
</template>

<script>
import UserItem from './UserItem.vue';

export default {
  components: {
    UserItem,
  },
  inject: ['users'],
};
</script>

<style scoped>
ul {
  list-style: none;
  margin: 2rem auto;
  max-width: 20rem;
  padding: 0;
}
</style>

[main.js]

import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router'
import TeamsList from './components/teams/TeamsList.vue';
import UsersList from './components/users/UsersList.vue';
import TeamMembers from './components/teams/TeamMembers.vue';
import NotFound from './components/NotFound/NotFound.vue';

import App from './App.vue';

const app = createApp(App)
const router = createRouter({
    history: createWebHistory(),
    routes:[{
        path: '/teams',
        component: TeamsList,
    },
    {
        path: '/users',
        component: UsersList,
    },
    {
        path: '/teams/:teamsId',
        component: TeamMembers,
        props: true,
    },
    {
        path: '/:anythingyouwant(.*)',
        component: NotFound
    },

],
    linkActiveClass: '커스텀클래스명'
});
app.use(router);

app.mount('#app');

 

 


수정 후 소스

[TeamsItem.vue]

<template>
  <li>
    <h3>{{ name }}</h3>
    <div class="team-members">{{ memberCount }} Members</div>
    <router-link :to="viewMembers">View Members</router-link>
  </li>
</template>

<script>
export default {
  props: ['id','name', 'memberCount'],
  computed:{
    viewMembers(){
      let path = "/teams/" + this.id;
      return path;
    }
  }
};
</script>

<style scoped>
li {
  margin: 1rem 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
  border-radius: 12px;
  padding: 1rem;
}

li h3 {
  margin: 0.5rem 0;
  font-size: 1.25rem;
}

li .team-members {
  margin: 0.5rem 0;
}

a {
  text-decoration: none;
  color: white;
  display: inline-block;
  padding: 0.5rem 1.5rem;
  background-color: #11005c;
}

a:hover,
a:active {
  background-color: #220a8d;
}
</style>

 

[main.js]

import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router'
import TeamsList from './components/teams/TeamsList.vue';
import UsersList from './components/users/UsersList.vue';
import TeamMembers from './components/teams/TeamMembers.vue';
import NotFound from './components/NotFound/NotFound.vue';

import App from './App.vue';

const app = createApp(App)
const router = createRouter({
    history: createWebHistory(),
    routes:[{
        path: '/teams',
        component: TeamsList,
        children: [
            {path:':teamsId', component: TeamMembers, props: true}
        ]
    },
    {
        path: '/users',
        component: UsersList,
    },
    // {
    //     path: '/teams/:teamsId',
    //     component: TeamMembers,
    //     props: true,
    // },
    {
        path: '/:anythingyouwant(.*)',
        component: NotFound
    },

],
    linkActiveClass: '커스텀클래스명'
});
app.use(router);

app.mount('#app');

회사에서는 

라우터를 한 두개? 외에는 사용하지 않는다.

 

<component> 태그를 사용하는데

그것이 훨씬 SPA느낌이 강한 듯 하다.

 

라우터로 URL이 이동되면

많은 부분이 갱신되고 

갱신되는 만큼 리소스를 로드하는데 시간이 걸린다.

 

나중에 되도록이면 router 보다는 component로 요소들을 구현하고

router는 보안, PC/MOBILE 등 설정을 위해 확인하는 용도로 사용할 듯 하다.