清茶书香

一杯清茶,一本书籍,一个下午。


  • 首页

  • 归档

  • 分类

  • 关于

  • 搜索
Redis JPA Solr SpringData SpringMVC localRepository local Mapper 事务 Mybatis JDBC AOP DI IOC 常用函数 触发器 存储过程 Promise Gateway SpringCloud vue-cli axios es6 webpack npm vue 个性化 zsh 终端 caffeine jvm缓存 guava cache validation Mapping MapStruct comment 小程序 建站 WeHalo config logback plugins database idea maven spring https http nginx password RabbitMQ 秒杀系统 Windows MySQL 数据备份 halo SpringBoot shell Linux ip Optional Stream Lambda k8s Docker 列编辑 vim MacOS 图片合成 Java 远程联调 nps 内网穿透

vue学习记录之vue路由组件vue-router

发表于 2022-01-17 | 分类于 vue | 0 | 阅读次数 271

前言

在使用前端框架之前链接其他页面我们可以使用<a>标签来实现,但是使用了vue等框架后,我们的源码都是各种.vue文件,自然无法再使用<a>标签进行跳转,如果要想使用<a>标签实现则必须知道编译后的路径+名称,这种虽然能做到但是太过费事。为此Vue官方为我们开发者提供了vue-router来实现页面跳转,它的作用就跟<a>标签类似。

安装

CDN使用

https://unpkg.com/vue-router@4

npm安装

npm i vue-router@4

一般在使用vue-cli创建项目时勾选vue-router自动会生成示例代码.

快速开始

需要用到路由的页面

<template>
  <router-link to="/"/>
  <!-- 对于命名路由还可以通过名称跳转 -->
  <router-link to="{name: 'home'}"/>
  <router-link to="/hello"/>
  
  <!-- 通过此标签展示跳转后的页面内容 -->
  <router-view/>
</template>

对路由进行配置的JavaScript

import {createRouter, createWebHistory} from 'vue-router';
import HelloWorld from "@/components/HelloWorld";

const routes = [
    {
        name: 'Home', // 设置了name属性的路由被称为命名路由
        path: '/',
        // 路由懒加载(推荐)
        component: () => {
            import('@/components/Home')
        }
    },
    {
        name: 'HelloWorld',
        path: 'hello',
        // 常规加载
        component: HelloWorld
    }
];

const router = createRouter({
    history: createWebHistory(process.env.BASE_URL),
    routes,
})


export default router;

把上面这个配置在vue对象中用上.

import { createApp } from 'vue'
import App from './App.vue'
// @是src的别名
import router from "@/router";

const app =  createApp(App)
app.use(router)
app.mount('#app')

基础

路由参数获取

可以使用$route.query或者$route.params来获取。

  • $route.query中存放的是URL中的参数(/api/user?userId=123)
  • $route.params中存放的是URL中的路径参数(/api/user/123,123即为路径参数)

路由跳转

此处的路由跳转指的是通过JavaScript的方式实现跳转。

路由跳转可以使用$router.push()函数实现,$router中还提供了其他的实用函数,例如:$router.go()、$router.replace()等。

以下几个API就是模仿的window.history的API,用的时候不会有太多陌生感。

  • $router.push({path: '', query: {}})
  • $router.go(1)在历史记录中前进,与$router.forward()等价
  • $router.go(-1)在历史记录中后退,与$router.back()等价
  • $router.push({path: '/home', replace: true})替换当前位置(不会记录在历史记录中),它等价于$router.replace({path: '/home'})。
// !!! 需要注意的是
$router.push({path: '', params: {}})
// 这种情况下params会被忽略,它们两个不能用在一起。可以使用下面两种替代

// 方式1
$router.push({name: 'home', params: {user: 123}})
// 方式2
$router.push({path: `/home/${user}`})

动态路由

SpringWebMVC中有@RequestMapping支持路径参数、正则语法,vue-router中支持这样的使用。

const routes = [
  // 动态段以冒号开始
  { path: '/users/:id', component: UserDemo },
]

这个路由能匹配到/users/abc、/users/1234等,其中abc、1234可以通过$route.params.id获取到。一个路由上还可以设置多个路径参数。

路由匹配路径$route.params
/users/:id/user/abc{id:abc}
/users/:id/:type/user/abc/1234{id:abc,type:1234}

使用带有参数的路由时需要注意的是,当用户从/users/johnny导航到/users/jolyne时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用。

404路由

const routes = [
  // 将匹配所有内容并将其放在$route.params.pathMatch下
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound }
]

高级匹配模式

一般使用静态路由/abc和动态路由/:abc就够我们用了,但vue-router还为我们提供了正则匹配模式。

自定义正则

const routes = [
  // /:orderId -> 仅匹配数字
  { path: '/:orderId(\\d+)' },
  // /:productName -> 匹配其他任何内容
  { path: '/:productName' },
]

可重复参数

如果要匹配多级路由比如/abc/1234/xyz可以使用*(将参数重复0次或多次)和+(将参数重复1次或多次)。

const routes = [
  // /:chapters ->  匹配 /one, /one/two, /one/two/three, 等
  { path: '/:chapters+' },
  // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
  { path: '/:chapters*' },

  // 仅匹配数字
  // 匹配 /1, /1/2, 等
  { path: '/:chapters(\\d+)+' },
  // 匹配 /, /1, /1/2, 等
  { path: '/:chapters(\\d+)*' },
]

此时如果使用$router.push进行跳转需要传入数组类型参数方可。$router.push({name: 'home', params: {chapters: ['one', 'two']}})

可选参数

如果要想让一个参数可传可不传则可以使用?(0次或1次)。

const routes = [
  // 匹配 /users 和 /users/posva
  { path: '/users/:userId?' },
  // 匹配 /users 和 /users/42
  { path: '/users/:userId(\\d+)?' },
]

*号虽然也可以做到,但它与?的区别是?参数只能用0或者1次,而*则还可以让参数用多次。

嵌套路由

有些项目中会存在一些嵌套组件,比如用户页面是由个人信息、设置、收藏等组件组成的,而这些组件假设也有对应的路由,并且点击后是在用户这个组件中展示(类似于手风琴式的UI),这种路由就属于嵌套路由。

对于嵌套路由vue-router提供了children属性来配置:

const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      // 当 /user/:id 匹配成功
      // UserHome 将被渲染到 User 的 <router-view> 内部
      { path: '', component: UserHome },

      {
        // 当 /user/:id/profile 匹配成功
        // UserProfile 将被渲染到 User 的 <router-view> 内部
        path: 'profile',
        component: UserProfile,
      },
      {
        // 当 /user/:id/posts 匹配成功
        // UserPosts 将被渲染到 User 的 <router-view> 内部
        path: 'posts',
        component: UserPosts,
      },
    ],
  },
]
<!-- User Component -->
<template>
  ...

  <router-view/>
</template>

命名视图

如果想同时展示多个视图,并且不是嵌套展示,这时候就需要命名视图了。
router-view默认的name属性值为default,多个视图就可以用多个router-view并设置上name属性(必须否则将会展示同样的内容)。

<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 routes = [
    {
      path: '/',
      components: {
        default: Home,
        // LeftSidebar: LeftSidebar 的缩写
        LeftSidebar,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        RightSidebar,
      },
    },
  ],

嵌套命名视图同理,在需要嵌套的组件中使用router-view组件,并在嵌套路由中使用命名视图即可。

重定向和别名

重定向

在使用redirect时可以不用写component,因为它是直接通过重定向后的配置访问的组件,这里可以不配置,除非是嵌套路由。

// path的方式
const router = [{path: "/abc", redirect: "/xyz"}]

// name的方式
const router = [{path: "/abc", redirect: {name: "demo"}}]

//方法的形式
const router = [{
    path: "/abc/:s", redirect: to => {
        // to和$route是一样的对象类型
        return {path: "/s", query: {q: to.params.s}}
    }
}]

别名

重定向是指当用户访问/home时,URL 会被/替换,然后匹配成/。那么什么是别名呢?

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

const routes = [{ path: '/', component: Homepage, alias: '/home' }]

嵌套路由中使用别名可以使用绝对路径来改变这个路由的路径

const routes = [
  {
    path: '/users',
    component: UsersLayout,
    children: [
      // 为这 3 个 URL 呈现 UserList
      // - /users
      // - /users/list
      // - /people
      { path: '', component: UserList, alias: ['/people', 'list'] },
    ],
  },
]

如果路由中有参数,那么绝对路径的别名路由必须包含此参数

const routes = [
  {
    path: '/users/:id',
    component: UsersByIdLayout,
    children: [
      // 为这 3 个 URL 呈现 UserDetails
      // - /users/24
      // - /users/24/profile
      // - /24
      { path: 'profile', component: UserDetails, alias: ['/:id', ''] },
    ],
  },
]

路由组件传参

vue-router提供了另一种传参给组件的方法,那就是props属性。props拥有布尔模式、对象模式、函数模式。

  • 布尔模式

props为true时$router.params将被设置为组件的props

const routes = [{ path: '/user/:id', component: User, props: true }]

// 命名视图必须为为每个视图定义props的值
const routes = [
  {
    path: '/user/:id',
    components: { default: User, sidebar: Sidebar },
    props: { default: true, sidebar: false }
  }
]
  • 对象模式

props是对象时,内容会原样设置到组件的props中。

const routes = [
  {
    path: '/user/profile',
    component: User,
    props: { isLogin: true }
  }
]
  • 函数模式

也可以是一个函数,参数为$route类型,返回值将被设置到组件的props中。

const routes = [
  {
    path: '/user',
    component: User,
    props: route => ({ name: route.query.abc })
  }
]

/user?abc=lisi这个URL将传递{name: 'lisi'}给User组件。

历史模式

创建路由时demo使用的历史模式是HTML5模式,除此还有Hash模式。

  • HTML5模式

这种模式对SEO很友好,URL看上去与正常的URL没什么区别。

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [],
})

但如果用户直接访问http://demo.com/user/123会是404错误,这需要在服务器配置一个会退路由来解决,让其为匹配到URL时找到你程序的index.html。

以下仅提供Nginx的配置,其他服务器请看官方文档。

location / {
  try_files $uri $uri/ /index.html;
}
  • Hash模式

Hash模式会在URL中用哈希字符#隔开实际上用到的URL,这种模式与常规URL不同,因此对SEO不是很友好。

import { createRouter, createWebHashHistory } from 'vue-router'

const router = createRouter({
  history: createWebHashHistory(),
  routes: [],
})

进阶内容

导航守卫

全局前置守卫

const router = createRouter({ ... })

router.beforeEach((to, from) => {
  // ...
  // 返回 false 以取消导航
  return false
})
  • to目标路由对象
  • from来源路由对象
  • 可以返回false来取消导航,也可以返回一个路由地址(可以是一个字符串或路由对象,返回的内容跟router.push()中可以用的参数是一样的)

除此支持旧版的第三个参数next

router.beforeEach((to, from, next) => {
    if (to.name !== 'Login' && !isAuthenticated) {
        next({name: 'Login'})
    } else {
        next()
    }
})

全局解析守卫

router.beforeResolve(async to => {
  if (to.meta.requiresCamera) {
    try {
      await askForCameraPermission()
    } catch (error) {
      if (error instanceof NotAllowedError) {
        // ... 处理错误,然后取消导航
        return false
      } else {
        // 意料之外的错误,取消导航并把错误传给全局处理器
        throw error
      }
    }
  }
})

全局后置钩子

router.afterEach((to, from) => {
  console.log(to.fullPath)
})

// 没有next参数,只有failure,在此钩子无法终止导航
router.afterEach((to, from, failure) => {
  if(failure){
    console.log(to.fullPath);
  }
})

这三个守卫/钩子跟拦截器的效果差不多,顺序就是按命名来理解。

路由级导航守卫

此导航守卫只在进入路由时才会触发,不会在params、query、hash变更时触发。

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from) => {
      // 返回值与全局前置导航守卫一样
      return true;
    },
  },
]

组件内导航守卫

提供三个可用的API:

  • beforeRouteEnter
  • beforeRouteUpdate
  • beforeRouteLeave
export default {
  name: 'Demo',
  beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例`this`,因为当守卫执行时,组件实例还没被创建!
  },
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 比如用的是一个带有动态参数的路径 `/users/:id`,参数变化组件会复用
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },
}

在beforeRouteEnter中虽然无法直接使用this访问实例,但是可以用next参数,在导航确认后调用此回调函数访问实例。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

完整的导航流程

  1. 导航被触发。
  2. 在失活的组件里调用beforeRouteLeave守卫。
  3. 调用全局的beforeEach守卫。
  4. 在重用的组件里调用beforeRouteUpdate守卫(2.2+)。
  5. 在路由配置里调用beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用beforeRouteEnter。
  8. 调用全局的beforeResolve守卫(2.5+)。
  9. 导航被确认。
  10. 调用全局的afterEach钩子。
  11. 触发DOM更新。
  12. 调用beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。

数据元信息

const routes = [
    {
        path: '/',
        name: 'Default',
        component: () => import('@/views/home/Home'),
        meta: {
            title: '首页',
            needAuth: false
        }
    }
];

router.beforeEach((to, from) => {
    // 设置每个路由页面的标题
    document.title = to.meta.title;
    return true;
})

参考文档

  • vue-router官方文档
Bennett wechat
欢迎收藏我的微信小程序,方便查看更新的文章。
  • 本文作者: Bennett
  • 本文链接: https://hibennett.cn/archives/study-vue-router
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# vue
清理docker镜像
Linux使用zsh终端及辅助工具oh-my-zsh
  • 文章目录
  • 站点概览
Bennett

Bennett

60 日志
28 分类
74 标签
RSS
Github E-mail Gitee QQ
Creative Commons
Links
  • MacWk
  • 知了
0%
© 2020 — 2023 hibennett.cn版权所有
由 Halo 强力驱动
|
主题 - NexT.Pisces v5.1.4

浙公网安备 33010802011246号

    |    浙ICP备2020040857号-1