Vue3——Router4教程(小满版本)(二)

简介: Vue3——Router4教程(小满版本)

Vue3——Router4教程(小满版本)(一)https://developer.aliyun.com/article/1470378

第六章——命名视图

default:默认的意思,这个具体是什么意思呢?就是说你默认显示出来的就是这个default


,就可以具体显示你想要的页面(你可以定义多个view也就是视图),跳转的时候同时显示多个视图的内容


命名视图可以在同一级(同一个组件)中展示更多的路由视图,而不是嵌套显示。 命名视图可以让一个组件中具有多个路由渲染出口,这对于一些特定的布局组件非常有用。 命名视图的概念非常类似于 “具名插槽”,并且视图的默认名称也是 default

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
 
 
const routes: Array<RouteRecordRaw> = [
    {
        path: "/",
        components: {//注意这里发生了变化,多了一个s
            default: () => import('../components/layout/menu.vue'),//转到默认的
            header: () => import('../components/layout/header.vue'),//router-view里面的name填header,下面content同理,这个一旦填写了,我们在template中可以定义多个<router-view>视图,然后跳转的时候同时显示多个视图的内容
            content: () => import('../components/layout/content.vue'),
        }
    },
]
 
const router = createRouter({
    history: createWebHistory(),
    routes
})
 
export default router

对应 Router-view 通过 name 对应组件

<div>
        <router-view></router-view>
        <router-view name="header"></router-view>
        <router-view name="content"></router-view>
    </div>
  • 注意事项
  • 这个是components而不是component,因为通过这个多加一个s的,我们可以一次性跳转到多个路由视图中,需要在template中去额外定义多的路由视图,并且通过你在components中取的名字来进行绑定,比如在上面代码块中,我们使用了header跟content这两个名字,那在使用的时候就需要name绑定这两个名字

第七章——重定向-别名

重定向 - redirect

字符串形式配置,访问 / 重定向到 /user (地址栏显示 /, 内容为 /user 路由的内容)


他的属性名就是redirect:

三次书写形式

  • 字符串的写法:

const routes: Array<RouteRecordRaw> = [
    {
        path:'/',
        component:()=> import('../components/root.vue'),
        redirect:'/user1',//写法上的区别在这里,这redirect是字符串的写法
        children:[
            {
                path:'/user1',
                components:{
                    default:()=> import('../components/A.vue')
                }
            },
            {
                path:'/user2',
                components:{
                    bbb:()=> import('../components/B.vue'),
                    ccc:()=> import('../components/C.vue')
                }
            }
        ]
    }
]


  • 对象的写法:

const routes: Array<RouteRecordRaw> = [
    {
        path: '/',
        component: () => import('../components/root.vue'),
        redirect: { path: '/user1' },//这里是对象形式的写法,path也会自动对应到下方children的path(子路由)中
        children: [
            {
                path: '/user1',
                components: {
                    default: () => import('../components/A.vue')
                }
            },
            {
                path: '/user2',
                components: {
                    bbb: () => import('../components/B.vue'),
                    ccc: () => import('../components/C.vue')
                }
            }
        ]
    }
]


  • 函数模式(可以传参)=> 回调形式

const routes: Array<RouteRecordRaw> = [
    {
        path: '/',
        component: () => import('../components/root.vue'),
        redirect: (to) => {//写法是以to的形式,把to括起来的这个括号可加可不加
            return {//必须要返回值,也就是return
                path: '/user1',
                query: to.query//由于这里to没有传值过来,我们可以暂时自己写一个
                //query:{name:"小余"},这个name=小余会在URL,也就是路径中体现出来
            }
        },
        children: [
            {
                path: '/user1',
                components: {
                    default: () => import('../components/A.vue')
                }
            },
            {
                path: '/user2',
                components: {
                    bbb: () => import('../components/B.vue'),
                    ccc: () => import('../components/C.vue')
                }
            }
        ]
    }
]


别名 alias

/ 别名为 /root,意味着当用户访问 /root 时,URL 仍然是 /user,但会被匹配为用户正在访问 /


通俗的说就是给路由取多个名字,但访问的组件都是同一个(比如我们都知道鲁迅跟周树人,这是两个名字,但其实都是指同一个人)

const routes: Array<RouteRecordRaw> = [
    {
        path: '/',//我就是下面说的path路径
        component: () => import('../components/root.vue'),//访问/root,/root2,/root3都是展示这个组件
        alias:["/root","/root2","/root3"],//三个名字,或者说是上面这个path路径的外号或者说是别名
        children: [
            {
                path: 'user1',
                components: {
                    default: () => import('../components/A.vue')
                }
            },
            {
                path: 'user2',
                components: {
                    bbb: () => import('../components/B.vue'),
                    ccc: () => import('../components/C.vue')
                }
            }
        ]
    }
]

第八章——导航守卫-前置守卫

全局前置守卫

router.beforeEach


小满称呼:中间件

//所有跳转、后退都会走这个函数
router.beforeEach((to, form, next) => {
    console.log(to, form);
    next()
})

小满在这里做的是一个登录的校验,使用了组件库这里都不进行说明

小满在这里使用了一个@别名,在这里对这个进行一个解释


在vite.config.ts文件下,代码如下
export default defineConfig({
    plugins:[vue(),vueJsx()],
    resolve:{
        alias:{
            '@':fileURLToPath(new URL('./src',import.meta.url)),
        }
    }
})


  • 这样的作用是对你的文件起了一个别名,此处的@的别名对应的是./src。从上面的路径中我们也可以看到

resolve.alias

在组件之间相互引用时,可能是下面这样的:

import Hello from '../src.components/Hello';

其中的路径是相对于当前页面的。 但是如果嵌套等更为复杂,那么写起来会比较麻烦。但是如果我们通过这样的配置:

resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@pages': path.join(__dirname, "..", "src", "pages"),
      "@components": path.join(__dirname, "..", "src", "components"),
      // 注意: 静态资源通过src,不能这么设置。
      // "@assets": path.join(__dirname, "..", "src", "assets"),
    }

其中 vue$ 表示引入 vue,就可以像下面这么写:

import Vue from 'vue'

另外,对于 @pages @components 我们就可以直接引用了,而省去了一大堆的复杂应用,另外通过 @可以消除歧义。如下所示:

import Hello from '@components/Hello';
import App from '@pages/App'

**值得注意的时: 在 webpack.config.js 中我们不能使用../ 以及./ 这种形式的路径方式,而是通过 path.join 和 __dirname 这种形式来表示路径,否则会报错。**

另外: 在组件中,我们会引用一些静态文件,即 static 下的文件, 这时我们就不能用 alias 下的配置了,而必须使用一般的配置方式。

我没找到vite的,根据webpack触类旁通

const rules = reactive({
    user:[
        {
            required:true,//证明是不是必填
            message:"失败给提示信息",
            type:"string",//类型
            tiggle:"change"//触发的条件
        }
    ]
})

每个守卫方法接收三个参数

to: Route, 即将要进入的目标 路由对象;
from: Route,当前导航正要离开的路由;
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。

案例 权限判断

const whileList = ['/']//白名单
 
router.beforeEach((to, from, next) => {
    let token = localStorage.getItem('token')
    //白名单 有值 或者登陆过存储了token信息可以跳转 否则就去登录页面
    if (whileList.includes(to.path) || token) {
        next()//next()是放行的意思
    } else {
        next({
            path:'/'//回到登录界面
        })
    }
})

呃,这个使用组件库的过程不好写笔记,建议自己看看视频

全局后置守卫(内容较深,后续回来补笔记)

使用场景一般可以用来做 loadingBar


你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:


和前置路由守卫相比为什么没有next功能呢?那是因为前置路由守卫是看大门前面的,而后置路由守卫则是在大门的后面。前面的保安如果都放行进来了,那后面的保安还要再确认一遍就太过繁复也没有必要啦

router.afterEach((to,from)=>{
    Vnode.component?.exposed?.endLoading()
})

loadingBar 组件

<template>
    <div class="wraps">
        <div ref="bar" class="bar"></div>
    </div>
</template>
    
<script setup lang='ts'>
import { ref, onMounted } from 'vue'
let speed = ref<number>(1)//这里是通过TS的规范类型为数字number类型,以下类似
let bar = ref<HTMLElement>()
let timer = ref<number>(0)//默认为0
const startLoading = () => {//开始进度条
    let dom = bar.value as HTMLElement;//因为有可能为undefined,所以我们使用as进行一个断言为HTMLElement
    speed.value = 1//初始化speed的值
    timer.value = window.requestAnimationFrame(function fn() {//requestAnimationFrame会将回流重绘收集起来只走一次,性能会更好。这里可以使用箭头函数,但我们选择使用function。回调函数只走一次,所以我们起了一个fn的名字拿去递归
        if (speed.value < 90) {
            speed.value += 1;
            dom.style.width = speed.value + '%'
            timer.value = window.requestAnimationFrame(fn)//递归
        } else {
            speed.value = 1;
            window.cancelAnimationFrame(timer.value)
        }
    })
 
}
 
const endLoading = () => {//结束进度条
    let dom = bar.value as HTMLElement;
    setTimeout(() => {
        window.requestAnimationFrame(() => {//箭头函数的写法
            speed.value = 100;//其实显示的就是进度条的进度,结束了就是100%
            dom.style.width = speed.value + '%'
        })
    }, 500)
 
}
 
 
defineExpose({//defineExpose 可以将方法主动暴露出来,通过对象的形式。然后能够在父组件中进行接收
    //当父组件通过模板引用的方式获取到当前组件的实例,获取到的实例会像这样 { startLoading: number, endLoading: number } (ref 会和在普通实例中一样被自动解包)
    startLoading,
    endLoading
})
</script>
    
<style scoped lang="less">//样式
.wraps {
    position: fixed;
    top: 0;
    width: 100%;//这些都是决定进度条的样式的
    height: 2px;
    .bar {
        height: inherit;
        width: 0;
        background: blue;
    }
}
</style>

main.ts

import loadingBar from './components/loadingBar.vue'
const Vnode = createVNode(loadingBar)//createVNode是Vue3中提供的方法,用于创造DOM节点
render(Vnode, document.body)
console.log(Vnode);
 
router.beforeEach((to, from, next) => {
    Vnode.component?.exposed?.startLoading()//问号为可选的意思
})
 
router.afterEach((to, from) => {
    Vnode.component?.exposed?.endLoading()
})

上述牵扯到的知识点补充:


计时器一直是javascriptz动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样
才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化


大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms


而setTimeout和setIntervall的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器U线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行


requestAnimationFrame.采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果

requestAnimationFrame 比起 setTimeout、setInterval 的优势主要有两点:


1、requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒 60 帧。


2、在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的的 cpu,gpu 和内存使用量。

第九章——路由元信息

路由元信息


通过路由记录的 meta 属性可以定义路由的元信息。使用路由元信息可以在路由中附加自定义的数据,例如:


  • 权限校验标识。
  • 路由组件的过渡名称。
  • 路由组件持久化缓存 (keep-alive) 的相关配置。
  • 标题名称

有时我们想将一些信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的 meta 属性来实现,并且它可以在路由地址和导航守卫上都被访问到。定义路由的时候你可以这样配置 meta 字段


我们可以在导航守卫或者是路由对象中访问路由的元信息数据。

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      component: () => import('@/views/Login.vue'),
      meta: {
        title: "登录"//这里定义什么都行,最终这里的数据是会被获取到的
      }
    },
    {
      path: '/index',
      component: () => import('@/views/Index.vue'),
      meta: {
        title: "首页",
      }
    }
  ]
})

使用 TS 扩展

如果不使用扩展 将会是 unknow 类型

declare module 'vue-router' {
  interface RouteMeta {//接口写法,将title变成是否可选的string类型
    title?: string//这个问号不写也没有关系,意思就变为title必须为string(字符串)类型
  }
}

如何获取元信息

在组件中可以通过 this.$route.meta 来获取

在路由中获取:

router.beforeEach((to, from, next) => {
 //通过to.meta来获取路由元信息
 console.log(to.meta.requiresAuth);
})
  • 通过这类方法我们可以在配置文件中通过router.beforeEach,也就是前置路由守卫中进行设置
  • 前置路由守卫有三个参数分别为tofromnext。我们这里使用到to,意思是将要去哪里的意思,我们通过to.meta.title拿到了我们在routers中对应的路由中配置的mate中的title,然后将这个值赋值给document.title
  • 产生的效果就是能够在路由跳转后,网页的标题也随之发生改变,变成对应路由的信息(比如我们设定好的登录和首页)

第十章——路由过渡动效

过渡动效


想要在你的路径组件上使用转场,并对导航进行动画处理,你需要使用 v-slot API


使用组件库的时候记得先安装(npm安装方式:npm install animate.css)后import引入

<router-view #default="{route,Component}">//#default,像这种使用井号的是简写方式
        //能解出来两个属性,route为当前路由的信息,Component就是当前的组件
        <transition  :enter-active-class="`animate__animated ${route.meta.transition}`">//animate__animated是动画组件库的前缀,然后active-class就是高亮样式的选项,动画组件库和路由的结合形成的过度效果
            <component :is="Component"></component>
        </transition>
    </router-view>

上面的用法会对所有的路由使用相同的过渡。如果你想让每个路由的组件有不同的过渡,你可以将元信息和动态的 name 结合在一起,放在 上:


declare module 'vue-router'{
     interface RouteMeta {
        title:string,
        transition:string,
     }
}
 
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      component: () => import('@/views/Login.vue'),
      meta:{
         title:"登录页面",
         transition:"animate__fadeInUp",//动画组件库
      }
    },
    {
      path: '/index',
      component: () => import('@/views/Index.vue'),
      meta:{
         title:"首页!!!",
         transition:"animate__bounceIn",
      }
    }
  ]
})

第十一章——滚动行为

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。vue-router 可以自定义路由切换时页面如何滚动。


当创建一个 Router 实例,你可以提供一个 scrollBehavior 方法

const router = createRouter({
  history: createWebHistory(),
  scrollBehavior: (to, from, savePosition) => {//scrollBehavior滚动的属性,savePosition参数为标记的距离
  if(savePosition){
        return savePosition
    }else{
        return{
            top:0//或者可以不进行判断直接return你想要的数据,就跟随你的心意距离顶部还有多少距离
        }
    }
  },
const router = createRouter({
  history: createWebHistory(),
  scrollBehavior: (to, from, savePosition) => {//scrollBehavior滚动的属性,savePosition参数为标记的距离
    console.log(to, '==============>', savePosition);
    return new Promise((r) => {//异步
      setTimeout(() => {
        r({
          top: 10000//top是Vue3的写法
        })
      }, 2000);
    })
  },

scrollBehavior 方法接收 to 和 from 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进 / 后退 按钮触发) 时才可用。

scrollBehavior 返回滚动位置的对象信息,长这样:

  • { left: number, top: number }
const router = createRouter({
    history: createWebHistory(),
    scrollBehavior: (to, from, savePosition) => {
        return {
            top: 200//这个就是跟随心意的写法了,不进行if判断,直接根据自己喜好返回距离顶部的距离
        }
    }
})

第十二章——动态路由

我们一般使用动态路由都是后台会返回一个路由表前端通过调接口拿到后处理 (后端处理路由)


主要使用的方法就是 router.addRoute


调用接口需要安装axios,命令:npm install axios -S


import引入axios

添加路由

动态路由主要通过两个函数实现。router.addRoute()router.removeRoute()。它们只注册一个新的路由,也就是说,如果新增加的路由与当前位置相匹配,就需要你用 router.push()router.replace() 来手动导航,才能显示该新路由

router.addRoute({ path: '/about', component: About })

删除路由

有几个不同的方法来删除现有的路由:

  • 通过添加一个名称冲突的路由。如果添加与现有途径名称相同的途径,会先删除路由,再添加路由:
router.addRoute({ path: '/about', name: 'about', component: About })
// 这将会删除之前已经添加的路由,因为他们具有相同的名字且名字必须是唯一的
router.addRoute({ path: '/other', name: 'about', component: Other })


  • 通过调用 router.addRoute() 返回的回调:
const removeRoute = router.addRoute(routeRecord)
removeRoute() // 删除路由如果存在的话


  • 当路由没有名称时,这很有用。
  • 通过使用 router.removeRoute() 按名称删除路由:
router.addRoute({ path: '/about', name: 'about', component: About })
// 删除路由
router.removeRoute('about')


当路由被删除时,所有的别名和子路由也会被同时删除

查看现有路由

Vue Router 提供了两个功能来查看现有的路由:

案例

前端代码


注意一个事项 vite 在使用动态路由的时候无法使用别名 @ 必须使用相对路径

const initRouter = async () => {
    const result = await axios.get('http://localhost:9999/login', { params: formInline });//get是params参数,post没有限制。formInline是表单的信息,通过.user.password之类的能获取到账号密码。
    result.data.route.forEach((v: any) => {//后端返回的信息进行动态添加
        router.addRoute({
            path: v.path,
            name: v.name,
                                    //这儿不能使用@
            component: () => import(`../views/${v.component}`)//最关键的组件需要我们动态的去拼接,这里使用模板字符串
            //然后就是import()动态引入
        })
        router.push('/index')//跳转页面,验证通过,进入登录页面
    })
    console.log(router.getRoutes());
 
}

后端代码 nodejs express

import express, { Express, Request, Response } from 'express'
 
const app: Express = express()
 
app.get('/login', (req: Request, res: Response) => {//这里使用get请求所以必须使用query参数
    res.header("Access-Control-Allow-Origin", "*");
    if (req.query.user == 'admin' && req.query.password == '123456') {//根据前端传过来的信息返回不同的信息回去
        res.json({
            route: [
                {
                    path: "/demo1",
                    name: "Demo1",
                    component: 'demo1.vue'
                },
                {
                    path: "/demo2",
                    name: "Demo2",
                    component: 'demo2.vue'
                },
                {
                    path: "/demo3",
                    name: "Demo3",
                    component: 'demo3.vue'
                }
            ]
        })
    }else{
        res.json({
            code:400,
            mesage:"账号密码错误"
        })
    }
})
 
app.listen(9999, () => {
    console.log('http://localhost:9999');
 
})


目录
相关文章
|
5天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
2天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
15 7
|
3天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
16 3
|
2天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
15 1
|
2天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
13 1
|
4天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
|
5天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
6天前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
6天前
|
缓存 JavaScript 搜索推荐
Vue SSR(服务端渲染)预渲染的工作原理
【10月更文挑战第23天】Vue SSR 预渲染通过一系列复杂的步骤和机制,实现了在服务器端生成静态 HTML 页面的目标。它为提升 Vue 应用的性能、SEO 效果以及用户体验提供了有力的支持。随着技术的不断发展,Vue SSR 预渲染技术也将不断完善和创新,以适应不断变化的互联网环境和用户需求。
24 9
|
5天前
|
缓存 JavaScript UED
Vue 中实现组件的懒加载
【10月更文挑战第23天】组件的懒加载是 Vue 应用中提高性能的重要手段之一。通过合理运用动态导入、路由配置等方式,可以实现组件的按需加载,减少资源浪费,提高应用的响应速度和用户体验。在实际应用中,需要根据具体情况选择合适的懒加载方式,并结合性能优化的其他措施,以打造更高效、更优质的 Vue 应用。