路由插件的创建
1. route.js --
<script setup>
import {createRouter,createWebHistory,createMemoryHistory,createWebHashHistory} from 'vue-router'
export default const router = createRouter({
// history
history:createWebHistory(), // 推荐
history:createMemoryHistory(), // 无历史记录
history:createWebHashHistory, // 不利于SEO
// routes
routes = [{},{},...],
// 一些参数
strict:true, // 严格不匹配尾部带/
sensitive:true, // 对大小写敏感
// 滚动行为
scrollBehaviror(to,from,savedPosition){
// 返回一个希望滚动到的位置
return {
// el: document.getElementById('main'),
el: '#main',
top: 10,
}
}
})
</script>
2. main.js
<script setup>
import APP from './App.vue'
import {createApp} from 'vue'
import {router} from 'route.js'
const app = createApp(APP) // APP为根组件的name
app.use(router) // 挂载路由插件,必须在app.mount之前
app.mount('#app') // index.html的根div的id
</script>
路由表的配置(每一条路由)
<script>
// 1. 每一条路由的配置项(包括嵌套路由)
{
path:'/home', // 必须要有path,否则不会匹配
name:'home',
component:Home,
redirect:'/introduction', // 重定向
alias:'/house', // 别名
strict:true, // 严格不匹配尾部/
sensitive:true, // 对大小写敏感
props:true, // url参数标记为prop,则路由组件内部可使用defineProps方式获取
// 命名视图
components:{
default:bodyBar, // b
leftSideBar,
rightSiderBar,
},
// 路由嵌套
children:[
{
path:'user',
component:User,
},
{
path:'post'
component:Post
},
],
// 路由独享守卫
beforeEach:((to,from)=>{}),
beforeEach:[fun1,fun2]],
// 路由元信息:将任意信息附加到路由
meta:{
requireAuth:true
}
},
// 2. 动态url和携带的参数
path:'/home',
path:'/home/:id', // :paramName 动态参数
path:'/home/:id?', // ? 可选参数,有无均可
path:'/home/:id+', // + 匹配1个以上
path:'/home/:id*', // * 匹配0个以上
path:'/home/:id(\\d+)', // ()正则
path:'/home/:id(.*)*', // 通配符,用于404
component:Home
// 3. redirect方式
redirect:'/',
redirect:{name:'homepage'},
redirect:to =>{ // 返回一个重定向对象
return {path:'/search',query:{}}
},
redirect:to =>{
return 'profile' // 相对重定向
},
// 4. props方式
props:true, // 单个组件,单个routerView
props:{ // 多个组件,多个routerView
default:true,
leftSideBar:true,
rightSideBar:true
},
// 5. meta路由元信息
可以通过当前路由信息对象route.meta访问到,包含了meta字段
</script>
路由跳转与呈现
1. 路由跳转(导航)
(1)声明式导航 router-link
// 静态url
<router-link to="ativeUrl"></router-link>
// 响应式url
<router-link :to="ativeUrl"></router-link>
<router-link :to="ativeUrl" replace></router-link> // 替换当前路由
(2)编程式导航
router.push(ativeUrl) // 添加历史记录
router.replace(ativeUrl) // 不添加历史记录,替换当前url
router.go(1)
router.go(-1) == router.back(1)
(3)ativeUrl(动态参数)
字符串
to="/user/muggle"
一个对象
:to="{ path:`/user/${username}` }
:to="{
path:`/user/${username}`
name:'user',
params:{userName:username,chapters:['A','B']},
query:{plan:'private'}, // 要使用params必须使用name
hash:'#team',
replace:true
}
2. 路由呈现
视图组件:同一URL渲染多个不同组件
conponents指定了routerViewName:componentName
<router-view name="routerViewName"></router-view>
嵌套组件:router-view的组件还嵌套了一个routerview
嵌套视图: 一个router-view嵌套了多个routerview
路由器实例与路由信息对象
1. router
模板中使用$router访问路由器实例,JS中使用userRouter()访问。用于控制路由的配置、导航和状态管理
属性:
router.mode // 路由模式
router.currentRoute == route
方法:
router.push(activeUrl)
router.replace(activeUrl)
router.go(n)
router.back(n)
router.forward() === router.go(1)
router.beforeEach((to,from,next)=>{}) // 全局路由守卫
router.beforeResolve((to,from,next)=>{}) // 全局前置守卫
router.afterEach((to,from)=>{}) // 全局后置钩子
router.addRoute(routerRecord) // 单纯添加一条路由记录,若想跳转需要使用router.push()或router.replace()手动跳转
router.removeRoute(routeName) // 按名称删除路由
router.hasRoute(routeName) // 按名称查询路由
router.getRoutes() // 获取所有路由记录
2. route
模板中使用$route访问当前路由信息对象,JS中使用userRoute()访问,每次url更新路由信息对象都会更新。用于提供当前路由信息
属性:
route.path
route.fullPath // 完整路径,包括query和hash
route.name
route.params
route.query
route.hash
route.meta
方法:
route.matched() // 返回一个数组,包含当前路由和所有嵌套路由的route对象
route.redirectedForm // 返回重定向路由url
路由参数
1. 动态参数params
是url的一部分,通过路由配置的path中:定义实现,用于获得动态url
/user/:id
2. 查询参数querys
附加在url路径后面的键值对,用于传递附加信息,查询参数从?开始,多个参数通过&分隔,用于附加的过滤、排序、分页
/user/:id/search?name=muugle&age=23
3. 路由传参方式
- 可通过route.params和route.query访问到
- 可通过定义路由为prop:true,路由组件内部声明defineProps访问
- 可通过router-view设置插槽v-shot传递,但不建议,这样做所有视图组件都会收到props
导航守卫
(一) 分类
1. 全局守卫--通过router配置
router.beforeEach((to,from,next)=>{}) -- 全局前置守卫
导航确认前,组件内守卫解析前,异步组件解析前。用于权限验证、路由重定向。
router.beforeResolve((to,from,next)=>{}) -- 全局解析守卫
导航确认前,组件内守卫解析后,异步组件解析后。用于获取数据,最终的权限检查。
router.afterEach((to,from)=>{}) -- 全局后置钩子
导航确认后,页面更新后,不能使用next。用于分析、更改页面标题、声明页面。
接收一个异步函数(to,from,next)=>{}。
to: 即将进入的路由信息对象
from: 正在离开的路由信息对象
next:已移除,当仍支持
return: false--取消导航,newUrl-- 重定向,udefine/true--导航有效并调用下一个导航守卫
全局注入可以在全局守卫中访问
main.js
<script>
const app = createApp(APP)
app.provide('name','muggle')
</script>
router.js
<script>
router.beforeEach((to,from,next)=>{
const name = inject('name') // 'muggle'
})
</script>
2. 路由独享守卫--在每一条路由中单独配置
beforeEnter:(to,from)=>{} // 一个函数
beforeEnter:[fun1,fun2] // 多个函数,用于路由重用守卫
进入路由时触发,不会在params\query\hash改变时触发
<script>
function removeQueryParams(to){
return {
path:to.path,
query:{},
hash:to.hash
}
}
function removeHashParams(to){
return {
path:to.path,
query:to.quert,
hash:{},
}
}
const routes = [
{
path:'/user',
component:User,
beforeEnter:[removeQueryParams,removeHashParams]
},
{
path:'/introduction',
component:;Introduction,
beforeEnter:[removeQueryParams]
}
]
</script>
3. 组件内守卫 -- 在该路由组件的scirpt setup下使用
onBeforeRouteLeave((to,from)=>{})
对应选项式API的beforeRouteLeave,但无法访问this。当前路由改变,离开该组件时调用。
onBeforeRouteUpdate((to,from)=>{})
对应选项式API的beforeRouteUpdate,但无法访问this。当前路由改变,但组件复用时调用。如url为/user/:id,由/user/1--> /user/2,但渲染相同组件User。
<script setup>
import {onBeforeLeave,onBeforeUpdate} from 'vue-router'
onBeforeLeave((to,from)=>{
const answer = window.confirm('Do you want to leave?')
if(!answer){
return false
}
})
</script>
(二) 导航守卫解析流程
1. 导航被触发。
2. 在失活的组件里调用 onbeforeRouteLeave 守卫。
3. 调用全局的 beforeEach 守卫。
4. 在重用的组件里调用 onbeforeRouteUpdate 守卫(2.2+)。
5. 在路由配置里调用 beforeEnter。
6. 解析异步路由组件。
7. 在被激活的组件里调用 beforeRouteEnter。
8. 调用全局的 beforeResolve 守卫(2.5+)。
9. 导航被确认。
10. 调用全局的 afterEach 钩子。
11. 触发 DOM 更新。
12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
导航被触发 --> 失活组件内守卫onbeforeRouteLeave --> 全局前置守卫beforeEach --> 重用组件内守卫onbeforeRouteUpdate --> 路由独享守卫beforeEnter --> 解析异步路由组件 --> 激活组件内守卫beforeRouteEnter --> 全局解析守卫beforeReolve --> 导航被确认 --> 全局后置钩子afterEach --> DOM更新 --> beforeRouteEnter中的next回调函数
路由懒加载
将所有路由导入都替换成动态导入,这样vue router只会在第一次进入页面时才会获取这个函数,如何使用缓存数据
<script>
// import()返回一个promise,将组件配置为接收一个返回promise的函数
const User = ()=>import('./user.vue')
const routes = [
{
path:'/user',
component:User,
children:[
{
path:'post',
component:()=>import('./post.vue')
},
]
},
]
</script>
动态增添路由
添加路由记录
1. 常规路由表配置routes
2. router.addRoute(routeRecord)方法
// 添加一条路由
router.addRoute({path:'/user',component:User})
// 添加嵌套路由,user为父路由的名称
router.addRoute('user',{path:'posts',component:Post})
// router.addRoute()只是单纯添加一条路由记录,需要手动导航
router.push('/user')
router.replace({path:'/user',params:{id:1}})
删除路由记录
1. 添加一个名称冲突的路由,前一个路由就会被删除
router.addRoute({path:'/about',name:'about',component:About}) // 被删除
router.addRoute({path:'Other,name:'about',component:Other})
2. router.addRoute()返回一个删除路由的回调,调用即可删除
const removeUser = router.addRoute({path:'/user',component:User})
removeUser()
3. router.removeRoute(RouteName)方法
router.removeRoute('user')