Vue – vue-router 路由概念与原理

简介

SPA 指的是一个web 网站只有唯一的一个HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。 此时,不同组件之间的切换需要通过前端路由来实现。

在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成!

前端路由,指的是Hash 地址与组件之间的对应关系!

路由的跳转页面会产生锚点,url 后方会包含 #foo 的锚点,这个 #foo 就是location.hash,所以说前端路由,指的是Hash 地址与组件之间的对应关系!

location.hash = #foo

 

路由的工作方式

①  用户点击了页面上的路由链接

②  导致了 URL 地址栏中的Hash 值发生了变化

③  前端路由监听了到Hash 地址的变化

④  前端路由把当前 Hash 地址对应的组件渲染都浏览器中

 

简易实现前端路由

 

本段指简单利用路由方式,但不使用 vue-route 插件的情况下手动实现类路由的功能,真实项目不会使用这种方法。

使用 component 组件

在 template 中建立通用组件元素

    <div class="box">
      <a href="#/home">主页</a>
      <a href="#/left">Left页</a>
      <a href="#/right">Right页</a>
    </div>

    // 使用 component 组件来自由切换组件
    <component :is="comName"></component>

 

导入预设组件

把需要切换的组件都预先导入进来

import Left from "@/components/Left";
import Right from "@/components/Right";

export default {
  name: 'App',
  data() {
    return {
      comName: '#'
    }
  },
  components: {
    Left,
    Right
  }
}

 

在 created 生命周期函数中设定

在页面创建的时候,加入 onhashchange 监听,当 hash 值发生改变的时候,会被执行。

 

created() {
  window.onhashchange = ()=>{
    switch (location.hash){
      case "#/home":
        this.comName = "#"
            break;
      case "#/left":
        this.comName = "Left"
            break;
      case "#/right":
        this.comName = "Right"
            break;
    }
  }
}

 

这样,当页面点击对应的链接时,就会被监听到 hash 改动,从而改变 comName 的值,从而改变组件的切换。

 

vue-route 路由安装

vue-router 是 vue.js 官方给出的路由解决方案。它只能结合vue 项目进行使用,能够轻松的管理 SPA 项目 中组件的切换。

 

步骤

①  安装 vue-router 包

②  创建路由模块

③  导入并挂载路由模块

④  声明路由链接和占位符

 

安装命令

npm i vue-route@3.5.2 -S

 

创建路由模块

在 src 源代码目录下,新建router/index.js 路由模块,并初始化如下的代码

 

import Vue from 'vue'
import VueRouter from 'vue-route'

// Vue 安装路由插件
Vue.use(VueRouter)

// 建立一个 VueRouter 路由实例
export default new VueRouter()

 

在main.js 文件下导入 router 配置文件使其生效

import router from 'router/index'

把注册 router 插件挂载到当前 Vue 实例上

new Vue({
  router: router 
}).$mount('#app')

 

vue-router 基本用法

VueRouter 实例中创建路由规则,在 router/index.js 中找到 new VueRouter() 实例对象,VueRouter() 接收一个对象属性,其中路由规则属性名为 routes ,它是一个数组,数组成员为对象,每一个对象代表一条路由规则。

 

路由规则

路由规则基本写法:

new VueRouter({
    routes:[
        { path:'/home', component:Home },
        { path:'/left', component:Left },
        { path:'/right', component:Right },
         一个对象表示一条路由规则
    ]
})

需要用到组件来对应 hash 锚点值,因此要在 router/index.js 中需要引入组件文件。

import Home from "@/components/Home";
import Left from "@/components/Left";
import Right from "@/components/Right";

 

template 占位组件

路由提供一个用于占位的组件

<router-view></router-view>

放到需要切换组件的 template 位置中即可。

 

使用 router-link 代替 a 标签

vue-router 提供了用于切换锚点的标签 router-link,代替 a 标签,官方推荐使用 router-link

<a href="#/home">首页</a>

router-link 实际上是经过包装的 a 标签

<router-link to="/home">首页</router-link>

router-link 的 to 属性,和 a 标签的 href 是一样的

 

路由重定向

用户在访问地址 A 的时候,强制用户跳转到地址C ,从而展示特定的组件页面。 通过路由规则的redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向。

使用 redirect 来实现路由重定向

routes:[
    // 路由重定向
    { path:'/', redirect:'/home' },
    // 路由规则
    { path:'/home', component:Home },
    { path:'/left', component:Left },
    { path:'/right', component:Right },
]

 

嵌套子级路由

路由可以让父组件方便地切换不同的子组件,但是子组件也有需要自由切换孙组件,这时的路由需要实现嵌套路由。

点击父级路由链接显示模板内容

点击父级路由链接显示模板内容

模板内容中又有子级路由链接 点击子级路由链接显示子级模板内容

 

 

 

 

 

 

 

 

 

通过 children 属性声明子路由规则

{
    path:'/left', component:Left,
    
    // Left 组件下的子路由规则
    children:[
        { path:'tab1', component:Tab1 },
        { path:'tab2', component:Tab2 }
    ]
},

注意:父组件的 path 可以增加 '/' ,但是 children 子组件中的路由中的 path 不可增加 '/'.

 

默认孙组件重定向

当父组件点击子组件路由时,第一次切换时没有默认孙组件路由,因此我们要在子组件中设置默认的子路由。在子组件路由规则中加入重定向属性即可。

{
    path:'/left', component:Left,
    
    // 设定子路由载入后默认显示 tab1 孙组件
    redirect: '/left/tab1',

    // Left 组件下的子路由规则
    children:[
        { path:'tab1', component:Tab1 },
        { path:'tab2', component:Tab2 }
    ]
},

 

默认子路由

除了上面使用 redirect 属性设定重定向孙组件的方法外,VueRouter 还规定了,如果子路由中某一条规则 path 为空时,则认为它就是默认子路由组件。

{
    path:'/left', component:Left,

    // Left 组件打开后,Tab1 为默认显示组件
    children:[
        { path:'', component:Tab1 },
        { path:'tab2', component:Tab2 }
    ]
},

 

router-link 中的 to 属性可以不写子组件的位置

<router-link to="/left"></router-link>

 

设置路由高亮[3.0]

在路由链接点击时,Vue-router 提供让由激活的路由链接设置高亮样式,也支持自定义样式名。

使用默认的高亮 class 类

被激活的路由链接,默认会应用一个叫做 router-link-active 的类名。开发者可以使用此类名选择器,为激活的路由链接设置高亮的样式

.router-link-active {
    background-color: red;
    color: white;
    font-weight: bold;
}

 

自定义路由高亮的 class 类

在创建路由实例对象时,开发者可以基于 linkActiveClass 属性,自定义路由链接被激活时用应用的类名。

const router = createRouter({
  history: createWebHashHistory(),

   // 指定被激活的路由链接,会应用 router-active 这个类名
   // 默认的 router-link-active 类名会被覆盖掉
   linkActiveClass: 'router-active',
   
  
})

 

css 样式

.router-active {
    background-color: red;
    color: white;
    font-weight: bold;
}

 

动态路由

动态路由指的是:把Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。 在 vue-router 中使用英文的冒号(:)来定义路由的参数项。

通过 动态参数 来代替多个变化的路由,使用 : 号加代替传过来的参数,如 :id。

该 :id 的值将保存在 this.$route.param  对象中,在实例中可以通过this.$route.param.id

获得该id值。

在 template 中,默认帮你自动填充 this 对象,可以直接$route.param.id 获得值。

 

使用 props 接收动态参数值

在 template 中使用 $route.param.id 来获得值非常麻烦,但可以通过组件接收 props 来实现直接接收参数。

 

只需在路由规则中加入属性 { props: true } 则可在相关组件中的 props 中接收到该占位符的数值。

{ path: 'movie/:foo', component: Movie, props: true }

props:[ 'foo' ]
foo 将接收 :foo 的值

 

使用对象型,定义传参,可在路由页面中使用props接收

props: {
    keyword: this.$route.params.keywork,
    queryKey: this.$route.query.k
}

 

使用函数方法,函数中返回一个对象,可在路由页面中使用props接收

props: ($route) => {
     return {
         keyword: $route.params.keyword,
     }
}

 

动态路由中的可选传参

当动态路由中的参数有可能不被传递时,会产生路由警告,可在动态参数后增加【?】定义参数可选传递。

{ path: 'movie/:foo?', component: Movie, props: true }

注意:可选传参中的【?】只支持当【:foo】为 undefined 时才能生效,但一般如果【:foo】参数是由 v-model 双向绑定时,当【:foo】不传值时,【:foo】为 "" 而不是 undefined .这使得【?】可选传参功能失效。

因此,在传参时,为了避免传到空值,应当先判断【:foo】是否为 "" ,如果为 "" ,应当设置为 undefined

this.$router.push({
    name: "search",
    params: {
      keyword: this.keyword || undefined
    },
});

 

 

查询参数

传入参数值有两种类型,一种是 /foo 的参数值,如上一节所写的,使用 this.$route.param  来获得。

还有另一种传参数值的类型,则是平常我们常见的 /foo?name=zx&age=18 这样的参数值,其中的 name=zx&age=18 则是查询参数值,如何获得这样的参数值呢?

我们可以通过使用 this.$route.query  来获取,query 是一个对象,其包含所有查询参数值

this.$route.query ==>

{ name:'zx', age: '18' }

 

声明式导航 与 编程式导航

 

声明式导航

在浏览器中,点击链接实现导航的方式,叫做声明式导航。例如:

普通网页中点击<a> 链接、vue 项目中点击 <router-link> 都属于声明式导航。

 

 

编程式导航

在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。例如:

普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航。

 

常见编程式导航 API

this.$router.push('hash 地址')

跳转到指定hash 地址,并增加一条历史记录

methods:{
    push(){
        this.$router.push('foo/1')
    }
}
this.$router.replace('hash 地址')

跳转到指定的hash 地址,并替换掉当前的历史记录

methods:{
    replace(){
        this.$router.replace('foo/1')
    }
}

注意:replace前进后,会清空或覆盖后面的历史浏览记录。

 

this.$router.go(数值 n)

 

实现导航历史前进、后退

methods:{
    go(){
        this.$router.go(-1)
    }
}

注意:go(-1) 代表后退一个页面,go(1) 代表前进一个页面,go(-2) 代表后退两个页面,如此类推。

 

go() 的简写方法

通常我们比较少使用 go( -2 ) 这样跳过两层页面的操作,更多的是后退一页或前进一页,因此 vue-router 提供了两个直接的方法来实现后退一页或前进一页的操作。

this.$router.back()  可以实现后退一页,用在 @click 中不可以加入 this,在方法中需要加入 this

<button @click="$router.back()"></button>

 

this.$router.forward()  可以实现前进一页,用在 @click 中不可以加入 this,在方法中需要加入 this

<button @click="$router.forward()"></button>

 

导航守卫

导航守卫可以控制路由的访问权限。

 

全局前置守卫

每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行 访问权限的控制。

 

// 创建路由实例对象
const router = new VueRouter({ ... })

// 为 router 实例对象,声明全局前置导航守卫
// 只要发生了路由的跳转,必然会触发 beforeEach 
router.beforeEach( function( to, from, next){} )

 

假如页面A要跳到页面C,则

to: 指的是 页面C.就是将要访问的页面。to 为一个 $route 对象

from: 指的是 页面A.就是当前页面。from 为一个 $route 对象

next: 它是 vue-router 提供的一个函数,当调用 next() 时,则表示允许跳转到 to 所在的页面。在这里不调用 next() 所有路由跳转将失效。

 

next() 三种调用方式

 

当前用户拥有后台主页的访问权限,直接放行:next()

当前用户没有后台主页的访问权限,强制其跳转到登录页面:next('/login')

当前用户没有后台主页的访问权限,不允许跳转到后台主页:next(false)

 

控制访问权限

 

 

全局解析守卫

全局解析守卫是Vue-Router 2.5 之后推出的功能,功能类似于【全局前置守卫】。

声明全局解析守卫如下:

/**
 * 路由全局解析守卫
 */
router.beforeResolve((to, from, next) => {
    next();
});

全局解析守卫将在全局前置守卫执行之后被调用。

 

 

全局后置钩子

全局后置钩子,指的是当全局前置守卫被放行之后被调用,页面已经实行跳转操作,此时的页面已经被允许跳转,所以全局后置钩子没有 next() 放行方法,作用是在跳转之后,对页面路由作最后的加工处理。

let router = new 

/**
 * 全局后置钩子
 */
router.afterEach((to, from) => {
})

 

 

路由独享的守卫

路由独享的守卫是配置在单独的页面路由上的,这样的守子只会在访问到该路由页面时,才会被触发路由守卫。定义如下

export default {
    path: "/login",
    name: "login",
    component: Login,
    meta: {
        headerShow: true,
        footerShow: true
    },
    // 路由独享守卫,定义在页面路由信息上,只会在访问这个路由时,才会被触发。
    // 本例中的 login 路由,定义了路由独享守卫,则在访问 /login 时才会触发守卫判断 
    beforeEnter(to, from, next) {
        // 判断是否已登陆,如果登陆了,就跳到Home首页
    }
}

 

 

组件内的守卫

组件内的守卫 不定义在 路由规则上,而是作为一个【VueComponent 生命周期】的形式进行触发调用,方法定义在组件中:

<script>
export default {
  name: 'Login',
  data() {
    return {
      username: "",
      password: ""
    }
  },
  /**
   * beforeRouteEnter 准备跳转到这个路由页面时调
   * 因为 beforeRouteEnter 调用时,该页面还没有被确认并创建完成,因此无法获取 "this"
   * @param to
   * @param from
   * @param next
   */
  beforeRouteEnter(to,from,next){},

  /**
   * beforeRouteLeave 当路由页面即将被跳转出去之前时调用
   * beforeRouteLeave 通常用于判断用户是否在未保存数据时关闭页面,作一个拦截操作
   * 可以获取 "this"
   * @param to
   * @param from
   * @param next
   */
  beforeRouteLeave(to,from,next){},

  /**
   * beforeRouteUpdate 路由页面参数被改变时,页面重新刷新时被调用
   * 如:/foo/:id 的路由,当 /foo/1 => /foo/2 等参数发生改变,路由被跳转但只路转给本路时调用。
   * 可以获取 "this"
   * @param to
   * @param from
   * @param next
   */
  beforeRouteUpdate(to,from,next){},
}
</script>

 

 

关于 beforeRouteEnter 阶段获取 this 的问题

我们知道 beforeRouteEnter 阶段是在路由准备跳转时的阶段,这时vc对象还没被创建,无法获取 this 对象。

但如果我们需要在 beforeRouteEnter 阶段获取 this 该怎么做?

VueRouter 在 beforeRouteEnter 中的 next() 提供了获取 vm 的方法。

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

注意:只有 beforeRouteEnter 的 next() 才有 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 的回调函数,创建好的组件实例会作为回调函数的参数传入。

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Vue – vue-router 路由概念与原理
SPA 指的是一个web 网站只有唯一的一个HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。 此时,不同组件之间的切换需要通过前端路由来实现。
<<上一篇
下一篇>>