認識前端路由
后端路由階段
前后端分離階段
URL的hash
hash-demo.html
<!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>Document</title>
</head>
<body>
<div id="app">
<a href="#/home">home</a>
<a href="#/about">about</a>
<div class="content">Default</div>
</div>
<script>
const contentEl = document.querySelector('.content');
// 【hashchange是window的方法,不是document的,寫document.addEventListener不能監聽hash的變化。】
window.addEventListener("hashchange", () => {
switch (location.hash) {
case "#/home":
contentEl.innerHTML = "Home";
break;
case "#/about":
contentEl.innerHTML = "About";
break;
default:
contentEl.innerHTML = "Default";
}
})
</script>
</body>
</html>
HTML5的History
history-demo.html
<!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>Document</title>
</head>
<body>
<div id="app">
<a href="/home">home</a>
<a href="/about">about</a>
<div class="content">Default</div>
</div>
<script>
const contentEl = document.querySelector('.content');
const changeContent = () => {
switch (location.pathname) {
case "/home":
contentEl.innerHTML = "Home";
break;
case "/about":
contentEl.innerHTML = "About";
break;
default:
contentEl.innerHTML = "Default";
}
}
const aEls = document.getElementsByTagName("a");
for (let aEl of aEls) {
aEl.addEventListener("click", e => {
e.preventDefault();
const href = aEl.getAttribute("href");
// 【pushState是history的方法,不是window的。】
// history.pushState({}, "", href);
history.replaceState({}, "", href);
changeContent();
})
}
window.addEventListener("popstate", changeContent)
</script>
</body>
</html>
認識vue-router
路由的使用步驟
路由的基本使用流程
路由的默認路徑
history模式
router-link
路由懶加載
打包效果分析
路由的其他屬性
動態路由基本匹配
獲取動態路由的值
匹配多個參數
NotFound
匹配規則加*
路由的嵌套
路由的嵌套配置
代碼的頁面跳轉
query方式的參數
替換當前的位置
頁面的前進后退
router-link的v-slot
router-view的v-slot
動態添加路由
動態刪除路由
路由導航守衛
登錄守衛功能
其他導航守衛
https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html
main.js
import { createApp } from 'vue'
import router from './router'
import App from './App.vue'
const app = createApp(App)
app.use(router)
app.mount('#app')
// createApp(App).use(router).mount("#app") // 也可以這樣寫
router/index.js
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'
// import Home from "../pages/Home.vue";
// import About from "../pages/About.vue";
// 配置映射關系
const routes = [
{
path: "/",
redirect: "/home"
},
// /home/shops
{
path: "/home",
name: "home",
component: () => import(/* webpackChunkName: "home-chunk" */"../pages/Home.vue"),
meta: {
name: "why",
age: 18,
height: 1.88
},
children: [
{
path: "",
redirect: "/home/message" // redirect要拿完整的路徑做重定向
},
{
path: "message",
component: () => import("../pages/HomeMessage.vue")
},
{
path: "shops",
component: () => import("../pages/HomeShops.vue")
}
]
},
{
path: "/about",
name: "about",
component: () => import("../pages/About.vue")
},
{
path: "/user/:username/id/:id",
component: () => import("../pages/User.vue")
},
{
path: "/login",
component: () => import("../pages/Login.vue")
},
{
path: "/:pathMatch(.*)", // 任意匹配,固定寫法
component: () => import("../pages/NotFound.vue")
}
];
// 創建一個路由對象router
const router = createRouter({
routes,
history: createWebHistory()
})
// 動態添加路由
const categoryRoute = {
path: "/category",
component: () => import("../pages/Category.vue")
}
// 添加頂級路由對象
router.addRoute(categoryRoute);
// 添加二級路由對象【即home/moment。】 【參數1:組件的name屬性值。】
router.addRoute("home", {
path: "moment",
component: () => import("../pages/HomeMoment.vue")
})
// 導航守衛beforeEach
let counter = 0;
// to: Route對象, 即將跳轉到的Route對象
// from: Route對象,
/**
* 返回值問題:
* 1.false: 不進行導航
* 2.undefined或者不寫返回值: 進行默認導航 【該去哪,就去哪,相當於這里啥也沒做。】
* 3.字符串: 路徑, 跳轉到對應的路徑中
* 4.對象: 類似於 router.push({path: "/login", query: ....})
*/
router.beforeEach((to, from) => {
console.log('to---', to)
console.log('from---', from)
console.log(`進行了${++counter}路由跳轉`)
// if (to.path.indexOf("/home") !== -1) {
// return "/login"
// }
if (to.path !== "/login") {
const token = window.localStorage.getItem("token");
if (!token) {
return "/login"
}
}
})
export default router
App.vue
<template>
<div id="app">
<!-- props: href 跳轉的鏈接 -->
<!-- props: route對象 -->
<!-- props: navigate導航函數 -->
<!-- props: isActive 是否當前處於活躍的狀態 -->
<!-- props: isExactActive 是否當前處於精確的活躍狀態 -->
<router-link to="/home" v-slot="props" custom>
<button @click="props.navigate">{{ props.href }}</button>
<button @click="props.navigate">哈哈哈</button>
<span :class="{ active: props.isActive }">{{ props.isActive }}</span>
<span :class="{ active: props.isActive }">{{ props.isExactActive }}</span>
<!-- <p>{{props.route}}</p> -->
</router-link>
<router-link to="/about">關於</router-link>
<router-link to="/user/kobe/id/111">用戶</router-link>
<router-link to="/category">分類</router-link>
<button @click="jumpToAbout">關於</button>
<button @click="forwardOneStep">前進一步</button>
<router-view v-slot="props">
<!-- <transition name="why"> -->
<keep-alive>
<component :is="props.Component"></component>
</keep-alive>
<!-- </transition> -->
</router-view>
</div>
</template>
<script>
import { useRouter } from 'vue-router'
import NavBar from './components/NavBar.vue'
export default {
name: 'App',
components: {
NavBar,
},
methods: {
// jumpToAbout() {
// // router
// this.$router.push("/about")
// }
},
setup() {
const router = useRouter()
const jumpToAbout = () => {
// router.push("/about")
// router.push({
// path: "/about",
// query: {
// name: "why",
// age: 18
// }
// })
// router.replace("/about")
}
const forwardOneStep = () => {
router.go(1)
// router.go(-1)
// router.forward()
// router.back()
}
return {
jumpToAbout,
forwardOneStep,
}
},
}
</script>
<style>
.why-active {
color: red;
}
.why-enter-from,
.why-leave-to {
opacity: 0;
}
.why-enter-active,
.why-leave-active {
transition: opacity 1s ease;
}
</style>
Home.vue
<template>
<div>
<h2>Home</h2>
<ul>
<li>home的內容1</li>
<li>home的內容2</li>
<li>home的內容3</li>
</ul>
<router-view />
<router-link to="/home/message">消息</router-link>
<router-link to="/home/shops">商品</router-link>
<router-link to="/home/moment">動態</router-link>
</div>
</template>
<script>
export default {}
</script>
<style scoped></style>
HomeShops.vue
<template>
<div>
<h2>商品組件</h2>
<ul>
<li>shops1</li>
<li>shops2</li>
<li>shops3</li>
</ul>
</div>
</template>
<script>
export default {}
</script>
<style scoped></style>
HomeMessage.vue
<template>
<div>
<h2>消息組件</h2>
<ul>
<li>messasge1</li>
<li>messasge2</li>
<li>messasge3</li>
</ul>
</div>
</template>
<script>
export default {}
</script>
<style scoped></style>
HomeMoment.vue
<template>
<div>
<h2>HomeMoment</h2>
</div>
</template>
<script>
export default {}
</script>
<style scoped></style>
About.vue
<template>
<div>
<h2>About: {{ $route.query.name }}-{{ $route.query.age }}</h2>
</div>
</template>
<script>
export default {}
</script>
Login.vue
<template>
<div>
<button @click="loginClick">登錄</button>
</div>
</template>
<script>
import { useRouter } from 'vue-router'
export default {
setup() {
const router = useRouter()
const loginClick = () => {
window.localStorage.setItem('token', 'why')
router.push({
path: '/home',
})
}
return {
loginClick,
}
},
}
</script>
NotFound.vue
<template>
<div>
<h2>Page Not Found</h2>
<p>您打開的路徑頁面不存在, 請不要使用我們家的應用程序了~</p>
<h1>{{ $route.params.pathMatch }}</h1>
</div>
</template>
<script>
export default {}
</script>
User.vue
<template>
<div>
<h2>User: {{ $route.params.username }}-{{ $route.params.id }}</h2>
</div>
</template>
<script>
import { useRoute } from 'vue-router'
export default {
created() {
// 【username是在router/index.js中自定義的,兩者要保持一致。】
console.log(this.$route.params.username)
},
setup() {
const route = useRoute()
console.log(route.params.username)
},
}
</script>
NavBar.vue
<template>
<div>
<h2>{{ title }}</h2>
</div>
</template>
<script>
export default {
props: {
title: String,
},
}
</script>
historyApiFallback
https://github.com/bripkens/connect-history-api-fallback