---
# System prepended metadata

title: '[ 想入門，我陪你 ] Re Vue 重頭說起｜Day 16：前端路由基礎與動態參數'
tags: [Alex 宅幹嘛, Vue, 'Re:Vue 重頭說起']

---

# [ 想入門，我陪你 ] 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