> ## Content Index
> Fetch the complete content index at: https://blog.hinablue.me/llms.txt
> Use this file to discover other available public pages before exploring further.

# [IT 鐵人賽] Router 進階應用 Day 10
- URL: https://blog.hinablue.me/2019-ithome-ironman-day-10/
- Published: 2019-09-12T02:11:00.000Z
- Updated: 2026-07-06T18:45:09.000Z
- Author: Hina Chen
- Tags: 2019, ITHome, 鐵人賽, VueJS, Vue-Router, VueJS 2.0, JavaScript, 前端路由, 路由

聊完了基礎的路由功能後，我們接著來看看關於路由的進階應用方式。你放心，前陣子一直提到的動態元件載入，這邊一樣會出現，所以先給各位打個預防針。不過，如果你真的看膩了什麼動態載入的事情，那麼你想要跳過我也是欣然接受。

何況，~~進來就進來，出去就出去，~~ 哪有人一直動態載入的啦。

---

## 路由守衛方法（ *Guards Methods* ）

> [Navigation Guards](https://router.vuejs.org/guide/advanced/navigation-guards.html?ref=blog.hinablue.me)

在 Router 當中提供了幾種所謂的路由守衛方法：

*其實我覺得叫路由防衛比較好（~~聽起來超中二的~~）*

- `beforeEach` 每個路由要被執行之前，都會先經過這裡，他會傳入三個參數：  
  - `to` 你要去的路由位置。
  - `from` 你從哪一個路由位置進來，如果沒有，預設是 `null`。
  - `next()` 繼續往下執行的回呼函式，你必須要呼叫他才能繼續執行。
- `beforeResolve` 當路由與所搭配的元件都被解析後，會執行這個函式，傳入參數跟 `beforeEach` 一樣，功能也一樣。
- `beforeEnter` 用於 Route 設定的地方，傳入參數跟 `beforeEach` 一樣，功能也一樣。

首先我們說明一下 `next()` 這個回呼函式，基本上無論你在哪一個階段，只要 `next()` 沒有被呼叫到，那麼你的路由基本上就會被中斷。而這個 `next()` 可以接受的呼叫方法有：

- `next()` 就這樣，很單純的呼叫。
- `next(false)` 代表中斷這個路由執行，畫面就會停住了。
- `next({ name: 'HelloKitty' })` 代表前往路由名稱為 HelloKitty 的地方。這種呼叫方式可以套用 `<router-link>` 的 `to` 的屬性，可以參考我昨天的文章 [Router 基本入門 Day 9](https://blog.hinablue.me/2019-ithome-ironman-day-9/)。
- `next(Error)` 傳入一個 Error 的實例，該路由會被中斷，然後發一個事件給 `router.onError()`。

至於剛剛說的 `beforeEnter` 的使用方式，是在 Route 設定的時候：

```js
const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/helloworld',
      component: HelloWorld,
      beforeEnter (to, from, next) {
        next();
      }
    }
  ]
});

```

---

然後在元件裡面，當你使用了 Vue Router 的時候，在整個生命週期當中，也提供了三組方法可以使用：

- `beforeRouteEnter` 路由尚未進入該元件時會執行，一樣會三個傳入三個參數：  
  - `to` 你要去的路由位置。
  - `from` 你從哪一個路由位置進來，如果沒有，預設是 `null`。
  - `next()` 繼續往下執行的回呼函式，你必須要呼叫他才能繼續執行。
- `beforeRouteUpdate` 當此元件被重複使用時，路由有更新，會呼叫這個方法，參數跟 `beforeRouteEnter` 相同。
- `beforeRouteLeave` 當路由要離開該元件時，會呼叫這個方法，參數跟 `beforeRouteEnter` 相同。

以上三組沒有太大的區別，唯一有差異的是 `beforeRouteEnter`，由於他是在路由進入點之前，也就是說在路由尚未進入，所以他沒有 `this` 可以用，而其他兩個，在方法中的 `this` 均指向元件實體。

而 `beforeRouteEnter` 若要拿到元件實體，必須使用 `next()` 來實作：

```js
export default {
  name: 'HelloWorld',
  beforeRouteEnter (to, from, next) {
    next(vm => {
      // 這裡的 vm 就是 HelloWorld 這個元件的實體。
    })
  }
}

```

---

或許你會問說，這些東西到底有什麼好處？為何要叫做 *路由守衛* ？我們底下舉出一些例子，你思考一下當你要做這些事情的時候，你會怎麼做？

1. 你的路徑是需要檢查登入的時候。
2. 你必須要檢查相對應資料是否存在時。
3. 你在頁面開始之前需要做非同步操作。
4. 你想讓特定來源走向不同路由。
5. ~~你想在每個頁面都加上蓋版廣告。~~

我們以登入檢查為例子，如果你沒有這些方法，那麼你勢必要在每個路徑所經過的地方，都要綁上一個檢查的步驟。這樣其實在後續維護上就會變得相當棘手。而，`beforeEach`，`beforeResolve`，`beforeEnter` 或是 `beforeRouteEnter` 就可以幫助你做掉這件事情。

詳細的流程可以參考官方說明：

> [The Full Navigation Resolution Flow](https://router.vuejs.org/guide/advanced/navigation-guards.html?ref=blog.hinablue.me#the-full-navigation-resolution-flow)

當然，你可能不會想放在 `beforeRouteEnter`，這樣等於每個元件都要做。所以，我們以 `beforeEnter` 為例子，就可以這麼做：

```js
const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/need-to-login',
      component: HelloWorld,
      beforeEnter (to, from, next) {
        checkLoginStatus()
          .then(next)
          .catch(() => {
            next({ name: 'signin' })
          });
      }
    }
  ]
});

```

倘若你的 `need-to-login` 底下還有 `children` 的話，也不用擔心，因為他一定會執行。即便你是直接進入其下的路由設定，他的上游的動作還是會被執行的。

不過，請注意一點，由於路由執行的順序，會先走過一次 `beforeRouteLeave`，所以，如果你在這邊有做什麼 *登出* 的動作，那麼你可能每次登入，換頁後就會一直被彈到登入頁面。

另外一個方式，是可以放在最外層的 `beforeEach` 的地方，作法大同小異，但為了識別 Route，所以你可以使用 `meta` 來儲存一些識別用資料，舉例來說：

```js
const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/need-to-login',
      component: HelloWorld,
      meta: {
        needLogin: true
      }
    }
  ]
});

router.beforeEach(function(to, from, next) {
  if (to.meta.needLogin === true) {
    return checkLoginStatus()
      .then(next)
      .catch(() => {
        next({ name: 'signin' })
      });
  }
  next();
});

```

但是！要注意一點，由於 `beforeEach` 並不會幫你做麼上游的動作，他是 *每一個* 路由進入之前會做一些事情，所以當你的 `need-to-login` 其下有子路由，那麼也一定要加上相同的 `meta` 來儲存識別資料，否則，你就會跑出一個漏洞了。

![](https://blog.hinablue.me/content/images/2019/09/ithome_ironman_2019_day_10_1.png)

![](https://blog.hinablue.me/content/images/2019/09/ithome_ironman_2019_day_10_2.png)

---

## 動態載入又來了

既然，這個方法可以 *阻擋* 路由實例的流程，那麼我們在阻擋的同時，是不是可以拿來做其他的事情？當然，可以，不然我就不會又跳出來講動態載入了。

> 如果聽膩的人左轉沒關係，謝謝。
> 
> [這裡我有寫一點邪魔歪道](https://blog.hinablue.me/vuejs-guan-yu-functional-component-de-xie-men-wai-dao/) 心臟比較大顆的可以試試看。

是的，就如同之前提到的 `import`，Vue Router 本身也支援懶加載這件事情，所以，我們在載入元件的時候，可以這麼做：

```js
const HelloWorld = () => import('@/components/HelloWorld.vue')

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/helloworld',
      component: HelloWorld,
      beforeEnter (to, from, next) {
        next();
      }
    }
  ]
});

```

你在開發模式其實不會有感覺，當你運行 `yarn build` 將正式環境打包後，你會發現你使用 `import` 的一些模組，會被分割成不同的 `.js` 檔案。而這些檔案會在 *需要的時候* 才會被載入到前端，用以實現懶加載這件事情。

當然，這也是相當好用的方法。不過，今天如果我連 *元件本身* 都還不確定存不存在呢？

> 請回想一下 [Component 魔術方法 Day 5](https://blog.hinablue.me/2019-ithome-ironman-day-5/)
> 
> 還有 [Component 魔術方法 Day 6](https://blog.hinablue.me/2019-ithome-ironman-day-6/)

所以，我們可以這麼做：

- 利用 XHR 把檔案讀取進來。
- 利用 Promise 把元件餵給 `component`。
- 利用 `Vue.options` 來配發 `component`。
- 利用 JavaScript 特性來更改 `component`。
- 利用剛剛的邪魔歪道餵入 Functional Component。

有沒有發現，其實到最後都是一種 **邪魔歪道** 的風格。

> 沒辦法，我長得不正。

我這邊不會在提 XHR, Promise 與 `Vue.options` 怎麼做了，我們就來講一個，利用 JavaScript 語言本身的特（缺）性（陷），來更改 `components`。我這邊先上 Code 與執行結果，但是不要問我為什麼會這樣！

```js
import Router from 'vue-router';
import HelloKitty from '@/components/HelloKitty.vue';

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/helloworld',
      component: null,
      beforeEnter (to, from, next) {
        to.matched[0].components.default = HelloKitty;
        next();
      }
    }
  ]
});

```

![](https://blog.hinablue.me/content/images/2019/09/ithome_ironman_2019_day_10_3.png)

![](https://blog.hinablue.me/content/images/2019/09/ithome_ironman_2019_day_10_4.png)

關於為何會這樣，可以參考 Kuro 講解 JavaScript 的文章。

> [JavaScript 是「傳值」或「傳址」](https://kuro.tw/posts/2017/12/08/JavaScript-%E6%98%AF%E3%80%8C%E5%82%B3%E5%80%BC%E3%80%8D%E6%88%96%E3%80%8C%E5%82%B3%E5%9D%80%E3%80%8D/?ref=blog.hinablue.me)

說句實在話，~~你是不是覺得 JavaScript 好棒棒！~~

---

## 小結

我這邊就大幅縮減動態載入的事情跟範例，反正我們之後還會繼續提到（啊哈哈哈哈）。關於路由的使用大概是這樣，如果你沒有要做什麼奇怪的事情，那麼基本上不太會用到更深的東西了。

> [官方文件](https://router.vuejs.org/?ref=blog.hinablue.me) 還是要參考一下！
> 
> [ITHome 鐵人賽同步刊登 Router 進階應用 Day 10](https://ithelp.ithome.com.tw/articles/10214740?ref=blog.hinablue.me)