데일리 공부 기록
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 등 설정을 위해 확인하는 용도로 사용할 듯 하다.