Vue Router
本章節以後 “發問的重要說明”
由於本章節以後,問題相對會複雜很多,很難從單一片段了解問題點。
如:部分程式碼無法運作,實際卻是其它片段的大小寫錯誤
所以本章節以後,為了加速回覆同學們問題的效率,請上傳完整程式碼至 Github 或個人雲端空間(不需要包含 node_modules),好讓我可以運行完整環境來仔細檢視喔。
如果僅上傳部分程式碼或程式碼的截圖,還是會要求重新上傳完整程式碼再行回覆,也感謝同學們的配合
Vue Router 簡介
關於路由 (MVC 概念)
後端路由在頁面上呈現的狀態
傳統後端路由: https://www.hexschool.com/user
前端路由在頁面上顯示的狀態
前端路由: https://www.hexschool.com/#/user
專案中的概念
前端路由: https://www.hexschool.com/#/user
前端路由: https://www.hexschool.com/#/products
路由表
‘/user’ > User.vue
‘/products’ > Products.vue
User.vue
Products.vue
Vue Router 開發流程
準備流程:
- 引入 Vue Router 環境
- 定義元件
- 定義路由表
- 加入對應連結
Vue Router 相關資源
注意:Vue 3 搭配的 Router 版本在網址中會有 “next” 的字樣,如:router.vuejs.org 則是 Vue 2 版本的路由
盡可能自己完成本章節的操作。
如果需要參考時,請注意 git 排序是逆向的,也可依據 Commit 名稱尋找,找到後可直接透過 hash 文字打開該章節所調整的片段
在一般網頁中引入 Vue Router
// HTML
<div id="app">
<h2>路由示範</h2>
{{ text }}
<router-link to="/a">a</router-link> |
<router-link to="/b">b</router-link>
<router-view></router-view>
</div>
// JS
<script src="https://unpkg.com/vue-router@4.0.5/dist/vue-router.global.js"></script>
<script>
const componentA = {
template: `
<div>A</div>
`
};
const componentB = {
template: `
<div>B</div>
`
};
const app = Vue.createApp({
data() {
return {
counter: 0,
text: '這裡有一段文字'
}
}
});
// 路由設定
const router = VueRouter.createRouter({
// 網址路徑模式:使用網址 hash 的形式
history: VueRouter.createWebHashHistory(),
// 匯入路由表
routes: [
{
// a 路徑
path: '/a',
// A 元件
component: componentA,
},
{
// b 路徑
path: '/b',
// B 元件
component: componentB,
},
]
});
app.use(router);
app.mount('#app');
</script>
Vue Cli 中使用 Vue Router 開發
- 使用 vue create vue_router_record
- Please pick a preset: Manually select features
- Check the features needed for your project: Choose Vue versin, Babel, Router, CSS Pre-processors, Linter
- Choose a version of Vue.js that you want to start the project with 3.x (Preview)
- Use history mode for router? (Requires proper server setup for index fallback in production) No
- Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with node-sass)
- Pick a linter / formatter config: Airbnb
- Pick additional lint features: Lint on save
- Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
- Save this as a preset for future projects? N
- 運行專案 – npm run serve
講解
Vue Cli 版本和 HTML 開發哪裡不一樣
- router/index.js 檔案
- main.js 檔案
- App.vue 檔案
<router-view/> – 主要的頁面切換
<router-link></router-link> – 用戶頁面連結點擊使用 - views 資料夾 – 主要頁面的設定
components 資料夾 – 相對比較小的元件
// src/router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 匯出
export default router;
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');
// src/App.vue
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
</template>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
</style>
新增元件和路由
- 新增一個元件加到畫面上
新增 views/NewPage.vue
原則: 先建立元件再加入路由 - 打開 router/index.js 檔案
在路由表新增物件內容 - 把頁面的連結加到畫面上
在 App.vue 檔案加入新的 <router-link>
使用另外一種寫法加入連結,改用動態屬性的方式把連結加入
// views/NewPage.vue
<template>
<div>新增頁面</div>
</template>
// router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
{
path: '/newpage',
name: '新增頁面',
component: () => import('../views/NewPage.vue'),
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 匯出
export default router;
// src/App.vue
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<!-- 動態屬性 -->
<router-link :to="{ name: '新增頁面' }">新增頁面</router-link>
</div>
<router-view/>
</template>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
</style>
中場: 為目前專案加入樣式
- 在 public/index.html 加入 Bootstrap 5 CSS CDN
- 在 App.vue 檔案調整 router-link,加入 navbar 的樣式
選擇相對簡單的樣式,直接複製貼到 App.vue 檔案 - 把 router-link 加進來,並調整 class 樣式
- router-view 外層加上 .container 樣式
// public/index.html
<!DOCTYPE html>
<html lang="">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
// src/App.vue
<template>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button"
data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<router-link to="/" class="nav-link active" aria-current="page">Home</router-link>
</li>
<li class="nav-item">
<router-link to="/about" class="nav-link">About</router-link>
</li>
<li class="nav-item">
<router-link :to="{ name: '新增頁面' }" class="nav-link">新增頁面</router-link>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<router-view/>
</div>
</template>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
</style>
巢狀路由
- App.vue CSS 樣式移除
- 建立兩個元件 – ComponentA、ComponentB
建立的元件放在 views 資料夾 - 加入路由 – 在 router/index.js 檔案
要加入子項目會在 newpage 的下方加入 children 陣列形式,然後加上物件內容 - 修改 NewPage.vue 檔案
- 補上左側的選單 – Bootstrap > List Group > Links and buttons
複製程式碼貼到 NewPage.vue 選單的地方
把連結改成<router-link> 標籤、加上路徑
// views/ComponentA.vue
<template>
元件 A
</template>
// views/ComponentB.vue
<template>
元件 B
</template>
// router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
{
path: '/newpage',
name: '新增頁面',
component: () => import('../views/NewPage.vue'),
children: [
{
// 子項目的路徑不用加上/
path: 'a',
component: () => import('../views/ComponentA.vue'),
},
{
path: 'b',
component: () => import('../views/ComponentB.vue'),
},
],
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 匯出
export default router;
// views/NewPage.vue
<template>
<div class="row">
<div class="col-4">
<div class="list-group">
<router-link to="/newpage/a" class="list-group-item list-group-item-action">
元件 A
</router-link>
<router-link to="/newpage/b" class="list-group-item list-group-item-action">
元件 B
</router-link>
</div>
</div>
<div class="col-8">
<router-view></router-view>
</div>
</div>
</template>
一個元件插入多個視圖 – 具名視圖
- 新增元件 C
- 新增元件 NamedView
加入兩個 router-view - 打開 router/index.js 檔案
路由表新增新的物件內容 - 在路由 namedView 新增 children 子路由
在 children 陣列加入其他子路由項目(c2a、a2b)
因為有兩個視圖(router-view),所以必須載入兩個元件
components 有加上s,因為要載入多個元件,以物件的形式把它帶進來 - 把具名視圖(命名視圖)路徑加到左側的選單
// views/ComponentC.vue
<template>
元件 C
</template>
// views/NamedView.vue
<template>
<h2>具名視圖(命名視圖)的範例</h2>
<div class="row">
<div class="col-6">
<router-view name="left"></router-view>
</div>
<div class="col-6">
<router-view name="right"></router-view>
</div>
</div>
</template>
// router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
{
path: '/newpage',
name: '新增頁面',
component: () => import('../views/NewPage.vue'),
children: [
{
// 子項目的路徑不用加上/
path: 'a',
component: () => import('../views/ComponentA.vue'),
},
{
path: 'b',
component: () => import('../views/ComponentB.vue'),
},
// 具名視圖(命名視圖)
{
path: 'namedview',
component: () => import('../views/NamedView.vue'),
children: [
{
path: 'c2a',
// 載入多個元件
components: {
// 命名視圖使用名稱就是物件所使用的名稱
left: () => import('../views/ComponentC.vue'),
right: () => import('../views/ComponentA.vue'),
},
},
{
path: 'a2b',
components: {
left: () => import('../views/ComponentA.vue'),
right: () => import('../views/ComponentB.vue'),
},
},
],
},
],
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 匯出
export default router;
// views/NewPage.vue
<template>
<div class="row">
<div class="col-4">
<div class="list-group">
<router-link to="/newpage/a" class="list-group-item list-group-item-action">
元件 A
</router-link>
<router-link to="/newpage/b" class="list-group-item list-group-item-action">
元件 B
</router-link>
<router-link to="/newpage/namedview/c2a" class="list-group-item list-group-item-action">
命名路由 c2a
</router-link>
<router-link to="/newpage/namedview/a2b" class="list-group-item list-group-item-action">
命名路由 a2b
</router-link>
</div>
</div>
<div class="col-8">
<router-view></router-view>
</div>
</div>
</template>
透過參數決定路由內容 – 動態路由
- 安裝 axios 套件 – npm install axios
- 新增 DynamicRouter 元件 – views/DynamicRouter.vue
- 重新運行 npm run serve
- DynamicRouter 檔案主要會用到 <script> 的部分
取得遠端資料,以 RANDOM USER 為範例
複製 api: https://randomuser.me/api/ - 載入 axios 套件
在 created 生命週期把遠端資料載進來 - DynamicRouter 元件加到 router 路由表裡面
- 確認有沒有取得遠端資料
http://localhost:8080/#/newpage/dynamicrouter - 觀察 info > seed、資料用戶
複製 data > info > seed 的資料
修改 DynamicRouter.vue 程式碼
透過 id 的形式取得相同一個資料 - id 透過網址來進行傳遞
http://localhost:8080/#/newpage/dynamicrouter/b9039789d7d5a209
修改 router/index.js 程式碼
path: ‘dynamicRouter/:id’, - 動態值取出來直接使用
console.log(this.$route.params.id)
// views/DynamicRouter.vue
<template>
<div>
</div>
</template>
<script>
// 載入 axios 套件
import axios from 'axios';
export default {
created() {
// b9039789d7d5a209
console.log(this.$route.params.id);
const seed = this.$route.params.id;
axios.get(`https://randomuser.me/api/?seed=${seed}`)
.then((res) => {
console.log(res);
});
},
};
</script>
// router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
{
path: '/newpage',
name: '新增頁面',
component: () => import('../views/NewPage.vue'),
children: [
{
// 子項目的路徑不用加上/
path: 'a',
component: () => import('../views/ComponentA.vue'),
},
{
path: 'b',
component: () => import('../views/ComponentB.vue'),
},
// 參數 - 動態路由
{
path: 'dynamicrouter/:id',
component: () => import('../views/DynamicRouter.vue'),
},
// 具名視圖(命名視圖)
{
path: 'namedview',
component: () => import('../views/NamedView.vue'),
children: [
{
path: 'c2a',
// 載入多個元件
components: {
// 命名視圖使用名稱就是物件所使用的名稱
left: () => import('../views/ComponentC.vue'),
right: () => import('../views/ComponentA.vue'),
},
},
{
path: 'a2b',
components: {
left: () => import('../views/ComponentA.vue'),
right: () => import('../views/ComponentB.vue'),
},
},
],
},
],
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 匯出
export default router;
// vue.config.js
module.exports = {
chainWebpack: (config) => {
config.plugin('define').tap((definitions) => {
Object.assign(definitions[0], {
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false',
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false'
})
return definitions
})
}
}
動態路由搭配 Props
- 打開 DynamicRouter.vue 檔案
直接另存新檔 DynamicRouterByProps.vue - 開啟 router/index.js 檔案
複製參數 – 動態路由的物件,並把對應的檔案調整 - 在網址列貼上新的路徑
http://localhost:8080/#/newpage/dynamicrouterbyprops/b9039789d7d5a209 - 在 router/index.js 檔案
在動態路由搭配 Props 物件裡面新增一個屬性 props
加上 id: seed 字串 - 在 DynamicRouterByProps.vue 檔案
做程式碼修改
新增 props 屬性,props 所取得的是 router/index.js 路由表所定義的 props 的 id
console 改成直接取得 props 的 id 內容
console.log(‘props’, this.id); - 在 router/index.js 檔案
id 要改成使用動態路由的方式帶過來
調整程式碼 props 的地方,改成大括號直接 return 結果
// 4. 在 router/index.js 檔案
props: () => ({
// id: seed 字串
id: 'b9039789d7d5a209',
}),
// 6. 在 router/index.js 檔案
props: (route) => {
console.log('route:', route);
return {
id: route.params.id,
};
},
// views/DynamicRouterByProps.vue
<template>
<div>
</div>
</template>
<script>
// 載入 axios 套件
import axios from 'axios';
export default {
props: ['id'],
created() {
// b9039789d7d5a209
console.log('props', this.id);
const seed = this.$route.params.id;
axios.get(`https://randomuser.me/api/?seed=${seed}`)
.then((res) => {
console.log(res);
});
},
};
</script>
// router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
{
path: '/newpage',
name: '新增頁面',
component: () => import('../views/NewPage.vue'),
children: [
{
// 子項目的路徑不用加上/
path: 'a',
component: () => import('../views/ComponentA.vue'),
},
{
path: 'b',
component: () => import('../views/ComponentB.vue'),
},
// 參數 - 動態路由
{
path: 'dynamicrouter/:id',
component: () => import('../views/DynamicRouter.vue'),
},
// 動態路由搭配 Props
{
path: 'dynamicrouterbyprops/:id',
component: () => import('../views/DynamicRouterByProps.vue'),
props: (route) => {
console.log('route:', route);
return {
id: route.params.id,
};
},
},
// 具名視圖(命名視圖)
{
path: 'namedview',
component: () => import('../views/NamedView.vue'),
children: [
{
path: 'c2a',
// 載入多個元件
components: {
// 命名視圖使用名稱就是物件所使用的名稱
left: () => import('../views/ComponentC.vue'),
right: () => import('../views/ComponentA.vue'),
},
},
{
path: 'a2b',
components: {
left: () => import('../views/ComponentA.vue'),
right: () => import('../views/ComponentB.vue'),
},
},
],
},
],
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 匯出
export default router;
路由方法介紹
使用 Options API 來操作路由
- 新增 RouterNavigation.vue 檔案、調整 router/index.js 檔案、調整 views/NewPage.vue 檔案
- getRoute 取得屬性以及方法
- $route – 取得目前在這個路由下有哪些的資訊
fullPath、params、
http://localhost:8080/#/newpage/routerNavigation?search=123 - $router – 這個路由下可以使用的方法
常見方法切換頁面: push、replace
- $route – 取得目前在這個路由下有哪些的資訊
- push
- replace
- go
- addRoute
// 1. views/RouterNavigation.vue
<template>
<button type="button" @click="getRoute">getRoute</button>
<button type="button" @click="push">Push</button>
<button type="button" @click="replace">Replace</button>
<button type="button" @click="go">Go</button>
<hr>
<button type="button" @click="addRoute">新增路由</button>
</template>
<script>
export default {
methods: {
// 包含歷史紀錄
push() {
},
// 沒有歷史紀錄
replace() {
},
// 操作歷史紀錄
go() {
},
// 取得常用參數
getRoute() {
// 取得路由的屬性
// https://next.router.vuejs.org/zh/api/#routelocationnormalized
// 範例: this.$route.fullPath (目前網址)
// console.log(this.$route);
// 呼叫路由的方法
// 參考: https://next.router.vuejs.org/zh/api/#router-方法
// 範例: this.$router.go(-1) (回到前一頁)
// console.log(this.$router);
},
// 延伸介紹
addRoute() {
},
},
};
</script>
// 1. router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
{
path: '/newpage',
name: '新增頁面',
component: () => import('../views/NewPage.vue'),
children: [
{
// 子項目的路徑不用加上/
path: 'a',
component: () => import('../views/ComponentA.vue'),
},
{
path: 'b',
component: () => import('../views/ComponentB.vue'),
},
// 參數 - 動態路由
{
path: 'dynamicrouter/:id',
component: () => import('../views/DynamicRouter.vue'),
},
// 動態路由搭配 Props
{
path: 'dynamicrouterbyprops/:id',
component: () => import('../views/DynamicRouterByProps.vue'),
props: (route) => {
console.log('route:', route);
return {
id: route.params.id,
};
},
},
{
path: 'routernavigation',
component: () => import('../views/RouterNavigation.vue'),
},
// 具名視圖(命名視圖)
{
path: 'namedview',
component: () => import('../views/NamedView.vue'),
children: [
{
path: 'c2a',
// 載入多個元件
components: {
// 命名視圖使用名稱就是物件所使用的名稱
left: () => import('../views/ComponentC.vue'),
right: () => import('../views/ComponentA.vue'),
},
},
{
path: 'a2b',
components: {
left: () => import('../views/ComponentA.vue'),
right: () => import('../views/ComponentB.vue'),
},
},
],
},
],
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 匯出
export default router;
// 1. views/NewPage.vue
<template>
<div class="row">
<div class="col-4">
<div class="list-group">
<router-link to="/newpage/a" class="list-group-item list-group-item-action">
元件 A
</router-link>
<router-link to="/newpage/b" class="list-group-item list-group-item-action">
元件 B
</router-link>
<router-link to="/newpage/namedview/c2a" class="list-group-item list-group-item-action">
命名路由 c2a
</router-link>
<router-link to="/newpage/namedview/a2b" class="list-group-item list-group-item-action">
命名路由 a2b
</router-link>
<router-link to="/newpage/dynamicrouter/b9039789d7d5a209"
class="list-group-item list-group-item-action">
動態路由($route)
</router-link>
<router-link to="/newpage/dynamicrouterbyprops/b9039789d7d5a209"
class="list-group-item list-group-item-action">
動態路由(props)
</router-link>
<router-link to="/newpage/routerNavigation" class="list-group-item list-group-item-action">
路由導覽
</router-link>
</div>
</div>
<div class="col-8">
<router-view></router-view>
</div>
</div>
</template>
// views/RouterNavigation.vue
<template>
<button type="button" @click="getRoute">getRoute</button>
<button type="button" @click="push">Push</button>
<button type="button" @click="replace">Replace</button>
<button type="button" @click="go">Go</button>
<hr>
<button type="button" @click="addRoute">新增路由</button>
</template>
<script>
export default {
methods: {
// 包含歷史紀錄
push() {
// 方法一: 直接帶入網址
// this.$router.push('/newpage/dynamicrouter/b9039789d7d5a209');
// 方法二: 使用 name 的方式帶入值
this.$router.push({
name: 'About',
});
},
// 沒有歷史紀錄
replace() {
this.$router.replace({
name: 'About',
});
},
// 操作歷史紀錄
go() {
// 正整數 - 下一頁
// 負整數 - 上一頁
this.$router.go(-1);
},
// 取得常用參數
getRoute() {
// 取得路由的屬性
// https://next.router.vuejs.org/zh/api/#routelocationnormalized
// 範例: this.$route.fullPath (目前網址)
// console.log(this.$route);
// 呼叫路由的方法
// 參考: https://next.router.vuejs.org/zh/api/#router-方法
// 範例: this.$router.go(-1) (回到前一頁)
console.log(this.$router);
},
// 延伸介紹
addRoute() {
this.$router.addRoute({
path: '/newabout',
name: 'newAbout',
component: () => import('./About.vue'),
});
},
},
};
</script>
預設路徑以及重新導向
- 新增 NotFound.vue 檔案
- 在 router/index.js 檔案
製作404頁面以及重新導向
404頁面提供一個頁面資訊給用戶了解到他現在進入到錯誤的路由,可以如何回到正確的路由下
重新導向直接把用戶導到一個正確的頁面
// views/NotFound.vue
<template>
<h1>404</h1>
<p>這頁找不到囉</p>
</template>
// router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
{
path: '/newpage',
name: '新增頁面',
component: () => import('../views/NewPage.vue'),
children: [
{
// 子項目的路徑不用加上/
path: 'a',
component: () => import('../views/ComponentA.vue'),
},
{
path: 'b',
component: () => import('../views/ComponentB.vue'),
},
// 參數 - 動態路由
{
path: 'dynamicrouter/:id',
component: () => import('../views/DynamicRouter.vue'),
},
// 動態路由搭配 Props
{
path: 'dynamicrouterbyprops/:id',
component: () => import('../views/DynamicRouterByProps.vue'),
props: (route) => {
console.log('route:', route);
return {
id: route.params.id,
};
},
},
{
path: 'routernavigation',
component: () => import('../views/RouterNavigation.vue'),
},
// 具名視圖(命名視圖)
{
path: 'namedview',
component: () => import('../views/NamedView.vue'),
children: [
{
path: 'c2a',
// 載入多個元件
components: {
// 命名視圖使用名稱就是物件所使用的名稱
left: () => import('../views/ComponentC.vue'),
right: () => import('../views/ComponentA.vue'),
},
},
{
path: 'a2b',
components: {
left: () => import('../views/ComponentA.vue'),
right: () => import('../views/ComponentB.vue'),
},
},
],
},
],
},
// 404 頁面
{
path: '/:pathMatch(.*)*',
component: () => import('../views/NotFound.vue'),
},
// 重新導向
{
path: '/newPage/:pathMatch(.*)*',
redirect: {
name: 'Home',
},
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 匯出
export default router;
路由設定選項
- 在 App.vue 檔案程式碼調整
調整 Navbar固定在最上方、調整網頁的高度、在最下方增加一個 router-link - 查看 Vue Router 文件
VueRouter > RouterOptions - linkActiveClass
- scrollBehavior
// App.vue
<template>
<nav class="navbar navbar-expand-lg bg-body-tertiary fixed-top">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button"
data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<router-link to="/" class="nav-link active" aria-current="page">Home</router-link>
</li>
<li class="nav-item">
<router-link to="/about" class="nav-link">About</router-link>
</li>
<li class="nav-item">
<router-link :to="{ name: '新增頁面' }" class="nav-link">新增頁面</router-link>
</li>
</ul>
</div>
</div>
</nav>
<div class="container" style="height: 300vh">
<router-view/>
</div>
<router-link to="/newpage/routernavigation">/newpage/routernavigation</router-link>
</template>
<style lang="scss">
body {
padding-top: 80px;
}
</style>
// router/index.js
// 匯入 vue-router 資源
import { createRouter, createWebHashHistory } from 'vue-router';
// 匯入獨立元件
import Home from '../views/Home.vue';
// 路由表
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
},
{
path: '/newpage',
name: '新增頁面',
component: () => import('../views/NewPage.vue'),
children: [
{
// 子項目的路徑不用加上/
path: 'a',
component: () => import('../views/ComponentA.vue'),
},
{
path: 'b',
component: () => import('../views/ComponentB.vue'),
},
// 參數 - 動態路由
{
path: 'dynamicrouter/:id',
component: () => import('../views/DynamicRouter.vue'),
},
// 動態路由搭配 Props
{
path: 'dynamicrouterbyprops/:id',
component: () => import('../views/DynamicRouterByProps.vue'),
props: (route) => {
console.log('route:', route);
return {
id: route.params.id,
};
},
},
{
path: 'routernavigation',
component: () => import('../views/RouterNavigation.vue'),
},
// 具名視圖(命名視圖)
{
path: 'namedview',
component: () => import('../views/NamedView.vue'),
children: [
{
path: 'c2a',
// 載入多個元件
components: {
// 命名視圖使用名稱就是物件所使用的名稱
left: () => import('../views/ComponentC.vue'),
right: () => import('../views/ComponentA.vue'),
},
},
{
path: 'a2b',
components: {
left: () => import('../views/ComponentA.vue'),
right: () => import('../views/ComponentB.vue'),
},
},
],
},
],
},
// 404 頁面
{
path: '/:pathMatch(.*)*',
component: () => import('../views/NotFound.vue'),
},
// 重新導向
{
path: '/newPage/:pathMatch(.*)*',
redirect: {
name: 'Home',
},
},
];
// router 結構
const router = createRouter({
history: createWebHashHistory(),
routes,
linkActiveClass: 'active',
scrollBehavior(to, from, savedPosition) {
console.log(to, from, savedPosition);
// `to` and `from` are both route locations
// `savedPosition` can be null if there isn't one
if (to.fullPath.match('newpage')) {
return {
top: 0,
};
}
return {};
},
});
// 匯出
export default router;