vue-router详解 - 从使用到扩展

1. 认识路由

1.1 后端路由
  • 早期的网站开发整个HTML页面是由服务器来渲染的.
    • 服务器直接生产渲染好对应的HTML页面,返回给客户端进行展示
  • 但是,一个网站,这么多页面服务器如何处理呢?
    • 一个页面有自己对应的网址,也就是URL.
    • URL会发送到服务器,服务器会通过正则对该URL进行匹配,并且最后交给一个Controller进行处理.
    • Controller进行各种处理,最终生成HTML或者数据,返回给前端.
    • 这就完成了一个IO操作.
  • 上面的这种操作,就是后端路由.
    • 当我们页面中需要请求不同的路径内容时,交给服务器来进行处理,服务器渲染好整个页面,并且将页面返回给客户顿
    • 这种情况下渲染好的页面,不需要单独加载任何的js和css,可以直接交给浏览器展示,这样也有利于SEO的优化.
  • 后端路由的缺点:
    • 一种情况是整个页面的模块由后端人员来编写和维护的.
    • 另一种情况是前端开发人员如果要开发页面,需要通过PHP和Java等语言来编写页面代码.
    • 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起,编写和维护都是非常糟糕的事情.
1.2 前端路由阶段
  • 前后端分离阶段︰
    • 随着Ajax的出现,有了前后端分离的开发模式.
    • 后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中
    • 这样做最大的优点就是前后端责任的清晰,后端专注于数据上,前端专注于交互和可视化上.
    • 并且当移动端(iOS/Android)出现后,后端不需要进行任何处理,依然使用之前的一套API即可
    • 目前很多的网站依然采用这种模式开发.
  • 单页面富应用阶段:
    • 其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由
    • 也就是前端来维护一套路由规则
  • 前端路由的核心是什么呢?
    • 改变URL,但是页面否进行整体的刷新。
    • 如何实现呢?

2. vue-router的基本使用

2.1 下载vue-router

打开控制台,在当前文件路径下输入命令

npm install vue-router --save
2.2 安装和配置vue-router
  • src文件夹下创建一个新的文件夹router,再创建一个router.js文件

  • router.js文件中导入路由对象,并且调用Vue.use(VueRouter)(因为是一个插件,所以可以通过Vue.use()来安装路由功能)

    // 1. 配置路由相关信息
    import VueRouter from 'vue-router'
    import Vue from 'vue'
    
    // 2. 安装路由插件
    // 所有插件在使用时都要用到,Vue.use()
    Vue.use(VueRouter);
    
  • 创建路由实例,并且传入路由映射配置

    // 3. 创建VueRouter对象
    const routes = [
        // 配置路由和组件间的映射关系
    ];
    
    const router = new VueRouter({
        // 配置路由和组件之间的应用关系
        routes
    });
    
    // 4. 将路由实例对象倒出
    export default router
    
  • 在Vue实例(main.js)中挂载创建的路由实例

    import router from './router'
    
    new Vue({
      render: h => h(App),
      router,
    }).$mount('#app')
    
2.3 使用vue-router
  • 创建路由组件

    • components文件夹下创建.vue文件
    • 我们创建两个文件Home.vueAbout.vue
  • 配置路由映射:组件和路径映射关系

    // 3. 创建VueRouter对象
    const routes = [
        // 配置路由和组件间的映射关系
        {
            path:'/home',
            component: Home
        },
        {
            path:'/about',
            component: About
        }
    ];
    
  • 使用路由:在App.vue中通过<router-link><router-view>

    <template>
    	<div id="app">
            <router-link to="/home">首页</router-link>
            <router-link to="/about">关于</router-link>
            
            <!-- <router-view>相当于一个占位符,切换到对应路由组件页面就会显示对应的内容 -->
            <router-view></router-view>
        </div>
    </template>
    
2.3.1 <router-link>补充
  • <router-link>的其他属性

  • tag: tag可以指定<router-link>之后渲染成什么组件,比如下面的代码会被渲染成一个<button>元素,而不是<a>

    <router-link to="/home" tag='button'>首页</router-link>
    
  • replace: replace不会留下history记录,所以指定replace的情况下,后退键返回不能返回到上一个页面中

  • active-class: 当<router-link>对应的路由匹配成功时,会自动给当前元素设置一个router-link-active的class,设置active-class可以修改默认的名称。

    <router-link to="/home" active-class="active">首页</router-link>
    
    • 在进行高亮显示的导航菜单或者底部tabbar时,会使用到该类.
    • 但是通常不会修改类的属性,会直接使用默认的router-link-active即可
2.4 设置路由默认路径

默认情况下,进入网站的首页,我们希望<router-view>渲染首页的内容。但是我们的实现中,默认没有显示首页组件,必须让用户点击才可以。如何可以让路径默认跳到到首页,并且<router-view>渲染首页组件呢?非常简单,我们只需要配置多配置一个映射就可以了。

const routes=[
    {
        path: '',
        redirect: '/home'
    }
]
2.5 路由跳转
  • 通过js代码实现路由跳转

    methods:{
        btnClick(){
            this.$router.push('/home');
            
            // 使用replace的情况下,后退键返回不能返回到上一个页面中
            this.$router.replace('/home')
        }
    }
    
2.6 动态路由的使用

我们希望在路由地址后加上id名,类似于一个按钮谁点击了地址后就加上谁的名字。

  • 首先需要改变路由入口文件router.js,在要添加id的映射path上加上/:id

    const routes = [
        {
            path:'/user/:id',
            component: User
        }
    ];
    
  • 第二步,在<router-link>上通过v-bind=""动态绑定路由地址,不需要改变的地方写在单引号里,表示为固定的字符串

    <router-link v-bind:to="'/user/'+userId">用户</router-link>
    
    data(){
        return{
            userId: 'lisi'
        }
    },
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-acY9b7XA-1630677073463)(C:\Users\xmydd\AppData\Roaming\Typora\typora-user-images\image-20210902162014501.png)]

2.6.1 获取id并显示

可以通过this.$route.params.参数名获取路由地址上的参数

.params后的参数名要跟router.js里的routes里的path路径写的一直,上面写的是id,那么后面就.id

export default {
    name: "User",
    computed:{
        userId(){
            return this.$route.params.id;
        }
    }
}

显示

<template>
    <div id="user">
        <p>{{userId}}</p>
    </div>
</template>
2.6.2 r o u t e 和 route和 routerouter的区别
  • $router为VueRouter实例,可以通过this.$router拿到VueRouter实例里定义好的方法。想要导航到不同URL,则使用$router.push方法
  • $route为当前router跳转对象,可以获取当前路由对象的name、path、query、params等属性值
2.7 路由懒加载
2.7.1 路由懒加载
  • 官方给出了解释:

    • 当打包构建应用时,Javascript包会变得非常大,影响页面加载。
    • 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了
  • 官方在说什么呢?

    • 首先,我们知道路由中通常会定义很多不同的页面
    • 这个页面最后被打包在哪里呢?—般情况下,是放在一个js文件中
    • 但是,页面这么多放在一个js文件中,必然会造成这个页面非常的大
    • 如果我们一次性从服务器请求下来这个页面,可能需要花费一定的时间,甚至用户的电脑上还出现了短暂空白的情况
    • 如何避免这种情况呢?使用路由懒加载就可以了
2.7.2 懒加载的方式
  • 方式一:结合vue的异步组件和webpack的代码分析

    const Home = resolve => {require.ensure(['../components/Home.vue'],()=>{resolve(require('../components/Home.vue'))})};
    
  • 方式二:AMD写法

    const About = resolve => require(['../components/About.vue'], resolve);
    
  • 方式三:在ES6中,我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割

    • 没有使用懒加载时我们是直接在路由入口文件中引入组件,现在我们定义一个变量指向引用组件的箭头函数,实现懒加载
    const Home = () => import('../components/Home.vue')
    

3. vue-router嵌套路由

在组件里嵌套其他组件

  • 路由入口文件router.js

    const HomeNews = () => import('../components/HomeNews.vue')
    const News = () => import('../components/News.vue')
    
    const routes = [
        {
            path: '/home',
            component: Home,
            children: [
                {
                    path: '',
                    redirect: 'homenews'
                },
                {
                    path: 'homenews',
                    component: HomeNews
                },
                {
                    path: 'news',
                    component: News
                }
            ]
        }
    ]
    
  • 在父组件Home.vue

    <template>
        <div id="home">
            <router-link to="/home/homenews">首页新闻</router-link>
            <router-link to="/home/news">新闻</router-link>
    
            <router-view></router-view>
        </div>
    </template>
    

4. vue-router参数传递

4.1 参数传递的方式
  • 传递参数主要有两种类型:paramsquery

    • params

      • 配置路由格式: /router/:id
      • 传递的方式: 在path后面跟上对应的值
      • 传递后形成的路径: /router/123, /router/abc
      const routes = [
          {
              path: '/home/:id',
              component: Home
          }
      ]
      
      <router-link :to="'/home'+homeId">首页</router-link>
      <script>
      	export default{
              name: "Home",
              data(){
                  rerurn{
                      homeId: lili
                  }
              }
          }
      </script>
      

      获取id

      <p>{{$route.params.id}}</p>
      
    • query

      • 配置路由格式: /router, 也就是普通配置
      • 传递的方式: 对象中使用querykey作为传递方式
      • 传递后形成的路径: /router?id=123/router?id=abc
      <router-link :to="{path: '/home', query: {name:'lili', age: 18, height: 158}}">首页</router-link>
      
      • 获取
      <p>
          {{$route.query.name}}
          {{$route.query.age}}
          {{$route.query.height}}
      </p>
      

补充:URL

URL:
协议://主机:端口/路径?查询
scheme://host:port/path?query#fragment
4.2 js代码数据传递方式

在不使用<router-link>的情况下也能进行页面跳转并且传递数据

  • 例如,当我们点击一个按钮的时候也能进行页面跳转并且传递数据

    <template>
    	<div>
            <button @click="homeClick">首页</button>
        </div>
    </template>
    
    methods:{
        homeClick(){
       		// 1.传递一个参数
            this.$router.push('/home/' + this.homeId);
            
            // 2. 传递多个参数
            this.$router.push({
                path: '/home',
                query:{
                    name: 'lili',
                    age: 17,
                    height: 167
                }
            })
        }
    }
    

5. vue-router导航守卫

对路由跳转的过程进行监听,使用监听函数实现一些要求

  • 示例:在跳转到不同页面时,我们希望网页标题也会发生改变

    • router.js文件中操作
    // 1. 首先给每一个组件页面设置一个title
    const routes = [
        {
            path: '/home',
            component: Home,
            meta:{
                title: '首页'
            }
        },
        {
            path: '/user',
            component: User,
            meta:{
                title: '用户'
            }
        }
    ]
    
    // 2. 通过beforeEach()实现对路由跳转的监听
    router.beforeEach((to, from, next)=>{
        document.title = to.matched[0].meta.title;
        next();
    })
    
    • to.matched[0]的使用说明

      • 在路由组件存在嵌套的情况下使用to.matched[0]
      • 我们可以打印一下to(console.log(to)😉,会发现在路由组件存在嵌套的情况时,meta是为空的,里面没有我们设置的title。而数组matched里有两个元素,子组件和父组件
        在这里插入图片描述
      • 在第一位元素(父组件)里找到了我们设置的title
        在这里插入图片描述
      • 当不存在组件嵌套时,可以直接写
      document.title = to.meta.title;
      

6. keep-alive

**前提:**有两个组件Home、UserHome下又有两个子组件
在这里插入图片描述

  • Home组件下创建两个生命周期函数created、destroyed,在控制台打印一串字符。

    created(){
        // console.log('home created');
    },
    destroyed(){
        // console.log('home destroyed');
    },
    
  • 我们可以发现,进入Home组件页面时,控制台会打印home created;l离开时会打印home destroyed。即Home组件的创建和销毁

  • 每一次切换和重新进入Home组件页面都会新建一个组件,这样会导致我们上一次在Home组件页面上的任何操作都不会被保留。

  • 我们可以通过<keep-alive>解决这个问题

6.1 解决办法

**要求:**不管我们点击了Home组件的哪一个子组件,重新进入后页面还是显示上一次点击的子组件的内容

  • 第一步:要实现上面的要求,首先就不能将Home的某一个子组件重定向(redirect)为首页

  • 第二步:将要缓存的组件用keep-alive包裹起来,根据要求我们是在HomeUser两个组件中来回切换,需要对这两个组件进行缓存

    • 在哪引用了路由就写在哪
    <template>
      <div id="app">
        <router-link to="/home/news" tag="button">首页</router-link>
        <router-link :to="'/user/'+userId" tag="button">用户</router-link>
        
        <keep-alive>
            <router-view></router-view>
        </keep-alive>
      </div>
    </template>
    
  • 第三步:使用activated()生命周期函数初始化页面数据

    • vue对象存活的前提下(我们之前使用<keep-alive>就是为了实现这个目的),进入当前存在activated()函数的页面时,一进入页面就触发this.$router.push('/home/news');,这样已进入页面就会显示我们想要看到的内容
    • Home组件中实现
    export default {
        name: 'Home',
        data(){
            return{
                path: '/home/news'
            }
        },
        activated(){
            this.$router.push(this.path).catch((e) => e);
        }
    }
    
  • 第四步:实现重新进入后页面还是显示上一次点击的子组件的内容

    • 可以通过this.$route.path获取到每一次是从哪个路由路径离开的
    beforeRouteLeave(to, from, next){
        this.path = this.$route.path;
        next();
    }
    
6.2 补充

当我们不希望某个组件被频繁销毁创建时,我们就需要用到keep-alive两个属性

  • include - 字符串或正则表达,只有匹配的组件会被缓存

  • exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存

    • exclude里写的字符串是对应组件的name
    • 写多个组件时中间不要有空格
    <keep-alive exclude="User,Home">
        <router-view></router-view>
    </keep-alive>
    

在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值