# [ 想入門,我陪你 ] Re Vue 重頭說起|Day 16:前端路由基礎與動態參數
###### tags: `Vue`、`Re:Vue 重頭說起`、`Alex 宅幹嘛`
## Getting Started (16:00)
### HTML
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Day 16</title>
</head>
<body>
<div id="app">
<!-- <div v-if="show === 1">Page 1</div> -->
<!-- div v-if="show === 2">Page 2</div> -->
<p></p>
<p>
<a href="/page1">A Tag - Page 1</a>
<a href="/page2">A Tag - Page 2</a>
<router-link to="/page1">Page 1</router-link>
<router-link to="/page2">Page 2</router-link>
</p>
<!-- 負責顯示路由內容在畫面 -->
<router-view></router-view>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/"></script>
<!-- 載入 cdn 網址會被加上# -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.x.x/"></script>
<script>
<!-- 用 template 製作頁面 -->
const Page1 = { template: '<div>Page1</div>' }
const Page2 = { template: '<div>Page2</div>' }
// 定義好 router 模組
const router = new VueRouter({
routes: [
{
path: '/page1',
component: Page1,
},
{
path: '/page2',
component: Page2,
},
],
})
new Vue({
el: '#app',
// 掛載到Vue,vue-devtool可以讀取到網址路由狀態
router,
data() {
return {
show: 1,
}
}
})
</script>
</body>
</html>
```
> vue-router 關鍵字: router(實體、操縱、控制路由) vs route(讀取、記錄、分配資料)
> 用 a tag 要自己分辨 hash/history 模式,router-link 會自動幫你加上 **.router-link-exact-active**
### Javascript
$vm0 = this,操作路由實體,可以在 vue-devtool 內 console看看 $vm0.$router 與 $vm0.$route 有什麼差別
![](https://i.imgur.com/55iRyVM.png)
![](https://i.imgur.com/wDVxcat.png)
![](https://i.imgur.com/1ePoOc1.png)
> 此節告訴你可以用 router 去做上下一頁,或是讀取query..等參數可以使用
:::warning
Notice that a ``<router-link>`` automatically gets the `.router-link-active` class when its target route is matched. You can learn more about it in its API reference.
:::
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Day 16</title>
</head>
<style>
.router-link-active {
background: black;
color: white;
};
</style>
<body>
<div id="app">
<!-- <div v-if="show === 1">Page 1</div> -->
<!-- div v-if="show === 2">Page 2</div> -->
<p>
<router-link to="/">Index</router-link>
<router-link to="/page1">Page 1</router-link>
<router-link to="/page2">Page 2</router-link>
</p>
<!-- 負責顯示路由內容在畫面 -->
<router-view></router-view>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/"></script>
<!-- 載入 cdn 網址會被加上# -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.x.x/"></script>
<script>
<!-- 用 template 製作頁面 -->
const Index = { template: '<div>Index</div>' }
const Page1 = { template: '<div>Page1</div>' }
const Page2 = { template: '<div>Page2</div>' }
// 定義好 router 模組
const router = new VueRouter({
routes: [
{
path: '/',
component: Index,
},
{
path: '/page1',
component: Page1,
},
{
path: '/page2',
component: Page2,
},
],
})
new Vue({
el: '#app',
// 掛載到Vue,vue-devtool可以讀取到網址路由狀態
router,
data() {
return {
show: 1,
}
}
})
</script>
</body>
</html>
```
當進入首頁時,Index是黑底白字,正常,但切換到 Page 1 時,Index的黑底白字沒有消失
![](https://i.imgur.com/lvmhzWZ.png)
> **.router-link-active** 是完整 router active 時有經過的(因為 Index是 '/' ,而且Page1的 '/page1' 也算是在 '/' 之下),所以要改用**.router-link-exact-active**才會修改好
> 麵包屑製作可以適用 **.router-link-active**
## Dynamic Route Matching(56:00)
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Day 16</title>
</head>
<style>
.router-link-active {
background: black;
color: white;
};
</style>
<body>
<div id="app">
<!-- <div v-if="show === 1">Page 1</div> -->
<!-- div v-if="show === 2">Page 2</div> -->
<p>
<router-link to="/">Index</router-link>
<router-link to="/page1">Page 1</router-link>
<router-link to="/page2">Page 2</router-link>
</p>
<router-view></router-view>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.x.x/"></script>
<script>
const Index = { template: '<div>Index</div>' }
const User = { template: '<div>User {{ $route.params.id }}</div>' }
// 定義好 router 模組
const router = new VueRouter({
routes: [
{
path: '/',
component: Index,
},
{
path: '/user/:id',
component: User,
},
],
})
new Vue({
el: '#app',
// 掛載到Vue,vue-devtool可以讀取到網址路由狀態
router,
data() {
return {
show: 1,
}
}
})
</script>
</body>
</html>
```
![](https://i.imgur.com/gPCwrnx.jpg)
>新聞網站每篇新聞有id編號,user可以透過id打API把資料拿回來
### Reacting to Params Changes (1:21:30)
偵聽路由參數改變,去調整程式
當地一次符合路由器時,Vue Router 幫你去 render 這個 Component,可是這個Component很多事情,例如打API...
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Day 16</title>
</head>
<body>
<div id="app">
<p>
<router-link to="/">Index</router-link>
<router-link to="/page1">Page 1</router-link>
<router-link to="/page2">Page 2</router-link>
</p>
<router-view></router-view>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.x.x/"></script>
<script>
const Index = { template: '<div>Index</div>' }
const User = {
template: '<div>User {{ $route.params.id }}</div>',
mounted() {
setTimeout(function() {
window.alert('API!!!')
}, 1000)
}
}
const router = new VueRouter({
routes: [
{
path: '/',
component: Index,
},
{
path: '/user/:id',
component: User,
},
],
})
new Vue({
el: '#app',
router,
data() {
return {
show: 1,
}
}
})
</script>
</body>
</html>
```
> 第一次進 user/123 ,成功打API顯示 alert,但是再次切到 user/456,API卻沒打,沒有 alert,因為路由更新了,但是 Component 沒有更新,
> 路由規則: 符合路由規則(path)的都是要顯示 User component,可是第一次已經符合了,Vue已經幫你把User放上去了,所以第二次轉換時,Component的mounted不觸發
> 解法1. 可以透過 watch 觀測 router 有沒有變化,去決定要不要打 API
> 解法2. 用 router 專屬的 lifeCycle => beforeRouterUpdate,如果router參數變了,做事情
ASK Question
1. user/123 -> user/profile/123 會不會觸發 API alert?(1個路由)
```javascript=
const router = new VueRouter({
routes: [
{
path: '/user/(profile/)?:id(\\d+)',
component: User,
},
],
})
```
> 不會,因為已經進到同一個路由,第二次不會 mounted
2. user/123 -> user/profile/123 會不會觸發 API alert?(2個路由)
```javascript=
const router = new VueRouter({
routes: [
{
path: '/user/:id(\\d+)',
component: User,
},
{
path: '/user/profile/:id(\\d+)',
component: User,
},
],
})
```
> 不會,因為已經進到同一個路由,第二次不會 mounted(Doc沒寫)
如下圖進行驗證:
![](https://i.imgur.com/PTTgqO1.jpg)
![](https://i.imgur.com/VFB5gaS.png)
![](https://i.imgur.com/CfYHk4v.png)
> 模組其實沒因為路由切換而改變,因為它仍是同一個User Component,Vue把模組掛上去後,就不幫你換了,為了效能控制,去限制使用者在router上會發生的效能問題
### Catch all / 404 Not found Route(59:40)
假設試用者亂打網址,畫面就沒了,因為路由的概念是從上到下去判斷符不符合,所以通常我們會有2種做法
```javascript=
const User = { template: '<div>404 Not Found</div>' }
const router = new VueRouter({
routes: [
{
path: '/',
component: Index,
},
{
path: '/user/:id',
component: User,
},
{
path: '*',
component: Error,
},
],
})
```
> 亂打網址時,瀏覽器會保留錯誤網址,畫面呈現 404 Error Component
```javascript=
const User = { template: '<div>404 Not Found</div>' }
const router = new VueRouter({
routes: [
{
path: '/',
component: Index,
},
{
path: '/user/:id',
component: User,
},
{
path: '/404',
component: Error,
},
{
path: '*',
redirect: /404,
},
],
})
```
> 亂打網址時,瀏覽器幫我們打到 /404 路由
### Advanced Matching Patterns(精華)
[path-to-regexp](https://github.com/pillarjs/path-to-regexp/tree/v1.7.0)
[this example](https://github.com/vuejs/vue-router/blob/dev/examples/route-matching/app.js)
```javascript=
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/' },
{ path: '/params/:foo/:bar' },
// 在參數後面加 ?
{ path: '/optional-params/:foo?' },
// 通常拿參數網址是需要打 api ,但有api接資料,有時候會告訴使用者id是數字還是純字串,所以可以使用 (\\d+) 做基本篩選
{ path: '/params-with-regex/:id(\\d+)' },
// 可以特定path 後再接 *
{ path: '/asterisk/*' },
// 它可以讓有一段路由是非必填,所以在很多共用網址或是不同網址指向同一component時,就可以使用這招
{ path: '/optional-group/(foo/)?bar' }
]
})
```
id是數字就進去 User Component,若不是數字就進到 /404 頁
```javascript=
const User = { template: '<div>404 Not Found</div>' }
const router = new VueRouter({
routes: [
{
path: '/',
component: Index,
},
{
path: '/user/:id(\\d+)',
component: User,
},
{
path: '/404',
component: Error,
},
{
path: '*',
redirect: /404,
},
],
})
```
> d是數字,+是1次或1次以上
/user/1 可以進到 user 頁
/user/profile/1 也可以進到 user 頁
```javascript=
const User = { template: '<div>404 Not Found</div>' }
const router = new VueRouter({
routes: [
{
path: '/',
component: Index,
},
{
path: '/user/(profile/)?:id(\\d+)',
component: User,
},
// {
// path: '/user/:id(\\d+)',
// component: User,
// },
// {
// path: '/user/profile/:id(\\d+)',
// component: User,
// },
{
path: '/404',
component: Error,
},
{
path: '*',
redirect: /404,
},
],
})
```
> ? 代表非必填
### Matching Priority