Vue-router(路由)

01

一、什么是路由?

说起路由你想起了什么?

6244f119b27553762186b4d2aae57514.png

路由器,那路由器是用来做什么的,你有没有想过?

- 路由时决定数据包从来源到目的地的路径;

- 将输入端的数据转移到合映射表适的输出端;

- 路由中最重要的概念就是路由表:路由表的本质就是一个映射表,决定了数据包的指向

02

二、后端路由

1. 早期的网站开发整个HTML页面是由服务器来渲染的。服务器将渲染好的对应的HTML页面返回给客户端进行展示;

2. 但是一个网站包含很多页面,那服务器是怎么处理的呢?

  • 每个页面都有对应的网址,也就是URL

  • URL会发送给到服务器,服务器会通过正则对该URL进行匹配,最后交给Controller进行处理

  •  Controller进行处理,最终生成HTML或者数据,然后返回给前端。

  • 这其实就是服务器的一个IO操作,这其实就是对后端对路由的解析

3. 后端渲染的好处,相对于发送ajax请求拿数据,可以提高首屏渲染的性能,也有利于SEO的优化;

4. 后端路由的缺点:

  • 另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码

  • 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情

03

三、前端路由

前后端分离阶段:

  • 随着Ajax的出现,有了前后端分离的开发模式;

  • 后端只提供API来返回数据(json,xml),前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中

  • 这样做最大的优点就是前后端责任的清晰, 后端专注于数据上, 前端专注于交互和可视化上

  • 并且当移动端(iOS/Android)出现后, 后端不需要进行任何处理, 依然使用之前的一套API即可

  •  目前很多的网站依然采用这种模式开发

单页面应用阶段:

  • 其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由

  • 也就是前端来维护一套路由规则

前端路由的核心是什么呢?

  • 改变URL,但是页面不进行整体的刷新

04

四、前端路由规则

 1、URL的hash

 URL的hash也就是锚点(#), 本质上是改变window.location的href属性

我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新

 2、HTML5的history模式

history接口时HTML5新增的,它有5种模式改变URL而不刷新页面

acaf3d34efa35020dde898899240aa83.png

history.pushState(data, title, url)

00d0c198d12269bddd8e7555031440ed.png

history.replaceState(data, title, url)

e8c3bc7ab9c7f0f5e911d02cdec58018.png

history.go(-1) 返回上一页

history.back() 等价于 history.go(-1)

history.forward() 等价于 history.go(1)

05

五、Vue-router基本使用

    目前前端流行的三大框架,都有自己的路由实现:

  • Angular的ngRouter

  • React的ReactRouter

  • Vue的vue-router

1、认识vue-router

vue-router是Vue的官方路由插件,它和Vue是深度集成的,适合用于构建单页面应用  https://router.vuejs.org/zh/

vue-router是基于路由和组件,路由用于设定访问路径, 将路径和组件映射起来;在vue-router的单页面应用中, 页面的路径的改变就是组件的切换.

 2、安装router

一般项目中建议在cli创建项目时就直接选择需要路由,并搭配history模式。如果并未选择,那么安装教程请参照官网:

https://router.vuejs.org/zh/installation.html

 3、路由使用

在src下的views中,创建 Home.vue 及 User.vue,随意写入一些信息。找到 router/index.js 中:

import Vue from 'vue'import VueRouter from 'vue-router'import Home from '@/views/Home.vue'const User = resolve => { require.ensure(['@/views/User.vue'], () => { resolve(require('@/views/User.vue')) }) };Vue.use(VueRouter)  // 导入路由对象// 定义路由规则const routes = [    {        path: '/',        name: 'Home',        component: Home    },    {        path: '/user',        name: 'User',        // component: () => import(/* webpackChunkName: "user" */ '../views/User.vue')        component: User    }]// 创建路由实例const router = new VueRouter({    mode: 'history',  // vue路由只有两种模式,一种是hash,一种是history,这里使用历史模式    base: process.env.BASE_URL,    route})export default router

可以看到,我们有两种引入组件的方式。第一种比较能理解,第二种我们称之为“路由懒加载”。而这个懒加载中,有个 webpackChunkName,这东西我们称为魔法注释。

 魔法注释的作用:

  • webpack在打包的时候,对异步引入的库代码(lodash)进行代码分割时,为分割后的代码块取得名字。

  • Vue中运用import的懒加载语句以及webpack的魔法注释,在项目进行webpack打包的时候,对不同模块进行代码分割,在首屏加载时,用到哪个模块再加载哪个模块,实现懒加载进行页面的优化。

  • 当你 npm run build 之后,生成的js文件中,就能看到以魔法注释定义的js文件名。

4、懒加载

懒加载的方式

// 方式一: 结合Vue的异步组件和Webpack的代码分析const User = resolve => { require.ensure(['@/views/User.vue'], () => { resolve(require('@/views/User.vue')) }) };// 方式二: AMD写法const User = resolve => require(['@/views/User.vue'], resolve);// 方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.const Home = () => import(/* webpackChunkName: "user" */ '../views/User.vue')

5、路由模式

vue中的路由默认时hash模式,使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。如果不想要很丑的 hash,我们可以用路由的history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

  • hash 虽然出现URL中,但不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。

  • history模式提供了对历史记录进行修改的功能,只是当它们执行修改是,虽然改变了当前的URL,但你浏览器不会立即向后端发送请求。history模式,会出现404 的情况,需要后台配置。

 404 错误:

1、hash模式下,仅hash符号之前的内容会被包含在请求中,如 http://www.xxx.com, 因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回404错误;

2、history模式下,前端的url必须和实际向后端发起请求的url 一致,如http://www.xxx.com/book/id 。如果后端缺少对/book/id 的路由处理,将返回404错误。

 6、路由跳转方式

我们可以使用 `outer-link 标签来实现跳转,如:

<div id="nav">  <router-link to="/">Homerouter-link> |  <router-link to="/user">Userrouter-link>div><router-view/>

然后通过 router-view 来显示页面。router-link 最终会被渲染为a标签。

7、编程式导航

我们还有别的方式:

this.$router.push('/user')

我们除了push,还有replace、go、forward、back这几个来触发不同情况的跳转。

8、路由命名

当我们给路由命名后:

const router = new VueRouter({    routes: [        {            path: '/user/:userId',            name: 'User',      // 对路由进行命名            component: () => import(/* webpackChunkName: "user" */ '../views/User.vue')        }    ]})

我们就可以在跳转时,借用name属性实现跳转:

"{ name: 'user', params: { userId: 123 }}">User// 以上等同于router.push({ name: 'user', params: { userId: 123 }})

而在User组件中,可以通过 $route.params.userId 获取到参数:

<div>  用户页{{$route.params.userId}}div>

9、query传参

使用params传参,得到的结果与使用query传参得到的结果有以下区别:

this.$router.push({name: "User", params: {userId: 123}})    // http://localhost:8081/user/123this.$router.push({name: "User", query: {userId: 123}})       // http://localhost:8081/?userId=123

重调强调:

编程式导航中,使用name进行路径跳转,携带参数可以通过params和query,其中query会将参数携带在导航路径上,而使用path进行路径跳转,无法携带params,只能携带query。

10、路由重定向

const routes = [    {        path: '/',        redirect: '/home'    // 这就是路由的重定向,重新定义跳转路径    },    {        path: '/home',        component: () => import('@/views/Home.vue')    },    {        path: '/user',        component: () => import('@/views/User.vue')    },    {        path: '/detail',        component: () => import('@/views/Detail.vue')    },    {        path: '*',    // 匹配所有剩余的路由,只要不是上面提及的页面,全部跳转到404页面        component: () => import('@/views/404.vue')    }]

11、嵌套路由

const routes = [    {        path: '/',        // 这里可以直接使用重定向,指定到某个子路由        redirect: '/home',        component: () => import('@/views/Index.vue'),        // 使用children包裹其他组件,就可以将它们变成当前路由的子路由        children: [            {                path: '/home',                component: () => import('@/views/Home.vue')            },            {                path: '/user',                component: () => import('@/views/User.vue')            },            {                path: '/detail',                component: () => import('@/views/Detail.vue')            }        ]    },    {        path: '*',        component: () => import('@/views/404.vue')    }]

12、京东底部Tab栏

借助以上嵌套路由的模板,我们来写个京东底部Tab栏。新建 Index.vue :

<template> <div class="">     <router-view>router-view>     <ul>         <li :class="num==1 ? 'active' : ''"><router-link to="/home">主页router-link>li>         <li :class="num==2 ? 'active' : ''"><router-link to="/detail">详情页router-link>li>         <li :class="num==3 ? 'active' : ''"><router-link to="/user">用户页router-link>li>     ul> div>template><script>export default {  data() {    return {      num: 1    };  },  created() {    // 页面刷新时,可以监测当前路由地址,从而切换tab栏当前项    this.showWitch();  },  updated() {    // 每次页面更新,就监测路由地址,从而切换tab栏当前项    this.showWitch();  },  methods: {    // 定义一个方法,专门用来判断当前路由地址    showWitch() {      switch (this.$route.path) {        case "/home":          this.num = 1;          break;        case "/detail":          this.num = 2;          break;        case "/user":          this.num = 3;          break;        default:          this.num = 1;          break;      }    }  }};script><style lang = "less" scoped>* {  margin: 0;  padding: 0;  border: 0;  list-style: none;}ul {  width: 100%;  display: flex;  justify-content: space-around;  height: 40px;  line-height: 40px;  position: fixed;  bottom: 0;  left: 0;}li {  a {    color: #000;    font-size: 30px;    text-decoration: none;  }  &.active {    a {      color: red;    }  }}style>

注意:

$route 和 $router 的区别:$router 指的是整个项目的路由配置,所以里面就包含了跳转路由的方法(push、replace、go、back、forward);而 $route 指的是当前活跃的路由对象,所以能够获取当前页面的path、query、params

 传参补充:

一般进入商品的详情页,都会选择携带参数,而通过router-link标签,我们无法像编程式导航一样直接携带query或params,所以我们需要这么来:

// router文件中{    path: '/detail/:productId?'    // 这里加上?,是让这个参数变为可选项,不加问号的话,必须携带参数才能跳转到详情页  }  // router-link标签上:  "/detail/22">  // 详情页组件中:  {{$route.params.productId}}

实际上我们可以看得出来,在路由中携带参数的本质,还是通过params去传参。

 13、导航守卫

我们来考虑一个需求:在一个京东应用中,未登陆,不能进入订单确认页面 解决的方法: 1. 在跳转到订单确认页面前做判断,登陆成功则可以顺利跳转,否则就跳转到别的页面去,或者登陆页面; 2. 上面的这种方式是可行的,但是页面多起来的时候就不能每个页面都写,代码冗余不好维护 那有没有更好的方式呢?  1. 可以使用导航守卫 2. 什么是导航守卫?vue-router提供的导航守卫主要用来监听路由的进入和离开的;vue-router提供了beforeEach和afterEach的钩子函数,它们会在路由即将改变前和改变后触发

 前置导航守卫

src/router/index.js

导航钩子的三个参数解析:

1. to: 即将要进入的目标路由对象

2. from: 当前导航即将要离开的路由对象

3. next: 调用该方法后,才能进入下一个钩子函数

router.beforeEach((to, from, next) => {    next()  })

比如,我们强制要求,只能从详情页进入用户页,其他页面若要进入用户页,就会强制跳进详情页。具体代码:

// 定义导航守卫router.beforeEach((to, from, next) => {    if (to.path == '/user' && from.path != '/detail') {       alert('请先登录');      next('/detail')      return;    }    next(); // 没有next()方法,导航不会跳转  })

后置导航守卫

router.afterEach( route => {    console.log(route)  })

后置导航守卫 afterEach, 不需要主动调用next()函数

14、路由元信息

我们可以给任何一个路由添加 meta 元信息:

...// 定义路由规则const routes = [  {    path: '/',    redirect: '/home',    component: () => import('@/views/Index.vue'),    children: [      {        path: '/home',        component: () => import('@/views/Home.vue'),        meta: {          num: 0        }      },      {        path: '/user',        component: () => import('@/views/User.vue'),        meta: {          num: 0        }      },      {        path: '/detail',        component: () => import('@/views/Detail.vue'),        meta: {          num: 0        }      }    ]  },  {    path: '*',    component: () => import('@/views/404.vue')  }]...router.afterEach( route => {  console.log(route)})export default router

通过 $route.meta 可以获取到当前路由的元信息,如果想要获取当前组件纵向队列中所有路由的元信息(数组形式),那么可以:$route.matched 。

6dd2b59939cff3163eda3d4ae758eed2.png 42656cb78a13eb48886ab303d43513ce.png

把分享当成一种习惯

Logo

前往低代码交流专区

更多推荐