---
# System prepended metadata

title: '[ 想入門，我陪你 ] Re Vue 重頭說起｜Day 19：路由器進階功能應用'
tags: [Alex 宅幹嘛, Vue, 'Re:Vue 重頭說起']

---

# [ 想入門，我陪你 ] Re Vue 重頭說起｜Day 19：路由器進階功能應用

###### tags: `Vue`、`Re:Vue 重頭說起`、`Alex 宅幹嘛`

## [Passing Props to Route Components](https://router.vuejs.org/guide/essentials/passing-props.html#boolean-mode) (13:00)

```htmlmixed=
    <body>
        <div id="app">
            <p>
                <router-link></router-link>
            </p>
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>
                <p>{{ $router.params.id }}</p>
                <p>{{ $router.query.test }}</p>
            `
        }

        const router = new VueRouter({
            routes: [
                {
                    path: '/:id',
                    componens: {
                        default: Index,
                    },
                    name: 'index'
                }
            ], 
        })
        
        new Vue({
            el: '#app',
            router,
        })
    </script>
```

Vue 幫你自動把 $route 傳給 component，讓你方便取道 query 與 params

:::info
Using `$route` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain URLs.
如果你在component內用到 `$route`參數 代表 這個component只能for這個 `$route`的 url 做使用，有此參數就不用把 router的狀態根vuex做同步
:::

$router 要把component與router做結耦

$router 是Component向上要資料
props 是 傳資料進 component

### Boolean mode
```htmlmixed=
    <body>
        <div id="app">
            <p>
                <router-link></router-link>
            </p>
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>
                <p>{{ $router.params.id }}</p>
                <p>{{ $router.query.test }}</p>
            `,
            // 簡易模式
            props: ['id', 'test'],
        }

        const router = new VueRouter({
            routes: [
                {
                    path: '/:id',
                    componens: {
                        // props 傳 id 進去
                        default: Index,
                    },
                    name: 'index'
                    // 新增 Boolean mode => the route.params will be set as the component props.
                    props: { default: true }
                }
            ], 
        })
        
        new Vue({
            el: '#app',
            router,
        })
    </script>
```

結果 query 拿得到，params 拿不到
![](https://i.imgur.com/EZ3rdQv.png)


### Object mode
```htmlmixed=
    <body>
        <div id="app">
            <p>
                <router-link></router-link>
            </p>
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>
                <p>{{ $router.params.id }}</p>
                <p>{{ $router.query.test }}</p>
            `,
            // 簡易模式
            props: ['id', 'test'],
        }

        const router = new VueRouter({
            routes: [
                {
                    path: '/:id',
                    componens: {
                        default: Index,
                    },
                    name: 'index'
                    // 新增 Object mode =>  Useful for when the props are **static**.
                    props: { 
                        default: {
                            id: 'aaa',
                            test: '123', 
                        }
                    }
                }
            ], 
        })
        
        new Vue({
            el: '#app',
            router,
        })
    </script>
```

> 用 props 好處，只告我自己的資料與別人給的props，這樣可以降低結耦合(Ex: 每一個模組都讀 router 與 vuex 會提高結耦合)

### Function mode (26:15)


```htmlmixed=
    <body>
        <div id="app">
            <p>
                <router-link></router-link>
<!--Index 模組是耦合的，也可以給自己塞參數用  -->
                <index uid="Alex" :test="666"></index>
            </p>
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>
                <p>{{ $route.params.uid }}</p>
                <p>{{ $route.query.test }}</p>
                // 可以用 this 簡寫                 
                <p>{{ this.uid }}</p>
                <p>{{ this.test }}</p>
            `,
            // 簡易模式
            props: ['id', 'test'],
        }

        const router = new VueRouter({
            routes: [
                {
                    path: '/:uid',
                    componens: {
                        // Index 模組是耦合的，可以給 router 獨立使用
                        default: Index,
                    },
                    name: 'index'
                    // 新增 Function mode => This allows you to cast parameters into other types, combine static values with route-based values
                    // 當 改 url 的 params與query時，模組的props也會自動跟著修改
                    props: { 
                        default: (route) => ({
                            uid: route.params.uid,
                            test: route.query.test,                   
                        }
                    })
                }
            ], 
        })
        
        new Vue({
            el: '#app',
            router,
            // 註冊 Index 模組
            components: {
                Index
            }
        })
    </script>
```

> 此法較佳，index模組不限定只能在router或是哪個網址才能使用，只要有傳 id 死 test 都可以使用

![](https://i.imgur.com/ScPAKAc.png)

##[ HTML5 History Mode](https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations) (33:45)

History mode > Hash mode 
1. 網址好看
2. 矛點功能的 # 與 Hash mode 的 # 有衝突
3. vue cli 可以用 History mode 因為 server 是 vue cli 幫你起的，所以他會幫你把每個位置都指向 index.html，Life server server端沒當你做，所以無法使用
4. 圖文描述(38:30)

41:10 預設是 hash mode，要用 history mode要對 server做處理

如果要使用 history mode，要請後端把所有頁面的route交由前端處理，並都指向同一個檔案 index.html page，

處理server的方式如下：

#### 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

```
location / {
  try_files $uri $uri/ /index.html;
}
```

#### #Native Node.js

```javascript=
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)
})
```

#### Express with Node.js

For Node.js/Express, consider using [connect-history-api-fallback middleware](https://github.com/bripkens/connect-history-api-fallback).

#### Firebase hosting

Add this to your `firebase.json`:

```json=

  "hosting": {
    "public": "dist",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}
```

#### Caveat
因為任何頁面都會導來我們這邊，所以 server 不會處理，都是 200 OK，所以前端要處理 router 404 
```javascript=
const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '*', component: NotFoundComponent }
  ]
})
```

更多 Vue SSR 知識：https://ssr.vuejs.org/

## [Navigation Guards](https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards)

### The Full Navigation Resolution Flow

1. Navigation triggered. 按下去切換時
1. Call **beforeRouteLeave** guards in deactivated components.
1. Call global **beforeEach** guards. 進每個路由前，有任何機會決定要不要進去，針對每一個路由設定不一樣的任物
3. Call **beforeRouteUpdate** guards in reused components. (page1?id=1 -> page1?id=2 page1元件沒有變，參數換，但是模組沒換，變化第二次時觸發**beforeRouteUpdate**)
4. Call **beforeEnter** in route configs. 元件第一次進去會觸發 **beforeEnter**
5. Resolve async route components.
6. Call **beforeRouteEnter** in activated components.  (page1?id=1 -> page1?id=2 page1元件沒有變，參數換，但是模組沒換，變化第一次時觸發**beforeRouteEnter**) 
7. Call global **beforeResolve** guards.
8. Navigation confirmed.
9. Call global **fterEach** hooks.
10. DOM updates triggered.
11. Call callbacks passed to next in **beforeRouteEnter** guards with instantiated instances.

> watch query 或 hook 都可以實現功能

### Global Before Guards - beforeEach

```htmlmixed=
    <body>
        <div id="app">
            <p>
                <router-link></router-link>
                <index uid="Alex" :test="666"></index>
            </p>
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>               
                <p>{{ this.uid }}</p>
                <p>{{ this.test }}</p>
            `,
            props: ['id', 'test'],
            //模組系列
            beforeRouteEnter(to, from, next){
                console.log(to, from, next);
                next();
            },
            beforeRouteUpdate(to, from, next){
                console.log(to, from, next);
                next();
            },
            beforeRouteLeave(to, from, next){
                console.log(to, from, next);
                next();
            },
        }
        const router = new VueRouter({
            routes: [
                {
                    path: '/:uid',
                    componens: {
                        default: Index,
                    },
                    name: 'index'
                    props: { 
                        default: (route) => ({
                            uid: route.params.uid,
                            test: route.query.test,                   
                        })
                    },
                    // 確定進到 /:uid 前 要做什麼事
                    router.beforeEnter((to, from, next) => {
                        console.log(to, from, next);
                        next();
                    })
                }
            ], 
        })
        // 進到每一個路由前要做什麼事
        router.beforeEach((to, from, next) => {
            console.log(to, from, next);
            // next類似管線，這關過了，交給下一關處理，有暫停處理的效過Ex: 非同步的檢查、需要花時間的處理、轉導頁，如果不加上，頁面不會往下走
            next();
        }),
        
        router.afterEach((to, from) => {
            console.log(to, from);
        })
        new Vue({
            el: '#app',
            router,
            components: {
                Index
            }
        })
    </script>
```

match 判斷是哪些與路徑有符合搭配到，目前只偵測到一個(1:05:00 不是很懂？)
![to](https://i.imgur.com/TYkxOLg.png)
![from](https://i.imgur.com/nWrc4Dp.png)

1:09:27 Global Before Guards 文件參數講解

1:16:30 請問登入該坐在哪一個HOOK? A. beforeEach B.beforeEach C. beforeEnter Ans: 大型網站的話：beforeEach 搭配 判斷式，單頁網站就 beforeEnter

確保 next() 只被呼叫一次，要這樣寫：

```javascript=
// BAD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  // if the user is not authenticated, `next` is called twice
  next()
})

// GOOD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})
```

### Global Resolve Guards (1:19:00)

### Global After Hooks(1:21:00)

### Per-Route Guard(1:21:27)

### In-Component Guards(1:21:35)

:::danger
The **beforeRouteEnter** guard does NOT have access to **this**, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet.
:::

```javascript=
beforeRouteEnter (to, from, next) {
// 在 next 內做 callback function，可以拿到 vm 參數去 access component
  next(vm => {
  })
}
```

beforeRouteUpdate 就可以拿到 this

```javascript=
beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}

```

離開網頁時，被阻擋與詢問

```javascript=
beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}
```

## [Route Meta Fields](https://router.vuejs.org/guide/advanced/meta.html) (1:25:10)

E可以在每一個路由加一些參數與資料，例如：在beforeEach做驗證，但頁面或路由器很多，所以我們就可以針對每一個路由去判斷是否要驗證，此時可以用 meta，Ex：requiresAuth

```javascript=
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})
```

```javascript=
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // this route requires auth, check if logged in
    // if not, redirect to login page.
    if (!auth.loggedIn()) {
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // make sure to always call next()!
  }
})
```

> 動態路由 & 另一種權限管控？！

## Transitions(1:29:44)
Transitions 動態效果有時候有，有時候沒有的主要原因是 Vue 沒有辦法依照現有狀態判斷你是否有切換，是以解決辦法是要加Key，所以可以用 `$route.fullPath` 當 key ，Ex: `fullPath: /bbb?test=456`，假設在同一個company或路由當資料改變時，但不想被做動畫可以用 `$route.name` 或 `$route.path` Ex: `name: index` or `path: '/bbb'`

```htmlmixed=
    <body>
        <div id="app">
            <p>
                <transition name="index">
                  <router-view></router-view>
                </transition>
                <index uid="Alex" :test="666"></index>
            </p>
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>               
                <p>{{ this.uid }}</p>
                <p>{{ this.test }}</p>
            `,
            props: ['id', 'test'],
            //模組系列
            beforeRouteEnter(to, from, next){
                console.log(to, from, next);
                next();
            },
            beforeRouteUpdate(to, from, next){
                console.log(to, from, next);
                next();
            },
            beforeRouteLeave(to, from, next){
                console.log(to, from, next);
                next();
            },
        }
        const router = new VueRouter({
            routes: [
                {
                    path: '/:uid',
                    componens: {
                        default: Index,
                    },
                    name: 'index'
                    props: { 
                        default: (route) => ({
                            uid: route.params.uid,
                            test: route.query.test,                   
                        })
                    },
                    // 確定進到 /:uid 前 要做什麼事
                    router.beforeEnter((to, from, next) => {
                        console.log(to, from, next);
                        next();
                    })
                }
            ], 
        })
        // 進到每一個路由前要做什麼事
        router.beforeEach((to, from, next) => {
            console.log(to, from, next);
            // next類似管線，這關過了，交給下一關處理，有暫停處理的效過Ex: 非同步的檢查、需要花時間的處理、轉導頁，如果不加上，頁面不會往下走
            next();
        }),
        
        router.afterEach((to, from) => {
            console.log(to, from);
        })
        new Vue({
            el: '#app',
            router,
            components: {
                Index
            }
        })
    </script>
```

### Per-Route Transition (1:32:49)

### Route-Based Dynamic Transition (1:33:00)

把 transition 用動態綁定，然後去 watch 這個 route，也可以用 hook 去控制 route 動畫效果 Ex: beforeRouteEnter

```javascript=
<!-- use a dynamic transition name -->
<transition :name="transitionName">
  <router-view></router-view>
</transition>
```

```javascript=
watch: {
  '$route' (to, from) {
    const toDepth = to.path.split('/').length
    const fromDepth = from.path.split('/').length
    this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
  }
}
```

[Demo](https://github.com/vuejs/vue-router/blob/dev/examples/transitions/app.js)

## Data Fetching (1:33:35)

討論：在進到頁面前Fetch資料，還是在進到Component在Fetch資料？

**Fetching After Navigation**: perform the navigation first, and fetch data in the incoming component's lifecycle hook. Display a loading state while data is being fetched.先導頁，接下來再Component內透過生命週期拿資料，適合頁面專用資料

**Fetching Before Navigation**: Fetch data before navigation in the route enter guard, and perform the navigation after data has been fetched.切換路由之前，hook先做資料處理，適合做驗證打API拿資料，之後再換頁

### Fetching After Navigation

在元件的 created 做 fetchData()

```javascript=
<template>
  <div class="post">
    <div v-if="loading" class="loading">
      Loading...
    </div>

    <div v-if="error" class="error">
      {{ error }}
    </div>

    <div v-if="post" class="content">
      <h2>{{ post.title }}</h2>
      <p>{{ post.body }}</p>
    </div>
  </div>
</template>
```

```javascript=
export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    // fetch the data when the view is created and the data is
    // already being observed 一開始做第一次 fetchData
    this.fetchData()
  },
  watch: {
    // route 改變後再做一次 fetchData
    // call again the method if the route changes
    '$route': 'fetchData'
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      const fetchedId = this.$route.params.id
      // replace `getPost` with your data fetching util / API wrapper
      getPost(fetchedId, (err, post) => {
        // make sure this request is the last one we did, discard otherwise
        if (this.$route.params.id !== fetchedId) return
        this.loading = false
        if (err) {
          this.error = err.toString()
        } else {
          this.post = post
        }
      })
    }
  }
}
```

### Fetching Before Navigation

beforeRouteUpdate、beforeRouteEnter

```javascript=
export default {
  data () {
    return {
      post: null,
      error: null
    }
  },
  beforeRouteEnter (to, from, next) {
     // 非同步
    getPost(to.params.id, (err, post) => {
      // beforeRouteEnter 沒 this 所以用 vm 呼叫 setData 把資料送進去
      next(vm => vm.setData(err, post))
    })
  },
  // when route changes and this component is already rendered,
  // the logic will be slightly different.
  beforeRouteUpdate (to, from, next) {
    // beforeRouteUpdate表示我已經在原本模組裡，但是要重讀的情況下要清除舊的
    this.post = null
    getPost(to.params.id, (err, post) => {
      // 此 hook 有提供 this
      this.setData(err, post)
      next()
    })
  },
  methods: {
    setData (err, post) {
      if (err) {
        this.error = err.toString()
      } else {
        this.post = post
      }
    }
  }
}
```

QA: 1:41:28 在 route 去 access store的資料

>The user will stay on the previous view while the resource is being fetched for the incoming view. It is therefore recommended to **display a progress bar** or some kind of indicator while the data is being fetched. If the data fetch fails, it's also necessary to **display some kind of global warning message**.

## Scroll Behavior (1:45:24)

```javascript=
const router = new VueRouter({
  routes: [...],
  // 切換路由捲軸的行為
  scrollBehavior (to, from, savedPosition) {
    // return desired position
  }
})
```

這段Code沒事初始化專案時可以先寫，因為當single page切頁時，當使用者捲到某個位置，但是你切了一頁後，捲軸不回彈到最上面，會停在同一個位置，所以加了 scrollBehavior ，捲軸就會置頂

## Lazy Loading Routes(1:46:36)

搭配 webpack 做延遲載入

```javascript=
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
```
> webpackChunkName 不要亂命名，webpackChunkName名字要一樣 1:47:48

1:50:05 分配路由的經驗

## Navigation Failures (1:50:40)
如果用 `router-link` component 不會出錯，使用 `router.push` or `router.replace` 就會出錯(`Uncaught (in promise) Error`)



```htmlmixed=
    <body>
        <div id="app">
            <p>
                <router-link></router-link>
            </p>
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>
                <p>{{ $router.params.id }}</p>
                <p>{{ $router.query.test }}</p>
            `
        }

        const router = new VueRouter({
            routes: [
                {
                    path: '/:id',
                    componens: {
                        default: Index,
                    },
                    name: 'index'
                }
            ], 
        })
        
        new Vue({
            el: '#app',
            router,
        })
    </script>
```

來看看 router-link 與 button 的差別
```htmlmixed=
    <body>
        <div id="app">
            <!-- 差別： 導頁的時候不使用 router-link，反而用程式去做要注意必須自己去控管此行為(已經在/bbb，你卻點擊 bbb Common Button) -->
<!-- router-link的優勢是他會自動般你判斷同頁面的錯誤，除此之外他會搭配 class的變化樣式  -->
            <router-link to="/bbb">Link Button</router-link>
            <button @click="router.push('bbb')">Common Button</button>
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>
                <p>{{ $router.params.id }}</p>
                <p>{{ $router.query.test }}</p>
            `,
            // 簡易模式
            props: ['id', 'test'],
        }

        const router = new VueRouter({
            routes: [
                {
                    path: '/:id',
                    componens: {
                        // props 傳 id 進去
                        default: Index,
                    },
                    name: 'index'
                    // 新增 Boolean mode => the route.params will be set as the component props.
                    props: { default: true }
                }
            ], 
        })
        
        new Vue({
            el: '#app',
            router,
        })
    </script>
```

router-link 是可以指定 tag 的，所以不需要很複雜的判斷式

```htmlembedded=
 <router-link to="/bbb" tag="span">Link Button</router-link>
```

> tag 預設是 <a> 有 href 屬性支援，若不是 <a> href 會自動被抽調，但是原本class樣式會保留

可以這樣設計動態切頁按鈕：router 是 /bbb 按鈕可以按，反之，按鈕無法按 (1:58:06)

```htmlembedded=
 <router-link to="/bbb" tag="$route.params.uid !== 'bbb' ? 'a' : 'span'">Link Button</router-link>
```

> 如果用 <button> 做此功能判斷式會比較麻煩

### Detecting Navigation Failures (1:59:00)

很少人用此功能，此功能是你要如何去接這個錯誤，或對錯誤行為做處理(Ex：把按鈕關掉/做錯誤擷取)，但push新的頁面時，可以把錯誤catch起來

Navigation Failures are Error instances with a few extra properties. To check if an error comes from the Router, use the isNavigationFailure function:(2:13:18)

```javascript=
import VueRouter from 'vue-router'
const { isNavigationFailure, NavigationFailureType } = VueRouter

// trying to access the admin page
router.push('/admin').catch(failure => {
  // 換頁出錯時，確定是因為 Navigation 相關的錯誤就會進到 catch
  if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
    // show a small notification to the user
    showToast('Login in order to access the admin panel')
  }
})
```

有一個網友提出他們家的做法：把原本的push做調整，加上了 catch 共用化，任何錯誤都進入 catch
```javascript=
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function(location) {
    return originalPush.call(this, location).catch((err) => err));
}
```

### Navigation Failures's properties (2:04:45)

錯誤可以透過 `to`與`form`參數觀察

```htmlmixed=
    <body>
        <div id="app">
            <button @click="change"></button>          
        </div>
    </body>
    <script>   
        const Index = { 
            template: `
                <div>Index</div>
                <p>{{ $router.params.id }}</p>
                <p>{{ $router.query.test }}</p>
            `,
        }

        const router = new VueRouter({
            routes: [
                {
                    path: '/:id',
                    componens: {
                        default: Index,
                    },
                    name: 'index'
                }
            ], 
        })
        
        new Vue({
            el: '#app',
            router,
            methods: {
                change() {
                    this.$router.push('/bbb').catch(err => {
                        // isNavigationFailure有兩個參數，第一個error是把錯誤丟進去給他確認
                        console.log(VueRouter.isNavigationFailure(failure, checkType)) // true，代表確實有錯，但是console不會噴紅字，而是觸發你的 callback
                        // 如果它不是我們在轉導頁時因為Vue造成的錯誤，我們就再繼續丟出來，讓紅字出現，這樣比較好 DEBUG
                        if (VueRouter.isNavigationFailure(err)) {
                            throw err;
                         }
                    })
                },
                // 可以針對錯誤類別做不同的 Error handling
                checkType() {
                
                }
            },
        })
    </script>
```

> TIP: 如果你省略 isNavigationFailure 第一個的參數 failure，他只會幫你確定這是不是Navigation的錯誤，不會幫你分類類別(NavigationFailureType)

#### NavigationFailureType: (2:12:12)

```
* redirected: next(newLocation) was called inside of a navigation guard to redirect somewhere else. next
* aborted: next(false) was called inside of a navigation guard to the navigation.
* cancelled: A new navigation completely took place before the current navigation could finish. e.g. router.push was called while waiting inside of a navigation guard.
* duplicated: The navigation was prevented because we are already at the target location
```

Ex: 剛剛例子在 /bbb 下，點擊 button 進入 /bbb 就會噴 duplicated type 的路由 error，如下圖
![](https://i.imgur.com/qQoRvD6.jpg)
