###### tags: `Vue`
# Vue Router
# 0. 大綱
Vue 與以前寫的 jquery 的架構大相逕庭,jquery 所建立出來的網頁屬於多頁式網頁(MPA),而Vue所建立出來的網頁為單頁式網頁(SPA),簡單說就是進入其他網頁而網址會變動但並無跳轉的跡象,~~讓使用者產生出網頁很流暢的錯覺~~。要如何達成這個魔法,就是使用到本篇所提到的Vue-Router。
# 1. 安裝
## 1.1. 在建立專案安裝
### 1.1.1 透過Vue UI建立專案時


1. 在建立專案,選擇專案模板時,選擇”手動”,按下一步。
2. 選取Router選項即可。
3. 建立成功,即可在該專案的/src找到系統自動建立的/router/index.js。

### 1.1.2 透過指令建立專案時


1. 輸入指令建立專案後,選擇第3個選項。
2. 選擇選項”Router”。
3. 之後的部分就依個人需求自訂,
4. 建立成功,即可在該專案的/src找到系統自動建立的/router/index.js。
## 1.2. 在專案建立後加裝
### 1.2.1 透過Vue UI加入

1. 按下最左邊的第2個”插件”。
2. 找到右上角的按鈕”新增vue-router”並按下,並按下"繼續"
3. 待安裝完,即可在該專案的/src找到系統自動建立的/router/index.js。
### 1.2.2 透過指令加裝

1. 進入該專案,輸入指令:
>💡 `npm install vue-router -save`
2. 在該專案下的/src中建立一個新資料夾,命名為”router”,並在其中建立”index.js”。

3. 編輯/router/index.js,內容為:
```jsx
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../components/HelloWorld.vue'
const About = { template: '<div>About</div>'}
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
```
4. 編輯最外層的main.js,將router加入。
```jsx
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'; //新增這行
//createApp(App).mount('#app') //將這行註解或刪除
const app = createApp(App); //新增這行
app.use(router) //新增這行,將router引進
app.mount('#app') //新增這行
```
5. 編輯app.vue:
```html
<template>
<router-link to="/">Home</router-link> <!--新增這行-->
<router-link to="/about">About</router-link> <!--新增這行-->
<router-view></router-view> <!--新增這行-->
<!--<img alt="Vue logo" src="./assets/logo.png"> 刪除或註解這行-->
<!--<HelloWorld msg="Welcome to Your Vue.js App"/> 刪除或註解這行-->
</template>
<script>
import HelloWorld from './components/HelloWorld.vue' //刪除或註解這行
export default {
name: 'App',
components: {
HelloWorld //刪除或註解這行
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
```
6. 啟動後,看到此畫面即成功。

# 2. 介紹功能
## 2.1 建立Router
```jsx
const router = createRouter({
history: createWebHashHistory(),
routes
})
```
- 使用`createRouter(options)`來建立Router
- 主要常用的options有:
- history :
- `createWebHashHistory()`
- `createWebHistory()`
- routes : Router的初始路由列表,細節留至下一小節詳細說明。
## 2.2 routes
- routes為初始的路由列表,之後若要再新增其他路由可有其他指令(ex: `addRoute`)辦到。
```jsx
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
}
]
```
- 通常會有很多路由,所以會獨立提出來寫,而非直接寫在`createRouter`裡
- 每個route中常用的有:
- **path**: 進入該頁面的路徑。
- 例如: `path: ‘about’`,網頁網址為`http://localhost:12345`,則要進入此頁面則須輸入`http://localhost:12345/about`
- **name**: 該路由的名稱。
- **component**: 該路由的元件。
- 例如: 上面的Home的元件取自`import Home from '../components/HelloWorld.vue’`,則當進入home頁面時,`HelloWorld.vue`就會載入至`<router-view></router-view>`
- 除了先將元件載入再指派給route,也可等進入該頁面在加載該元件的方法。
- 例如: `component: ()⇒import('../components/HelloWorld.vue’)`。
- **meta**: 自定義的參數
- 例如: 該頁面的title,index等,並使用route.meta.title等來讀取。
- ****redirect: 進入該頁面時,重定向到指定的頁面。****
- **alias**: 路由的別名。
- 例如: `path: ‘/’,alias: ‘/home’`,則使用/或/home都匯到同意頁面。
- **beforeEnter**: 進入該路由之前做某些事,待下一節說明。
# 3. 導航守衛(****Navigation Guards****)
- 分為
- beforeEach
- beforeResolve
- afterEach
- beforeEnter
- beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
## 3.1 全域守衛
### 3.1.1 beforeEach
- 使用`router.beforeEach`來使用此守衛。
```jsx
const router = createRouter({ ... })
router.beforeEach((to, from) => {
if(...){
return {name: "/"}
}else if(...){
return '/'
}else{
// ...
// 返回 false 以取消導航
return false
}
})
```
- 此守衛將在進入路由之前執行,但此時路由尚未解析,所以不可調用meta屬性。
- to: 即將進入的路由
- from: 即將離開的路由
- 如果return false,則取消路由,true或undefine則為成功
### 3.1.2 beforeResolve
```jsx
const router = createRouter({ ... })
router.beforeResolve((to, from) => {
})
```
- 此守衛與beforeEach類似,但beforeResolve是在路由解析完後才被調用的,所以可以使用meta屬性。
### 3.1.3 afterEach
```jsx
router.afterEach((to, from) => {
sendToAnalytics(to.fullPath)
})
```
- 此守衛在導航成功後,頁面渲染前,被調用。
- 不能改變導航路徑
## 3.2 路由內的守衛
### 3.2.1 beforeEnter
```jsx
const routes = [
{
path: '/abc',
component: Test,
beforeEnter: (to, from) => {
},
},
]
```
- beforeEnter在進入該路由時被調用。
- 可使用函數傳遞:
```jsx
function removeQueryParams(to) {
if (Object.keys(to.query).length)
return { path: to.path, query: {}, hash: to.hash }
}
function removeHash(to) {
if (to.hash) return { path: to.path, query: to.query, hash: '' }
}
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: [removeQueryParams, removeHash],
},
{
path: '/about',
component: UserDetails,
beforeEnter: [removeQueryParams],
},
]
```
## 3.3 完整的導航流程
1. 導航被觸發。
2. 在要離開的元件裡調用守衛`beforeRouteLeave`。
3. 調用全域守衛`beforeEach`。
4. 在重用的元件裡調用守衛`beforeRouteUpdate`。
5. 在路由配置裡調用`beforeEnter`。
6. 解析異步路由元件。
7. 在被啟動的元件裡調用`beforeRouteEnter`。
8. 調用全域的守衛`beforeResolve`。
9. 導航被確認。
10. 調用全域的鉤子`afterEach`。
11. 觸發 DOM 更新。
12. 調用守衛中傳給的回調函數,創建好的元件實例會作為回調函數的參數傳入。`beforeRouteEnternext`
# 4. 參考資料
[4-4 路由守衛(Navigation Guards) | 重新認識 Vue.js | Kuro Hsu](https://book.vue.tw/CH4/4-4-navigation-guards.html)
[Navigation Guards | Vue Router](https://router.vuejs.org/guide/advanced/navigation-guards.html)