• Vue (十四) --------- vue-router (路由)



    一、路由概述

    路由,其实就是指向的意思,当我点击页面上的 home 按钮时,页面中就要显示home 的内容,如果点击页面上的 about 按钮,页面中就要显示 about 的内容。home 按钮 --> home 内容, about 按钮 --> about 内容,也可以说是一种映射。所以在页面上有两个部分,一个是点击部分,一个是点击之后,显示内容的部分。

    点击之后,怎么做到正确的对应,比如,我点击 home 按钮,页面中怎么就正好
    能显示 home 的内容。这就要在 js 文件中配置路由。

    路由中有三个基本的概念 route、routes、router。

    route,是一条路由, home 按钮 --> home内容, 这是一条 route, about 按钮 --> about 内容,这是另一条 route。

    routes 是一组路由,把上面的每一条路由组合起来,形成一个数组。[ { home 按钮 -->home内容 }, { about按钮 -->about 内容} ]

    router 是一个机制,相当于一个管理者,它来管理路由。因为 routes 只是定义了一组路由,它放在哪里是静止的,当真正来了请求,怎么办? 就是当用户点击home 按钮的时候,怎么办 ?这时 router 就起作用了,它到 routes 中去查找,去找到对应的 home 内容,所以页面中就显示了 home 内容。

    客户端中的路由,实际上就是 dom 元素的显示和隐藏。当页面中显示 home 内容的时候,about 中的内容全部隐藏,反之也是一样。客户端路由有两种实现方式:基于 hash 和基于 html5 history api。

    vue-router 是 vue 的一个插件库,专门用来实现 SPA 应用。

    关于 SPA 应用:

    SPA 是一种特殊的 Web 应用,是加载单个 HTML 页面并在用户与应用程序交互时动态更新该页面的。它将所有的活动局限于一个 Web 页面中,仅在该 Web 页面初始化时加载相应的 HTML 、 JavaScript 、 CSS 。一旦页面加载完成, SPA 不会因为用户的操作而进行页面的重新加载或跳转,而是利用 JavaScript 动态的变换 HTML(采用的是 div 切换显示和隐藏),从而实现UI与用户的交互。在 SPA 应用中,应用加载之后就不会再有整页刷新。相反,展示逻辑预先加载,并有赖于内容 Region(区域)中的视图切换来展示内容。

    二、路由分类

    路由可以分为前端路由后端路由

    什么是前端路由?

    特点:不向后台发送请求,不刷新页面,前后端分离

    前端路由即响应页面内容的任务是由前端来做的,根据不同的url更新页面的内容,随着SPA(单页面应用)的普遍使用,前后端开发分离,项目中基本都使用前端路由,通过路由实现页面的变化。例如,通过 vue 开发的SPA中,切换路由,并不刷新页面,而是根据路由在虚拟 DOM 中加载所需要的数据,实现页面内容的改变。

    什么是后端路由?

    特点:向服务器发送请求,会刷新页面,前后端不能分离

    在浏览器的地址栏中切换不同的 url 时,每次都向后台服务器发出请求,服务器根据不同的响应不同的数据,浏览器接收到数据后再进行渲染,所以后端路由会刷新页面,如果网速慢的话,就会看到一个空白页面等待服务端返回数据,后台路由最大的问题就是不能前后端分离。

    三、安装和使用 vue-router

    安装:输入 npm install vue-router

    使用

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    
    • 1
    • 2
    • 3

    脚手架创建

    使用脚手架创建 vue-router 项目是自动生成 router 文件夹,vue-router 包也会自动给导入,步骤如下

    首先在命令行中输入创建 vue 项目的指令

    在这里插入图片描述

    按下回车选择自定义配置
    在这里插入图片描述

    按空格是选中配置,按回车是进入到下一步,我们把 Router 选项选上,然后不断进入下一步即可
    在这里插入图片描述

    最后生成的项目结构如下

    在这里插入图片描述
    比之前的项目多了一个 vue-router 文件夹,下面有个 index.js 文件,里面有对vue-router进行导入

    router-link 与 router-view

    首先我们来看到其生成的 App.vue 界面,发现其有俩个组件 <router-link></router-link><router-view></router-view>

    在这里插入图片描述

    router-link : 为路由入口,用来设置路由跳转,通过 to 属性指定目标地址,默认渲染成带有正确链接的<a>标签

    router-view : 为路由出口,router-view 根据路由显示组件。在路由切换时,切换的是<router-view>挂载的组件,其他内容不会发生改变。

    路由配置

    在router 下 index.js 中,我们来进行路由配置

    在这里插入图片描述

    首先要定义 route, 表示一条路由的实现。它是一个对象,由两个部分组成: path 和 component。path 指路径,component 指的是组件。如:{path:’/home’, component: home}

    这里是两条路由定义组成一个 routes。

    最后创建 router 对路由进行管理,它是由构造函数 new vueRouter() 创建,接受 routes 参数。

    在这里插入图片描述

    const router = new VueRouter({
          routes // routes: routes 的简写
    })
    
    • 1
    • 2
    • 3

    配置完成后,把router 实例注入到 vue 根实例中,就可以使用路由了,这个在main.js 中进行配置
    在这里插入图片描述

    const app = new Vue({
      router
    }).$mount('#app')
    
    • 1
    • 2
    • 3

    四、关于前端路由两种模式

    路由需要实现三个功能:

    • 当浏览器地址变化时,切换页面;
    • 点击浏览器【后退】、【前进】按钮,网页内容跟随变化;
    • 刷新浏览器,网页加载当前路由对应内容;

    在单页面 web 网页中, 单纯的浏览器地址改变, 网页不会重载,如单纯的 hash 网址改变网页不会变化,因此我们的路由主要是通过监听事件,并利用js实现动态改变网页内容,有两种实现方式:

    • hash模式:监听浏览器地址 hash 值变化,执行相应的 js 切换网页;
    • history模式:利用 history API 实现 url 地址改变,网页内容改变;

    它们的区别最明显的就是 hash 会在浏览器地址后面增加#号,而 history 可以自定义地址。

    hash模式

    使用 window.location.hash 属性及窗口的 onhashchange 事件,可以实现监听浏览器地址 hash 值变化,执行相应的 js 切换网页。下面具体介绍几个使用过程中必须理解的要点:

    • hash指的是地址中#号以及后面的字符,也称为散列值。hash也称作锚点,-本身是用来做页面跳转定位的。如http://localhost/index.html#abc,这里的#abc就是hash;

    • 散列值是不会随请求发送到服务器端的,所以改变hash,不会重新加载页面;

    • 监听 window 的 hashchange 事件,当散列值改变时,可以通过location.hash来获取和设置hash值;

    • location.hash值的变化会直接反应到浏览器地址栏;

    history模式

    • window.history 属性指向 History 对象,它表示当前窗口的浏览历史。当发生改变时,只会改变页面的路径,不会刷新页面。
    • History 对象保存了当前窗口访问过的所有页面网址。通过 history.length 可以得出当前窗口一共访问过几个网址。
    • 由于安全原因,浏览器不允许脚本读取这些地址,但是允许在地址之间导航。
    • 浏览器工具栏的【前进】和【后退】按钮,其实就是对 History 对象进行操作。

    五、多级路由

    配置路由规则,使用 children 配置项。

    routes:[
    	{
    		path:'/about',
    		component:About,
    	},
    	{
    		path:'/home',
    		component:Home,
    		children:[ //通过children配置子级路由
    			{
    				path:'news', //此处一定不要写:/news
    				component:News
    			},
    			{
    				path:'message',//此处一定不要写:/message
    				component:Message
    			}
    		]
    	}
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    跳转(要写完整路径):

    <router-link to="/home/news">News</router-link>
    
    • 1

    六、路由传参

    vue 路由传参是指嵌套路由时父路由向子路由传递参数,否则操作无效。传参方式可以划分为 params 传参和 query 传参,params 传参又可以分为 url 中显示参数和不显示参数两种方式。

    1. params 传参

    定义路由:

    在定义path路由路径时定义参数名和格式,如 path:"/one/login/:num" ,router > index.js 文件如下

    注::变量 表示这是一个路径参数

    /* eslint-disable*/
     
    //第一步:引用vue和vue-router ,Vue.use(VueRouter)
    import Vue from 'vue'
    import Router from 'vue-router'
    Vue.use(Router)
     
    //第二步:引用定义好的路由组件
    import ChildOne from '../components/childOne'
    import ChildTwo from '../components/childTwo'
    import Log from '../components/log.vue'
    import Reg from '../components/reg.vue'
     
    //第三步:定义路由(路由对象包含路由名、路径、组件、参数、子路由等),每一个路由映射到一个组件
    //第四步:通过new Router来创建router实例
    
    
    export default new Router({
      mode: 'history',
      routes: [
        {
          path: '/one',
          name: 'ChildOne',
          component: ChildOne,
          children:[
            {
              path:'/one/log/:num',
              component:Log,
            },
            {
              path:'/one/reg/:num',
              component:Reg,
            },
          ],
        },
        {
          path: '/two',
          name: 'ChildTwo',
          component: ChildTwo
        }
      ]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    在父路由组件上使用 router-link 进行路由导航,传参用<router-link to="/one/login/001"> 的形式向子路由组件传递参数。使用 router-view 进行子路由页面内容渲染,父路由组件 childOne.vue 如下:

    <template>
      <div style="border:1px solid red;color:red;">
        <p>这是父路由childOne对应的组件页面</p>
        <p>下面可以点击显示嵌套的子路由 </p>
        <router-link to="/one/log/123">显示登录页面</router-link>
        <router-link to="/one/reg/002">显示注册页面</router-link>
        <router-view></router-view>
      </div>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    子路由通过 this.$route.params.num 的形式来获取父路由向子路由传递过来的参数,子路由组件 login.vue 如下:

    <template>
      <div style="border:1px solid orange;color:orange;">
        <p>登录界面:这是另一个嵌套路由的内容</p>
        <h3>{{this.$route.params.num}}</h3>
      </div>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.$router为 VueRouter 实例,想要导航到不同URL,则使用$router.push方法

    2.$route为当前 router 跳转对象,里面可以获取name、path、query、params等

    当然,我们也可以不用显式参数进行传参。

    定义路由时添加name属性给映射的路径取一个别名,router>index.js文件修改router 如下:

    export default new Router({
      mode: 'history',
      routes: [
        {
          path: '/one',
          name: 'ChildOne',
          component: ChildOne,
          children:[
            {
              path:'/one/log/',
              name:'Log',
              component:Log,
            },
            {
              path:'/one/reg/',
              name:'Reg',
              component:Reg,
            },
          ],
        },
        {
          path: '/two',
          name: 'ChildTwo',
          component: ChildTwo
        }
      ]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在父路由组件上使用 router-link 进行路由导航,使用 <router-link :to="{name:'home',params:{id:001}}> 形式传递参数。注意 ': to= ' 前面的冒号,childOne.vue组件修改如下:

    <template>
      <div style="border:1px solid red;color:red;">
        <p>这是父路由childOne对应的组件页面</p>
        <p>下面可以点击显示嵌套的子路由 </p>
        <router-link :to="{name:'Log',params:{num:666}}">显示登录页面</router-link>
        <router-link :to="{name:'Reg',params:{num:888}}">显示注册页面</router-link>
        <router-view></router-view>
      </div>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    子路由组件页面获取父路由传参方式不变,reg.vue 文件如下:

    <template>
      <div style="border:1px solid orange;color:orange;">
        <p>登录界面:这是另一个嵌套路由的内容</p>
        <h3>子路由获取的参数:{{this.$route.params.num}}</h3>
      </div>
    </template>
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意:上述这种利用 params 不显示 url 传参的方式会导致在刷新页面的时候,传递的值会丢失。

    2. query 传参

    定义路由 router>index.js文件如下:
    export default new Router({
      mode: 'history',
      routes: [
        {
          path: '/one',
          name: 'ChildOne',
          component: ChildOne,
          children:[
            {
              path:'/one/log/',
              component:Log,
            },
            {
              path:'/one/reg/',
              component:Reg,
            },
          ],
        },
        {
          path: '/two',
          name: 'ChildTwo',
          component: ChildTwo
        }
      ]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    修改路由导航 <router-link :to="{path:'/one/log',query:{num:123}}"> ,childOne.vue 文件。

    修改如下:

    <template>
      <div style="border:1px solid red;color:red;">
        <p>这是父路由childOne对应的组件页面</p>
        <p>下面可以点击显示嵌套的子路由 </p>
        <router-link :to="{path:'/one/log',query:{num:123}}">显示登录页面</router-link>
        <router-link :to="{path:'/one/reg',query:{num:999}}">显示注册页面</router-link>
        <router-view></router-view>
      </div>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    子路由组件通过 this.$route.query.num 来显示传递过来的参数,reg.vue 文件如下:

    <template>
      <div style="border:1px solid purple;color:purple;">
        <p>注册界面:这是二级路由页面</p>
        <h3>{{this.$route.query.num}}</h3>
      </div>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    七、编程式路由导航

    脱离<router-link>标签,实现跳转,让路由跳转更加灵活

    //push模式,不破坏历史记录
    this.$router.push({
        name:'detail',
        params:{
            id:m.id,
            title:m.title
        }
    })
    //replace模式,覆盖历史记录
    this.$router.replace({
        name:'detail',
        params:{
            id:m.id,
            title:m.title
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    路由实现前进后退

    //后退
    this.$router.back()
    //前进
    this.$router.forward()
    //正数前进,负数后退
    this.$router.go()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    八、路由守卫

    1. 什么是路由守卫

    路由守卫就是路由跳转过程中的一些钩子函数 ,在路由跳转的时候,做一些判断或其它的操作。 类似于组件生命周期钩子函数 。

    2. 分类

    1.全局路由守卫

    beforeEach(to, from, next) 全局前置守卫,路由跳转前触发
    beforeResolve(to, from, next) 全局解析守卫 在所有组件内守卫和异步路由组件被解析之后触发
    afterEach(to, from) 全局后置守卫,路由跳转完成后触发
    
    • 1
    • 2
    • 3

    2.路由独享守卫

    beforeEnter(to,from,next) 路由对象单个路由配置 ,单个路由进入前触发
    
    • 1

    3.组件路由守卫

    beforeRouteEnter(to,from,next) 在组件生命周期beforeCreate阶段触发
    beforeRouteUpdadte(to,from,next) 当前路由改变时触发
    beforeRouteLeave(to,from,next) 导航离开该组件的对应路由时触发
    
    • 1
    • 2
    • 3

    4.参数

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

    from: 即将要离开的路由对象

    next(Function):是否可以进入某个具体路由,或者是某个具体路由的路径

    3. 详解

    1.路由前置守卫 beforeEach(to, from, next)

    const router = new VueRouter({ ... })
    
    router.beforeEach((to, from, next) => {
      // ...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在路由跳转前触发,在实际项目中应用最多,主要是登陆验证和跳转权限判断

    2.全局解析守卫 beforeResolve(to, from, next)

    router.beforeResolve((to, from, next) => {
      // ...
    })
    
    • 1
    • 2
    • 3

    类似于路由前置守卫 beforeEach(to, from, next),也是路由跳转前触发,但它是同时在所有组件内守卫和异步路由组件被解析之后触发的

    调用时机:在 beforeEach(to, from, next)和组件内beforeRouteEnter(to, from, next)之后,afterEach(to, from)之前调用

    3.全局后置守卫 afterEach(to, from, next)

    router.afterEach((to, from) => {
      // ...
    })
    
    • 1
    • 2
    • 3

    于路由前置守卫 beforeEach(to, from, next) 相对,路由跳转后触发,但它是同时在所有组件内守卫和异步路由组件被解析之后触发的

    调用时机:在 beforeEach(to, from, next) 和组件内 beforeResolve (to, from, next)之后, beforeRouteEnter(to, from) 之前调用

    4.路由独享守卫 beforeEnter(to, from, next)

    const router = new VueRouter({
      routes: [
        {
          path: '/foo',
          component: Foo,
          beforeEnter: (to, from, next) => {
            // ...
          }
        }
      ]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    于路由前置守卫 beforeEach(to, from, next)相同,但在beforeEach(to, from, next)后触发

    5.组件路由守卫 beforeRouteEnter(to, from, next)

    const Foo = {
      template: `...`,
      beforeRouteEnter(to, from, next) {
        // 不能获取组件实例 
        // 因为当守卫执行前,组件实例还没被创建
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    因为该守卫在组件创建之前阶段触发,那个时候组件还没有创建成功,所以这个守卫内不能使用 this 获取组件实例。

    调用时机:在全局守卫beforeEach(to, from, next)和独享守卫beforeEnter(to, from, next)之后,全局beforeResolve(to, from, next)和全局afterEach(to, from)之前调用

    6.组件路由守卫 beforeRouteUpdate(to, from, next)

    beforeRouteUpdate(to, from, next) {
      // 在当前路由改变,但是该组件被复用时调用
      // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
      // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
      // 可以访问组件实例 
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    调用时机:在当前路由复用时

    7.组件路由守卫 beforeRouteLeave(to, from, next)

    beforeRouteLeave(to, from, next) {
      // 导航离开该组件的对应路由时调用
      // 可以访问组件实例
    }
    
    • 1
    • 2
    • 3
    • 4

    通常用来禁止用户在还未保存修改前突然离开

    调用时机:导航离开该组件的对应路由时调用

    4.完整的导航解析流程

    1.触发进入其它路由
    2.调用要离开路由的组件守卫beforeRouteLeave
    3.调用全局的前置守卫beforeEach
    4.在重用的组件里调用 beforeRouteUpdate
    5.在路由配置里的单条路由调用 beforeEnter
    6.解析异步路由组件
    7.在将要进入的路由组件中调用beforeRouteEnter
    8.调用全局的解析守卫beforeResolve
    9.导航被确认
    10.调用全局的后置钩子afterEach
    11.触发 DOM 更新mounted
    12.执行beforeRouteEnter守卫中传给 next的回调函数

  • 相关阅读:
    三季度都快过完了,看看项目经理年初立的flag怎样了?
    电脑数据删除了还能恢复吗?为你推荐三种超实用的电脑数据恢复方法
    VS2019 第一个驱动程序
    实现二叉树先序,中序和后序遍历
    QT_字符串相关操作_QString
    Linux学习笔记(2)
    贪心算法(算法竞赛、蓝桥杯)--线段覆盖
    全国青少年软件编程等级考试标准(正式级)
    如何搭建神经网络模型,多层全连接神经网络
    Java之Spring MVC中表单标签的简介说明
  • 原文地址:https://blog.csdn.net/m0_51111980/article/details/125583800