vue-route详细介绍 (下)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: vue-route详细介绍 (下)

二. vue-router的嵌套



嵌套路由是一个很常见的功能, 比如主业引入了组件Home, 我们在Home里面引入了banner图组件. 这样就是组件的嵌套.


我们的路由映射规则是: 一个路径映射一个组件. 访问两个路径, 会分别渲染两个组件.

下面来实现嵌套路由,


第一步: 创建组件,创建两个组件


HomeBanner.vue组件


<template>
    <div>
      <h2>首页banner图</h2>
      <ul>
        <li>banner图1</li>
        <li>banner图2</li>
        <li>banner图3</li>
        <li>banner图4</li>
      </ul>
    </div>
</template>
<script>
    export default {
        name: "HomeBanner"
    }
</script>
<style scoped>
</style>

HomeNews.vue组件


<template>
    <div>
      <h2>首页新闻</h2>
      <ul>
        <li>第一条新闻</li>
        <li>第二条新闻</li>
        <li>第三条新闻</li>
        <li>第四条新闻</li>
      </ul>
    </div>
</template>
<script>
    export default {
        name: "HomeNews"
    }
</script>
<style scoped>
</style>

第二步: 创建组件路由


我们要在Home也添加子路由, 需要在路由里面增加一个children属性配置.


//引入子路由-使用懒加载的方式进行加载
const HomeBanner = ()=>import('../components/HomeBanner');
const HomeNews = () => import('../components/HomeNews');
{
    path: "/home",
    component: Home,
    children: [{
      path:'',
      redirect: 'HomeNew'
    },{
      path: 'HomeNew', //注意: 这里面没有/
      component: HomeNews
    },{
      path: 'HomeBanner',
      component: HomeBanner
    }]
  }

里面的路径依然是有两个部分:


  • 一个是path路由路径: 这里需要注意的是,不需要在路径名的前面加/
  • 另一个是component: 指定组件名称


第三步: 增加<router-link> 和 <router-view/>


我们要在Home页面展示子组件, 因此需要将子组件的展示放在页面上


<template>
  <div>
    <h2>这是Home主页</h2>
    <div>欢迎来到home主页</div>
    <router-link to="/home/homeNew">新闻</router-link>
    <router-link to="/home/homeBanner">Banner图</router-link>
    <router-view></router-view>
  </div>
</template>


完成以后,效果如下:

image.png

三. vue-router参数传递



vue-router参数传递有两种方式: 第一种是: param的方式. 第二种是: query的方式


1. param方式


这种方式在动态路由的时候有提到过. 下面来回顾一下:


第一步: 创建一个User.vue组件

<template>
    <div>
      <h2>用户界面</h2>
    </div>
</template>
<script>
    export default {
        name: "User",
    }
</script>
<style scoped>
</style>

第二步:创建动态路由的时候, 定义变量


const routes = [
  {
    path: "/user/:userId",
    component: User
  }
]

我们定义了一个user/:userId, 这样的动态路由. 路由可以是/user/zhangsan, 或者/user/lisi


第三步: 在App.vue中定义动态路由跳转的组件


<template>
  <div id="app">
    <!-- 定义两个路由链接 -->
    <router-link to="/home" tag="button" replace active-class="active">首页</router-link>
    <router-link to="/about" tag="button" replace active-class="active">关于</router-link>
    <!-- 定义动态路由 -->
    <router-link v-bind:to="'/user/' + userId" tag="button" replace active-class="active">用户</router-link>
    <!-- 展示组件内容 -->
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: 'App',
  data(){
    return {
      userId:"zhangsan"
    }
  }
}
</script>
<style>
    .active {
      color:red;
    }
</style>

这里面userId是变化的部分. 通过bind事件动态将userId绑定到path路径中


<router-link v-bind:to="'/user/' + userId" tag="button" replace active-class="active">用户</router-link>

第四步: 修改User.vue组件


首先, 在User.vue脚本中获取传递过来的路由参数. 我们使用[this.$route.params.变量名]的方式获取路径参数


<script>
    export default {
        name: "User",
        data() {
          return {
            userId: this.$route.params.userId
          }
        }
    }
</script>

然后使用语法糖, 将变量显示出来


<template>
    <div>
      <h2>用户界面</h2>
      <div>欢迎{{userId}},来到用户界面</div>
      <div>{{$route.params.userId}}</div>
    </div>
</template>

也可以直接使用使用{{$route.params.userId}}的写法


以上是使用参数的方式传递变量.


2. query方式


第一步: 配置路由的时候是普通配置: /user

第二步:传递的方式使用query的方式, query是一个对象,

第三步:传递后形成的路径是:/user?useId=2, /user?userId=9

下面来举个案例:


第一步: 创建一个组件Profile.vue


<template>
    <div>
      <h2>这是一个Profile组件</h2>
    </div>
</template>
<script>
    export default {
        name: "Profile"
    }
</script>
<style scoped>
</style>

第二步: 配置路由


// 配置路由相关的信息
// 导入vue和vue-router
import Vue from 'vue'
import Router from "vue-router";
// 这里会引入你要导入的组件, 然后通过路由配置组件内容
// 懒加载
const Profile = () => import('../components/Profile')
/*
 * 第一步: 安装插件
 * vue-router是一个插件, 所以, 我们需要使用vue.use(插件名) 来安装插件
 */
Vue.use(Router)
/*
 * 第二步: 构建VueRouter对象
 * 在VueRouter中主要是配置路由和组件之间的映射关系的.
 */
const routes = [
  {
    path: "/profile",
    component: Profile
  }
]
const router = new Router({
  // 这里配置的是路由和组件的映射关系, 是一个数组.
  routes,
  mode: "history",
  linkActiveClass: "active"
})
/*
 * 第三步: 将VueRouter对象导出
 *
 */
export default router

第三步: 渲染组件


<!-- 配置动态路由 -->
    <router-link v-bind:to="{path:'/profile', query:{name:'lily',sex:'男',age:12}}" tag="button" replace active-class="active">档案</router-link>

通常我们定义路由是这样定义的


<!-- 配置动态路由 -->
    <router-link to="/profile" tag="button" replace active-class="active">档案</router-link>

但是, 这样路由就固定写死了, 所以肯定不行, 我们需要给他一个变量, 使用v-bind:to

然后就有了下面这个写法


v-bind:to="{path:'/profile', query:{name:'lily',sex:'男',age:12}}"

第四步: 来看一下效果


image.png


我们看到路径上带了?参数.


http://localhost:8080/profile?name=lily&sex=%E7%94%B7&age=12

第五步: 在Profile组件中获取参数


获取参数有两种方式


  1. 直接语法糖方式获取{{$route.query.***}}
  2. 在脚本中获取:this.$route.query.***

如下采用方式一获取参数:


<template>
    <div>
      <h2>这是一个Profile组件</h2>
      <h4>{{$route.query}}</h4>
      <h4>{{$route.query.name}}</h4>
      <h4>{{$route.query.age}}</h4>
      <h4>{{$route.query.sex}}</h4>
    </div>
</template>

第六步: 查看效果

image.png

四. vue-router导航守卫



导航守卫, 听者特别的高大上. 到底是什么意思呢?

在之前的html页面, 如果我们想要实现title跟着页面变, 那么只需要设置html页面的title属性就可以了.

在vue中, 只有一个index.html页面,如何实现title的改变呢?

有两种方法:

  • 第一种, 使用生命周期函数created().
  • 第二种: 使用全局导航守卫


1. 使用生命周期实现页面更新title属性


组件的生命周期, 这在之前有提到过. 子组件的生命周期中有很多函数, 比如created(), mouted(), updated(), 我们可以在他的生命周期增加处理逻辑.


,我们可以在created()方法中增加处理逻辑.

<template>
  <div>
    <h2>这是Home主页</h2>
    <div>欢迎来到home主页</div>
    <router-link to="/home/homeNew">新闻</router-link>
    <router-link to="/home/homeBanner">Banner图</router-link>
    <router-view></router-view>
  </div>
</template>
<script>
    export default {
        name: "Home",
      created() {
          document.title="首页"
      }
    }
</script>
<style scoped>
</style>

我们看到在script中增加一个created方法. 并指定了title名称.


<script>
    export default {
        name: "Home",
      created() {
          document.title="首页"
      }
    }
</script>

来看一下效果


image.png


这样有什么问题? 假如现在有100个页面, 我们需要在100个页面中都增加created()函数. 虽然可以实现功能,但似乎有些麻烦, 有没有可以统一修改的办法呢?我们可以使用全局导航守卫实现


3.2. 使用全局导航守卫的方式更新title属性


什么是导航守卫?


来看看官网的解释: https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。


  • 这里的导航,指的就是路由的跳转或者取消跳转
  • 守卫指的是: 有很多钩子方法, 允许我们在某一个过程中植入代码逻辑.
  • 常见的导航守卫有: 全局导航守卫(包括三个: 全局前置守卫, 全局解析守卫, 全局后置守卫), 路由独享导航守卫, 组件内的守卫.

下面老看看[全局前置守卫]


1. 使用 router.beforeEach 注册一个全局前置守卫


const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
  // ...
})

当一个导航触发时,全局前置守卫按照创建顺序调用。


守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。

我们来看看router.beforeEach的实现.

7ffdaf9e6f5b4c8799d81770e135460b_tplv-k3u1fbpfcp-watermark.png


点击beforeEach进入到router.ts文件, 可以看到里面定义了router.beforeEach()方法的定义


beforeEach(guard: NavigationGuard): Function

这是一个函数, 方法有一个入参, 参数是一个方法, 参数名是guard. 方法体是NavigationGuard. 我们来看看NavigationGuard的实现逻辑


export type NavigationGuard<V extends Vue = Vue> = (
  to: Route,
  from: Route,
  next: NavigationGuardNext<V>
) => any

NavigationGuard也是一个函数, 并且这个函数继承自Vue. 函数有三个入参: to, from, next. 方法实现是any.

所以,我们要想重写beforeEach()方法. 那么参数就要定一个方法, 并且方法里有三个入参. 方法如下:


router.beforeEach(function(to, from, next) {
})

除了这种方法, 我们还可以使用箭头函数


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

两种都是可以的.


接下来, 看看函数中三个参数都是什么含义呢?


  • to: 是一个router对象, 含义是导航到的目标路径.
  • from: 是一个router对象, 含义是当前导航正要离开的路由.
  • next: 是一个函数, 这是一个钩子函数. 一定要调用该方法来 resolve 这个钩子

函数实现的部分, 一定要调用next()方法. 表示导航继续向下执行. 如果不调用next(), 那么后面的函数将不会被解析或者执行.


也就是说, 代码这至少是这样的


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

确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。


2. 在路由index.js文件中增加元数据属性, 并设置title属性值


const routes = [
  {
    path:"/",
    redirect: "/home",
  }, {
    path: "/home",
    component: Home,
    children: [{
      path:'',
      redirect: 'HomeNew'
    },{
      path: 'homeNew', //注意: 这里面没有/
      component: HomeNews
    },{
      path: 'homeBanner',
      component: HomeBanner
    }],
    meta: {
      title: "首页"
    }
  },{
    path: "/about",
    component: About,
    meta: {
      title: "关于"
    }
  },{
    path: "/user/:userId",
    component: User,
    meta: {
      title: "用户"
    }
  },{
    path: "/profile",
    component: Profile,
    meta: {
      title: "档案"
    }
  }
]

增加的元数据内容如下


meta: {
      title: "档案"
    }

3. 后面在全局导航路由中设置title属性


router.beforeEach((to, from, next) => {
  console.log(to)
  console.log(from)
  document.title = to.matched[0].meta.title
  next()
})

我们通过打印to和from对象, 发现他们确实都是路由对象, 我们可以通过matched中的第0个元素,获取到meta中的内容.

d61cf0c67fc24b3aa86064f8a3a657a4_tplv-k3u1fbpfcp-watermark.png


我们看到其实to下面和matched平级的也有一个meta属性, 但是这个属性在某些情况下值是空的. 所以, 我们还通过matched的第一个元素来获取meta对象


以上就是全局前置导航守卫的用户, 后置导航守卫等其他守卫, 用法与其相似, 可以查看官方文档: https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

前置守卫也好, 后置守卫也好, 都是路由组件的钩子函数, 通过钩子函数, 我们可以在对应的生命周期织入业务逻辑.


理解守卫的含义



全局前置守卫, 全局解析守卫, 全局后置守卫: 定义在路由文件中, 表示对所有路由都有效


路由独享守卫: 可以在路由配置上直接定义 beforeEnter 守卫:


组件内的守卫:你可以在路由组件内直接定义以下路由导航守卫, 有效范围是某个组件.


                      常见的组件内守卫: beforeRouteEnter(进入路由前),beforeRouteUpdate (更新路由前)),beforeRouteLeave(离开路有前)

五. keep-alive


我们有首页, 关于, 用户, 档案. 首页下面有两个按钮[新闻],[消息]

当点击首页的[消息], 然后切换到关于页面, 再回到首页的时候, 我们希望能够继续展示[消息]的内容


默认是不会保留操作的记忆的. 下次回来直接到[首页->新闻], 使用keep-alive就可以有保留记忆的效果了


为什么每次回到首页, 都会展示[首页->新闻]的内容呢?


原因是每次回到首页都会创建一个新的Home组件.

我们来验证每次回到首页都会重新创建一个新的组件. 来看看vue组件的生命周期. 其实Vue对象本身也是一个大组件

87a0bed4773b4c7a89c489f62cecb067_tplv-k3u1fbpfcp-watermark.png


如上图所示:在vue整个生命周期, 他有很多挂载函数, 比如:beforeCreate(创建组件之前), created(创建组件后), beforeMount(挂载组件之前), mounted(挂载组件之后),
beforeUpdate(组件值更新之前),updatd(组件值更新之后),beforeDestroy(组件销毁之前),destroyed(组件销毁之后)


1. 验证, 是否销毁了原来的组件呢?


我们只需要验证created()和destroyed()两个函数是否每次跳走都会被执行一遍.

<script>
    export default {
        name: "Home",
      created() {
          document.title="首页"
        console.log("created home")
      },
      destroyed() {
        console.log("destroyed home")
      }
    }
</script>

如上所示:定义了创建函数和销毁函数, 下面来看看当跳走的时候, 是不是会执行这两个函数.

image.png

如上图所示: 当离开首页,就会执行destroyed函数, 当进入首页, 就会执行created函数. 说明每次确实都在创建新的组件


2. 如何才能让组件有记忆,而不是每次都重新创建呢?


如果想要实现路由跳转走以后, 返回来不需要重新创建组件, 我们可以使用keep-alive, 他的用法很简单


在组建展示的位置增加标签


<keep-alive>
        <router-view></router-view>
    </keep-alive>

这样调走再跳回来就不会重新创建组建了, 来看看效果

image.png

我们看到只有第一次创建了home组件, 后来路由调走, 组件并没有被销毁.

3.案例: 实现从[首页->banner图]跳走后, 在跳回来, 依然定位在[首页->banner图]的位置


跳走, 在跳回来, 这实际上是在控制路由.我们可以让路由调走之前记住当前组件的路由. 要想实现这个功能,需要了解一下几个钩子函数:


  1. activated: 路由激活时触发
  2. deactivated: 路由取消激活时触发


先来看这两个: 这两个函数生效的条件是 : 设置了<keep-alive>组件才有效. 也就是说, 组件离开时不销毁.


我们在home组件中增加两个方法


activated() {
      // 路由激活
      console.log("activated home")
    },
    deactivated() {
      // 路由取消激活
      console.log("deactivated home")
    },

然后来看看效果: 我们发现跳到home路由, 触发activated方法, 离开home路由, 触发deactivated方法.


如何记住路由呢? 我们可以定义一个变量来记住调走前的路由.


this.$route.path : 他是获取当前激活的路由


要想实现这个功能, 还需要使用到路由守卫.

路由守卫有三种类型

const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

我们选择使用最后一个beforeRouteLeave, 在离开当前路由的时候, 记录下离开前的路由.

代码实现如下:


1. 在home组件增加两个方法, 一个是activated组件激活的时候重定向路由, 另一个是beforeRouteLeave组件离开前记录离开前的路由


activated() {
        // 路由激活, 路由到path路径
        this.$router.push(this.path)
      },
      beforeRouteLeave(to, from, next) {
        console.log("离开home前的路径 "+this.path)
        this.path = this.$route.path;
        next()
      }

2. 看一下运行的效果:

image.png


以上就是keep-alive的内容, 这里重要要有一个概念, 组件调走以后是会销毁原来的组件的.如果我们不想它被销毁, 那么可以使用<keep-alive>组件实现.


4. keep-alive 的属性


keep-alive有两个属性: 一个是include, 另一个是exclude


  • include: 字符串或者正则表达式, 只有匹配的组件才会被缓存
  • exclude: 字符串或者正则表达式, 匹配到的组件不会被缓存.

比如,我们整个组件都希望每次加载会缓存, 但是档案组件特殊, 希望他每次都被销毁, 重新创建.


第一步: 在Profile组件中增加created和destroy事件


created() {
          console.log("Profile created")
        },
      destroyed() {
        console.log("Profile destroyed")
      }

第二步: 在<keep-alive>中配置排除Profile组件


<keep-alive exclude="Profile">
      <router-view></router-view>
    </keep-alive>

这样, Profile组件就不会被缓存了


第三步: 演示效果


image.png

相关文章
|
8月前
|
JavaScript 前端开发
Vue3--Vue Router详解--学习笔记
Vue3--Vue Router详解--学习笔记
110 3
|
JavaScript CDN
【VUE】vue.js中的路由router
【VUE】vue.js中的路由router
|
8月前
|
JavaScript
vue.router和vue.route
vue.router和vue.route
|
5月前
|
资源调度 JavaScript 前端开发
Vue Router 的使用方式是什么
【8月更文挑战第30天】Vue Router 的使用方式是什么
29 2
|
6月前
|
JavaScript
vue $router与$route的区别详解
vue $router与$route的区别详解
38 0
|
8月前
|
JavaScript
|
缓存 JavaScript
Vue Router 学习 new Router
Vue Router 学习 new Router
151 0
|
8月前
|
缓存 移动开发 JavaScript
【学习笔记】Vue Router
【学习笔记】Vue Router
67 0
|
8月前
|
JavaScript 前端开发 网络架构
Vue Router:让你的应用路由起来!
Vue Router:让你的应用路由起来!
|
JavaScript
Vue Router 学习 router-view基础 以及 routes配置
Vue Router 学习 router-view基础 以及 routes配置
151 0