安裝
vue-router是一個vue的插件,用來實作前端的路由, 推薦使用
pnpm add vue-router@4進行安裝,推薦配合vue3組合式api使用
基礎
從一個例子開始
<!-- App.vue檔案 -->
<div id="app">
<h1>Hello App!</h1>
<p>
<!--使用 router-link 組件進行導航 -->
<!--通過傳遞 `to` 來指定鏈接 -->
<!--`<router-link>` 將呈現一個帶有正確 `href` 屬性的 `<a>` 標簽-->
<router-link to="/hello/112233">hello</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這里 -->
<router-view></router-view>
</div>
// router/index.js 檔案
//createRouter 用來創建路由
//createWebHashHistory 指定路由是hash模式
import { createRouter,createWebHashHistory } from 'vue-router'
import Hello from '../components/Hello.vue'
import ErrorPage from '../components/ErrorPage.vue'
// 配置路由規則
const routers = [
// 匹配所有路由(正則匹配權重低),自定義404
{path: '/:all(.*)*', component: ErrorPage},
{path: '/hello/:id',component: Hello}
]
// 匯出路由
export default createRouter({
history: createWebHashHistory(), // 這里我們使用簡單的hash模式
routes: routers
})
// main.js 檔案
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import routers from './routers'
createApp(App)
.use(routers) // 掛載路由
.mount('#app')
專案中的路由物件
tips: 別忘記掛載路由哦 如 use(router)
- $router: 通過this呼叫,或者通過
import匯出路由也一樣,此路由物件,就是匯出的路由物件,可以呼叫push等 ... <-- $route: 通過this呼叫,此路由物件是當前的路由物件,比如說 /Home 拿到的是當前 /Home 的路由物件,可以通過 $route.params 拿到路由引數等
動態路由
匹配規則
| 匹配模式 | 匹配路徑 | 匹配說明 | $route.params | $route.query | $route.hash |
|---|---|---|---|---|---|
| /users/:username | /users/eduardo?sid=123#ok | ok | |||
| /users/:username/posts/:postId | /users/eduardo/posts/123?sid=123#ok | ok | |||
| /:pathMatch(.*)* | /a/b | 匹配所有 | |||
| /user-:afterUser(.*) | /user-admin | 匹配user- 以開頭的 | |||
| /:orderId(\\d+) | /123 | 匹配數字 | |||
| /:chapters+ | /a/a/a | 匹配重復的(一個或多個) |
從上面可以看出,路由匹配是支持自定義正則的,官方推薦的路由除錯工具 https://paths.esm.dev/
Sensitive 與 strict 路由配置
const router = createRouter({
history: createWebHistory(),
routes: [
{
// /users ok
// /Users error
// /user/ error
path: '/users',
sensitive: true, // 大小寫敏感
strict: true, // 是否嚴格按照path匹配
},
],
strict: true, // 或者這里設定全域
})
嵌套路由
比如我們有兩個路由 /home/user,/home/posts,可以看出來他們都是處于 /home下的,屬于是嵌套關系,我們可以使用以下代碼實作
const routes = [
{
path: '/home',
component: Home,
children: [ // home的子級
{
path: 'user',
component: User,
},
{
path: 'posts',
component: Posts,
},
],
},
]
編程式導航
頂文的有個例子使用
<router-link to="/home">點我啊</router-link>進行路由導航,其實點擊<router-link :to="...">相當于呼叫router.push(...),編程時導航就是通過js代碼來跳轉路由,通過以下例子介紹下
// 字串路徑
router.push('/users/admin')
// 帶有路徑的物件
router.push({ path: '/users/admin' })
// 命名的路由,讓路由建立 url, 可以傳遞params, query,hash, 傳遞的引數vue-router會自動編碼,如果放在path中需要先編碼一下
router.push({ name: 'user', params: { username: 'admin' }, query: { plan: '花兒為什么這么紅' }, hash: '#h1' } })
push: 這個方法會向 history 堆疊添加一個新的記錄,所以,當用戶點擊瀏覽器后退按鈕時,會回到之前的 URLreplace: 會替代當前位置,無法回退,其余的同上forward: 向前移動一條記錄back: 向后移動一條記錄go: go(-1)后退,反之亦然
命名路由
除了
path之外,你還可以為任何路由提供name,這有以下優點:
- 沒有硬編碼的 URL
params的自動編碼/解碼,- 防止你在 url 中出現打字錯誤,
const routes = [
{
path: '/user/:username',
name: 'user',
component: User,
},
]
router.push({ name: 'user', params: { username: 'any' } }
命名視圖
有時候我們想要在同一個頁面里展示多個視圖,又不想嵌套展示,例如創建一個布局,sidebar(側邊欄),main(主要區域),示例代碼如下,當然也可以嵌套命名視圖,
<router-view name="LeftSidebar"></router-view>
<router-view ></router-view>
<router-view name="RightSidebar"></router-view>
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
components: {
default: Home,
// LeftSidebar: LeftSidebar 的縮寫
LeftSidebar,
// 它們與 `<router-view>` 上的 `name` 屬性匹配
RightSidebar,
},
},
],
})
// 或使用嵌套命名視圖
{
path: '/settings',
// 你也可以在頂級路由就配置命名視圖
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
重定向和別名
// redirect可以是一個字串,一個物件,一個函式,示例如下
const routes = [
{
// /search/screens -> /search?q=screens
path: '/search/:searchText',
redirect: to => {
// 方法接收目標路由作為引數
// return 重定向的字串路徑/路徑物件
return { path: '/search', query: { q: to.params.searchText } }
},
},
{
path: '/search',
// ...
},
]
// 別名,就是說當訪問這個別名的時候,跳轉到此路由,不受路由限制,推薦少用少用少用!!!
const routes = [
{
path: '/home',
alias: ['/',‘’] // 也可以設定兩個別名
}
]
給組件傳遞props
我們可以通過$route拿到引數,但是這樣寫的話組件過于依賴路由,我們有更好的一種方式,通過設定
props: true開啟props傳參, 示例如下
const routes = [
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
// 有以下幾種寫法
// 1:props: true
// 2:props: { default: true, sidebar: false }
// 3:{ id: '傳入的引數' }
// 4: route=> ({ id: route.params.id })
}
]
路由模式
Hash 模式
在瀏覽器url使用了哈希字符 # 這部分url沒有被發送服務器, 在SEO優化 有不好的影響,如果擔心這個問題可以使用HTML5模式
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
HTML5 模式
這種模式看起來像一個比較正常的url 比如
https://example.com/home/user, 需要在服務器配置
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
服務器配置
-
nginx
location / { try_files $uri $uri/ /index.html; } -
node
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) })-
iis
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="Handle History Mode and custom 404/500" stopProcessing="true"> <match url="(.*)" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="/" /> </rule> </rules> </rewrite> </system.webServer> </configuration>
-
進階
好啦,看到這已經理解基礎了,想了解更深的話繼續往下看,我們有時候需要做點別的 事情
導航守衛
導航守衛就是AOP的思想,在路由進入前、進入后 、更新,做點事情,導航守衛可以使用多個會依次執行,類似于管道,路由匹配的組件會復用, 它可以掛載到全域、單個路由、單個組件,各有妙用,望君細品,代碼示例如下
全域守衛
// 進入前
router.beforeEach((to, from)=>{
//回傳 false 以取消導航,也可以回傳一個物件,比如 {name:'home'},相當于重定向
})
// 或者加入第三個引數 next,如果宣告了第三個引數,則必須呼叫一個,否則會一直等待
router.beforeEach((to, from, next)=>{
// next(false) 或者 next({path: '/home'})
})
// 進入后
router.afterEach((to, from)=>{
})
// 決議守衛,發生在beforeEach后
router.beforeResolve((to, from)=>{
})
路由專用守衛
// 進入路由觸發(但是還沒有進入,在beforeResolve前觸發)
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], // 可以是陣列,按順序呼叫
},
]
組件內的守衛
// 選項式: beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave
// 組合式:onBeforeRouteUpdate,onBeforeRouteLeave
const UserDetails = {
template: `...`,
beforeRouteEnter(to, from) {
// 在渲染該組件的對應路由被驗證前呼叫
// 不能獲取組件實體 `this` !
// 因為當守衛執行時,組件實體還沒被創建!
},
beforeRouteUpdate(to, from) {
// 在當前路由改變,但是該組件被復用時呼叫
// 舉例來說,對于一個帶有動態引數的路徑 `/users/:id`,在 `/users/1` 和 `/users/2` 之間跳轉的時候,
// 由于會渲染同樣的 `UserDetails` 組件,因此組件實體會被復用,而這個鉤子就會在這個情況下被呼叫,
// 因為在這種情況發生的時候,組件已經掛載好了,導航守衛可以訪問組件實體 `this`
},
beforeRouteLeave(to, from) {
// 在導航離開渲染該組件的對應路由時呼叫
// 與 `beforeRouteUpdate` 一樣,它可以訪問組件實體 `this`
},
}
完整的導航決議流程
- 導航被觸發,
- 在失活的組件里呼叫
beforeRouteLeave守衛, - 呼叫全域的
beforeEach守衛, - 在重用的組件里呼叫
beforeRouteUpdate守衛(2.2+), - 在路由配置里呼叫
beforeEnter, - 決議異步路由組件,
- 在被激活的組件里呼叫
beforeRouteEnter, - 呼叫全域的
beforeResolve守衛(2.5+), - 導航被確認,
- 呼叫全域的
afterEach鉤子, - 觸發 DOM 更新,
- 呼叫
beforeRouteEnter守衛中傳給next的回呼函式,創建好的組件實體會作為回呼函式的引數傳入,
路由元資訊
有時候想要將資訊附加到路由上,我們看下面這個例子
const routes = [
{
path: '/posts',
component: PostsLayout,
children: [
{
path: 'new',
component: PostsNew,
// 只有經過身份驗證的用戶才能創建帖子
meta: { requiresAuth: true }
},
{
path: ':id',
component: PostsDetail
// 任何人都可以閱讀文章
meta: { requiresAuth: false }
}
]
}
]
// 我們可以通過 $route.meta 拿到meta資料
router.beforeEach((to, from) => {
// 而不是去檢查每條路由記錄
// to.matched.some(record => record.meta.requiresAuth)
if (to.meta.requiresAuth && !auth.isLoggedIn()) {
// 此路由需要授權,請檢查是否已登錄
// 如果沒有,則重定向到登錄頁面
return {
path: '/login',
// 保存我們所在的位置,以便以后再來
query: { redirect: to.fullPath },
}
}
})
在組合式API的應用
在組合式API公開下面兩個導航守衛API
import { onBeforeRouteUpdate,onBeforeRouteLeave } from 'vue-router'onBeforeRouteLeave: 即將離開路由
onBeforeRouteUpdate: 路由的hash或者query發生了變化
自定義router-link
<template> <div @click="handle"> <slot></slot> </div> </template> <script> import { RouterLink, useLink } from 'vue-router' import { computed } from 'vue' export default { name: 'AppLink', props: { // 如果使用 TypeScript,請添加 @ts-ignore ...RouterLink.props, inactiveClass: String, }, methods:{ handle(){ this.$router.push(this.to) } }, setup(props) { const { // 決議出來的路由物件 route, // 用在鏈接里的 href href, // 布爾型別的 ref 標識鏈接是否匹配當前路由 isActive, // 布爾型別的 ref 標識鏈接是否嚴格匹配當前路由 isExactActive, // 導航至該鏈接的函式 navigate } = useLink(props) const isExternalLink = computed( () => typeof props.to === 'string' && props.to.startsWith('http') ) return { isExternalLink, href, navigate, isActive } }, } </script>
過度動效
顧名思義就是給路由的切換添加影片效果
例子如下
<router-view v-slot="{ Component, route }"> <transition name="fade"> <component :is="Component" :key="route.path" /> </transition> </router-view>我們用v-slot拿到了 component和route,然后通過動態組件的方式來顯示,在transition指定影片效果
由于vue會自動復用看起來類似的組件,所有我們需要加上唯一 key
滾動行為
這個比較簡單就是一個 api
例子如下
const router = createRouter({ history: createWebHashHistory(), routes: [...], scrollBehavior (to, from, savedPosition) { // return 期望滾動到哪個的位置,示例如下 } }) /* 回傳值支持以下,也可以回傳promise,支持延遲滾動 return { // 也可以這么寫 // el: document.getElementById('main'), // el: to.hash, 定位錨點 // behavior: 'smooth', 滾動流暢 el: '#main', top: -10, } */如果回傳一個 falsy(非真值) 的值,或者是一個空物件,那么不會發生滾動,
回傳 savedPosition,在按下 后退/前進 按鈕時,就會像瀏覽器的原生表現那樣:
路由懶加載
vue-router支持動態匯入我們這樣寫,示例如下
const router = createRouter({ routes: [ { path: '/users/:id', component: () => import('./views/UserDetails.vue') } ], })為了避免請求過多,我們有時候需要組件按組分塊,示例如下
// 在路由下配置webpackChunkName,vite支持webpack的這種寫法,同名的設定為一組 const UserDetails = () => import(/* webpackChunkName: "group-user" */ './UserDetails.vue') const UserDashboard = () => import(/* webpackChunkName: "group-user" */ './UserDashboard.vue') const UserProfileEdit = () => import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue') // 我們在vite.config.js 配置 export default defineConfig({ build: { rollupOptions: { // https://rollupjs.org/guide/en/#outputmanualchunks output: { manualChunks: { 'group-user': [ // group-user組 './src/UserDetails.vue', './src/UserDashboard.vue', './src/UserProfileEdit.vue', ], }, }, }, }, }) // 配置后將會在build后 才能生效!!!!
擴展router-link
在router-link組件上封裝一層(自定義 RouterLink 組件),示例如下
<template> <a v-if="isExternalLink" v-bind="$attrs" :href="https://www.cnblogs.com/ProCheng/archive/2023/05/06/to" target="_blank"> <slot /> </a> <router-link v-else v-bind="$props" custom v-slot="{ isActive, href, navigate }"> <a v-bind="$attrs" :https://www.cnblogs.com/ProCheng/archive/2023/05/06/href="href" @click="navigate" :> <slot /> </a> </router-link> </template> <script> import { RouterLink } from 'vue-router' export default { name: 'AppLink', inheritAttrs: false, props: { // 如果使用 TypeScript,請添加 @ts-ignore ...RouterLink.props, inactiveClass: String, }, computed: { isExternalLink() { return typeof this.to === 'string' && this.to.startsWith('http') }, }, } </script>使用組件
<AppLink v-bind="$attrs" active- inactive- > 點我點我 </AppLink>
路由導航檢測
有時候我們需要在push后做點什么,可以在 push 前面加上 await 示例如下
通過 push等方法進行路由導航,有時候未必會成功跳轉,比如已經在目標路由,或者路由回傳了false,示例如下
// push回傳的是一個promise,因此我們可以這樣寫 await $router.push('/user/list') this.isMenuOpen = false; // 判斷是否導航成功 var navigationResult = await $router.push('/user/list'); if (navigationResult) { // 導航被阻止 } else { // 導航成功 (包括重新導航的情況) this.isMenuOpen = false } // 其實通過navigationResult物件可以拿到詳細的錯誤資訊,這個用的比較少,這里就不寫了
動態路由
對于已經運行的程式,我們想添加路由,就看這里,動態路由主要通過兩個函式實作
router.addRoute()和router.removeRoute()
var removeRoute = router.addRoute({path: '/user', name: 'user', component: xxx })
// router.replace(router.currentRoute.value.fullPath) // 然后手動呼叫replace覆寫當前路由
// 可以覆寫替換,當名字一樣的情況下
removeRoute('填入路由的名字') // 呼叫它的回傳值可以刪掉路由
// 添加嵌套路由
router.addRoute('父路由的名字', {path: '/user', name: 'user', component: xxx })
// 查看現有路由
router.hasRoute() // 檢查路由是否存在
router.getRoutes() // 獲取一個包含所有路由記錄的陣列
擴展
unplugin-vue-router
基于檔案的自動路由,可以使用vite提供的功能(vite-plugin-pages)
也可以通過官方提供的一個插件 unplugin-vue-router
emmmm... 這個內容有點多,還是看官網吧...
GitHub unplugin-vue-router
持續學習
閱讀 vue-router@4 API
詳細見官網
拜了個拜~
本文來自博客園,作者:Pro成,轉載請注明原文鏈接:https://www.cnblogs.com/ProCheng/p/17293241.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/551835.html
標籤:其他
下一篇:返回列表
