前端实习准备 — Vue路由相关

41 阅读6分钟

路由插件的创建

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')