Vue Router
Vue 라우터는 Vue.js의 공식 라우터입니다. Vue.js를 사용한 싱글 페이지 앱을 쉽게 만들 수 있도록 Vue.js의 코어와 긴밀히 통합되어 있습니다.
Features
아래의 기능을 포함합니다.
- 중첩된 라우트/뷰 매핑
- 모듈화된, 컴포넌트 기반의 라우터 설정
- 라우터 파라미터, 쿼리, 와일드카드
- Vue.js의 트랜지션 시스템을 이용한 트랜지션 효과
- 세밀한 네비게이션 컨트롤
- active CSS 클래스를 자동으로 추가해주는 링크
- HTML5 히스토리 모드 또는 해시 모드(IE9에서 자동으로 폴백)
- 사용자 정의 가능한 스크롤 동작
시작하기
Vue와 Vue 라우터를 이용해 싱글 페이지 앱을 만드는 것은 매우 쉽습니다. Vue.js를 사용한다면 이미 컴포넌트로 앱을 구성하고 있을 것입니다. Vue 라우터를 함께 사용할 때 추가로 해야하는 것은 라우트에 컴포넌트를 매핑한 후, 어떤 주소에서 렌더링할 지 알려주는 것 뿐입니다.
HTML
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 네비게이션을 위해 router-link 컴포넌트를 사용합니다. -->
<!-- 구체적인 속성은 `to` prop을 이용합니다. -->
<!-- 기본적으로 `<router-link>`는 `<a>` 태그로 렌더링됩니다.-->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 라우트 아울렛 -->
<!-- 현재 라우트에 맞는 컴포넌트가 렌더링됩니다. -->
<router-view></router-view>
</div>
JavaScript
// 0. 모듈 시스템 (예: vue-cli)을 이용하고 있다면, Vue와 Vue 라우터를 import 하세요
// 그리고 `Vue.use(VueRouter)`를 호출하세요
// 1. 라우트 컴포넌트를 정의하세요.
// 아래 내용들은 다른 파일로부터 가져올 수 있습니다.
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 라우트를 정의하세요.
// Each route should map to a component. The "component" can
// 각 라우트는 반드시 컴포넌트와 매핑되어야 합니다.
// "component"는 `Vue.extend()`를 통해 만들어진
// 실제 컴포넌트 생성자이거나 컴포넌트 옵션 객체입니다.
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3. `routes` 옵션과 함께 router 인스턴스를 만드세요.
// 추가 옵션을 여기서 전달해야합니다.
// 지금은 간단하게 유지하겠습니다.
const router = new VueRouter({
routes // `routes: routes`의 줄임
})
// 4. 루트 인스턴스를 만들고 mount 하세요.
// router와 router 옵션을 전체 앱에 주입합니다.
const app = new Vue({
router
}).$mount('#app')
// 이제 앱이 시작됩니다!
라우터를 주입하였으므로 this.$router
와 현재 라우트를 this.$route
로 접근할 수 있습니다.
// Home.vue
export default {
computed: {
username () {
// 곧 `params` 확인할 수 있습니다.
return this.$route.params.username
}
},
methods: {
goBack () {
window.history.length > 1
? this.$router.go(-1)
: this.$router.push('/')
}
}
}
문서 전체에서 router
를 자주 사용했습니다. this.$router
는 정확히 router
와 동일합니다. this.$router
를 사용하는 이유는 라우터를 조작해야하는 모든 컴포넌트에서 라우트 객체를 가져올 필요가 없기 때문입니다.
이 예제에서 바로 확인해볼 수 있습니다.
<router-link>는 현재 라우트와 일치할 때 자동으로 .router-link-active
클래스가 추가됩니다. API 레퍼런스를 확인하세요.
<router-link>
<router-link>는 라우터 지원 앱에서 사용자 네비게이션을 가능하게하는 컴포넌트입니다. 목표 위치는 to
prop로 지정됩니다. 기본적으로 올바른 href
를 갖는 <a>태그로 렌더링 되지만 tag
prop로 구성 될 수 있습니다. 또한 대상 라우트가 활성화되어 있으면 링크가 자동으로 active CSS 클래스를 가져옵니다.
<router-link>는 다음과 같은 이유로 하드 코드 된 <a href="...">보다 선호됩니다.
- HTML5 히스토리 모드와 해시 모드에서 모두 동일한 방식으로 작동하므로 모드를 트랜지션하기로 결정하거나 라우터가 IE9에서 해시 모드로 트랜지션 한 경우 변경할 필요가 없습니다.
- HTML5 히스토리 모드에서,
router-link
는 클릭 이벤트를 차단하여 브라우저가 페이지를 다시 로드하지 않도록합니다. - HTML5 히스토리 모드에서
base
옵션을 사용할 때to
prop의 URL에 이를 포함 할 필요가 없습니다.
동적 라우트 매칭
중첩된 라우트
프로그래밍 방식 네비게이션
이름을 가지는 라우트
이름을 가지는 뷰
리다이렉트와 별칭
라우트 컴포넌트에 속성 전달
vue router로 데이터를 전달하는 방법은 query
와, params
2가지가 있습니다.
Query
받을 때:
<template>
<div>
<h1>Query</h1>
<h2>name: {{ $route.query.name }}</h2>
<h2>age: {{ $route.query.age }}</h2>
</div>
</template>
<script>
export default {
name: 'Query',
}
</script>
Params
받을 때:
<template>
<div>
<h1>Params</h1>
<h2>params로 받은 데이터</h2>
<h2>name: {{ $route.params.name }}</h2>
<h2>age: {{ $route.params.age }}</h2>
<h2>props로 받은 데이터</h2>
<h2>name: {{ name }}</h2>
<h2>age: {{ age }}</h2>
</div>
</template>
<script>
export default {
name: 'Params',
props: {
name: {
type: String,
default : ''
},
age: {
type: Number,
default: 0
}
}
}
</script>
WARNING |
Params 방식을 사용할 경우 주의할 점은, named paths를 사용해야 정상적으로 작동한다. 1 |
Example
<template>
<div>
main
<ul>
<li @click="clickList">
Query 프로그래밍 방식
</li>
<router-link :to="{name: 'Query', query: {name: 'cat', age: 3}}">
Query 선언적 방식
</router-link>
<li @click="clickParams">
params
</li>
<router-link :to="{name: 'Params', params: {name: 'dog', age:4}}">
params 선언적 방식
</router-link>
</ul>
</div>
</template>
<script>
export default {
name: 'Main',
methods: {
clickList () {
this.$router.push({name: 'Query', query: {name: 'cat', age: 3}})
},
clickParams () {
this.$router.push({name: 'Params', params: {name: 'dog', age:4}})
}
}
}
</script>
HTML5 히스토리 모드
이러면 해시뱅(#
)이 사라진다.
히스토리 모드에서 배포시 유의 사항
$URL/app/
로 배포할 경우 모든 절대 경로 라우트도 위의 경로로 바꿔야 한다. vue.config.js 파일의 publicPath
변수를 참고하면 된다. 예를 들면 다음과 같이 하면 된다.
export const PUBLIC_PATH = require('@/../vue.config').publicPath;
export const rootChildren = [
{
path: PUBLIC_PATH + 'init',
component: Init,
name: rootNames.init,
},
// ...
];
HTML5 히스토리 모드 # 서버 설정 예제를 보면 알게 되지만, 모든 라우팅 경로는 index.html
파일로 전달해야 한다.
Apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
nginx
Native Node.js
const http = require('http')
const fs = require('fs')
const httpPort = 80
http
.createServer((req, res) => {
fs.readFile('index.html', 'utf-8', (err, content) => {
if (err) {
console.log('We cannot open "index.html" file.')
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8'
})
res.end(content)
})
})
.listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
})
aiohttp
aiohttp의 경우 aiohttp.web.static
함수를 다음과 같이 사용할 수 있지만...
from aiohttp import web
# ...
app = web.Application()
app.router.add_routes([
web.static('/www/deploy/path')
])
$URL/index.html
이 아닌, $URL/$routingPaths
방식으로 라우팅 되므로 위의 방식을 사용할 수 없다. 무엇보다 aiohttp.web.static
함수는 $URL/
경로로 라우팅할 경우 디렉토리 리스팅으로 바뀌기 때문에 사용 해선 안된다. (vue-router 에서 /
로 라우팅 할 수 있기 때문이다.)
참고로 다음의 예제처럼...
from aiohttp import web
# ...
app = web.Application()
app.router.add_routes([
web.get('/{tail:.*}', self.on_default_route),
web.static('/', '/www/deploy/path'),
])
'/{tail:.*}'
와 같이 regex를 사용한 방법도 결국 문제가 생기더라...
devServer
webpack-dev-server 이다. 참고로 vue-cli의 개발용 서버는 이 것을 사용한다.
라우트-뷰 이벤트 획득
권한에 따른 접근 제한
- 권한에 따른 vue router 접근 제한 걸기
- Stackoverflow - Vue JS permissions on routes
- Vue.js - Role Based Authorization Tutorial with Example
- Vue Js Role-Based Access Control with CASL Library
replace
Troubleshooting
Not found
아래와 같이 라우팅 옵션을 설정했다.
export const routes = [
{
path: 'init',
component: Init,
name: 'init',
},
{
path: 'signin',
component: Signin,
name: 'signin',
},
{
path: '*',
component: Error,
}
];
Signin 접속시 초기화가 안되있을 경우 Init 으로 이동하도록 설정했을 때, http://localhost:8080/signin
로 접속해도 계속 Error 페이지로 이동한다.
이 경우, path
속성을 'init'
이 아닌, '/init'
으로 하면 정상적으로 작동한다.
Avoided redundant navigation to current location
라우팅시 다음과 같은 에러가 발생될 수 있다.
vue-router.esm.js?8c4f:2065 Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/main/projects".
경로가 같을 경우 발현되는 에러이다. 경로가 같은지 우선 확인한 후 라우팅하면 된다.
의 기본 페이지 설정
children
경로가 있는 컴포넌트 페이지는
이 경우, 다음과 같이 path
가 빈 문자열, redirect
로 children
중 하나로 이동되도록 설정할 수 있다.
NavigationDuplicated
import Vue from 'vue';
import VueRouter from 'vue-router';
import { UserService } from './../service/user.service';
import store from './../store';
Vue.use(VueRouter);
...(생략)
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => {
if (err.name !== 'NavigationDuplicated') throw err;
});
};
export default router;
또는 현재 경로 (this.$router.currentRoute.path
)를 체크해서 이동하면 된다.
moveTo(location: RawLocation) {
if (this.$router.currentRoute.path === location.toString()) {
return;
}
this.$router.push(location).catch((reason: any) => {
if (reason.name !== 'NavigationDuplicated') {
throw reason;
}
})
}
Named Route 'Main' has a default child route.
vue-router.esm.js?8c4f:16 [vue-router] Named Route 'Main' has a default child route. When navigating to this named route (:to="{name: 'Main'"), the default child route will not be rendered. Remove the name from this route and use the name of the default child route for named links instead.
위와 같은 에러가 발생된 경우 라우트를 설정한 곳을 확인해 보자.
export const Routes = [
{
path: '/main',
component: Main,
name: 'Main',
meta: {requiresAuth: true},
children: [
{
path: '',
redirect: 'dashboard',
},
{
path: 'dashboard',
component: MainDashboard,
name: Names.mainDashboard,
},
],
},
...
];
위와 같이 children
속성이 있는 경로는 name
속성을 제거하고, 기본 라우팅 경로 (children
라우트 중 path: ''
인 경로)에 name
속성을 추가하면 된다.
올바른 결과는 다음과 같다.
export const Routes = [
{
path: '/main',
component: Main,
meta: {requiresAuth: true},
children: [
{
path: '',
name: 'Main',
redirect: 'dashboard',
},
{
path: 'dashboard',
component: MainDashboard,
name: 'MainDashboard',
},
],
},
...
];
How to remove hashbang (#
)
히스토리 모드를 사용하면 된다.
See also
- vue
- Vue:render - render 함수.
Favorite site
- 소개 | Vue Router
- Vue-Router 살펴보기
- Vue.js에 VueRouter로 라우팅 하기 - 치치스페이스
- 수동 라우팅 - NativeScript-Vue
- (Vue) Vue Router 개념 및 설치