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/
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 class="view left-sidebar" name="LeftSidebar"></router-view>
<router-view class="view main-content"></router-view>
<router-view class="view right-sidebar" 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: ['/',‘’] // 也可以設定兩個別名
}
]
我們可以通過$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 })
}
]
在瀏覽器url使用了雜湊字元 # 這部分url沒有被傳送伺服器。 在SEO優化 有不好的影響,如果擔心這個問題可以使用HTML5模式
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
這種模式看起來像一個比較正常的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
勾點。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
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元件上封裝一層(自定義 RouterLink 元件),範例如下
<template> <a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank"> <slot /> </a> <router-link v-else v-bind="$props" custom v-slot="{ isActive, href, navigate }"> <a v-bind="$attrs" :href="href" @click="navigate" :class="isActive ? activeClass : inactiveClass"> <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" class="inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out" active-class="border-indigo-500 text-gray-900 focus:border-indigo-700" inactive-class="text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:text-gray-700 focus:border-gray-300" > 點我點我 </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() // 獲取一個包含所有路由記錄的陣列
基於檔案的自動路由,可以使用vite提供的功能(vite-plugin-pages)
也可以通過官方提供的一個外掛 unplugin-vue-router
emmmm... 這個內容有點多,還是看官網咖...
閱讀 vue-router@4 API
詳細見官網
本文來自部落格園,作者:Pro成,轉載請註明原文連結:https://www.cnblogs.com/ProCheng/p/17293241.html