• Vue学习:路由


    2. 路由

    2.1 前端路由的发展历程

    2.1.1 认识前端路由

    路由其实是网络工程中的一个术语:
    在架构一个网络时,非常重要的两个设备就是路由器和交换机
    在这里插入图片描述

    ​ 当然,目前在我们生活中路由器也是越来越被大家所熟知,因为我们生活中都会用到路由器:
    事实上,路由器主要维护的是一个映射表;映射表会决定数据的流向;

    前端路由: 就是一组key-value的映射(通俗的讲:就是一个url与组件/function的映射)

    前端路由器: 管理多个路由的,就称为路由器
    在这里插入图片描述

    2.1.2 路由的发展历程

    ​ 路由的概念在软件工程中出现,最早是在后端路由中实现的,原因是web的发展主要经历了这样一些阶段:

    • 后端路由阶段;
    • 前后端分离阶段;
    • 单页面富应用(SPA);

    阶段一: 后端路由阶段:

    • 早期的网站开发整个HTML页面是由服务器来渲染的.
    • 服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示.
    • 但是, 一个网站, 这么多页面服务器如何处理呢?
    • 一个页面有自己对应的网址, 也就是URL;
    • URL会发送到服务器, 服务器会通过正则对该URL进行匹配, 并且最后交给一个Controller进行处理;
    • Controller进行各种处理, 最终生成HTML或者数据, 返回给前端.
    • 上面的这种操作, 就是后端路由:
    • 当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户端.
    • 这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化.
    • 后端路由的缺点:
    • 一种情况是整个页面的模块由后端人员来编写和维护的;
    • 另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码;
    • 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情;

    阶段二: 前后端分离阶段

    • 前端渲染的理解:
    • 每次请求涉及到的静态资源都会从静态资源服务器获取,这些资源包括HTML+CSS+JS,然后在前端对这些请求回来的资源进行渲染;
    • 需要注意的是,客户端的每一次请求,都会从静态资源服务器请求文件;
    • 同时可以看到,和之前的后端路由不同,这时后端只是负责提供API了;
    • 前后端分离阶段:
    • 随着Ajax的出现, 有了前后端分离的开发模式;
    • 后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中;
    • 这样做最大的优点就是前后端责任的清晰,后端专注于数据上,前端专注于交互和可视化上;
    • 并且当移动端(iOS/Android)出现后,后端不需要进行任何处理,依然使用之前的一套API即可;

    阶段三: 单页面富应用阶段:

    • SPA(single page web application): 单页面富应用, 整个应用只有一个完整的页面.

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

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

    • 前端路由的核心是什么呢?改变URL,但是页面不进行整体的刷新。

    2.2 路由的基本使用

    使用步骤:

    1. 安装vue-router插件

      npm i vue-router
      
      • 1
    2. 在src目录下创建router目录,再在router目录下创建一个index.js(该文件用于创建整个应用的路由器)

    1. 在main.js文件中使用vue-router

      //引入vue-router
      import router from './router'
      
      //使用VueRouter
      createApp(App).use(router).mount('#app')
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. 在router目录下的index.js文件创建路由器对象,并进行相关的路由规则的设置

    //引入函数
    import { createRouter,createWebHistory } from 'vue-router'
    //引入组件
    import Home from '../components/Home'
    import About from '../components/About'
    const routes = [
        //配置路由规则  
        {   
            path:'/home',
            component:Home
        },
        {   
            path:'/about',
            component:About
        },
    ]
    //创建路由器实例并传递 `routes` 配置
    const router = createRouter({
        history: createWebHistory(process.env.BASE_URL),
        routes
      })
    
      //导出router
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. 编写Home和About组件

    About.vue

    <template>
        <div>
            <p class="lead">About内容...p>
        div>
    template>
    
    <script>
    export default {
        name: 'About',
    };
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Home.vue

    <template>
        <div>
            <p class="lead">Home内容...p>
        div>
    template>
    
    <script>
    export default {
        name: 'Home',
    };
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 编写App.vue

      <template>
        <nav class="nav">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">Homea>li>
                <li><a href="#">Abouta>li>
            ul>
        nav>
        <div style="margin-top: 60px;">
           组件内容展示区域
        div>
      template>
      
      <script>
      import Home from './components/Home.vue'
      import About from './components/About.vue'
      export default {
        name: 'App',
        data() {
          return {
          };
        },
        components: {
          Home,
          About,
        },
      };
      script>
      <style lang="css" scoped>
        li{
          list-style-type: none;
        }
        .nav{
          height: 50px;
          line-height: 50px;
          background-color: #999;
        }
        .nav ul{
          display: flex;
        }
        .nav ul li{
           margin-right: 20px;
           display: flex;
        }
       .active{
            background-color : skyblue;
            padding:0px 10px;
            color: #FFF;
         }
      style>
      
      • 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
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
    2. 使用router-link替换App.vue中的标签

       <li class="active"><router-link to="/home">Homerouter-link>li>
       <li><router-link to="/about">Aboutrouter-link>li> 
      
      • 1
      • 2

    在这里插入图片描述

    1. 使用router-view替换组件内容展示区
      在这里插入图片描述

    2. 测试
      在这里插入图片描述

    我们发现.当我们点击不同的导航,但是导航的样式没有跟着变,那是因为我们把active类样式写死在Home上,如果想做到,点击不同的导航,样式也跟着切换,我们可以在标签上添加active-class=‘类名’
    在这里插入图片描述

    如果我们的导航很多,那我们就需要在每个标签上添加active-class='类名’很繁琐,其实我们也可以在创建router对象,进行设置linkActiveClass属性,而需要在每个设置:

    在这里插入图片描述

    router-link的其他属性:

    • to属性:
      是一个字符串,或者是一个对象

    • replace属性:
      设置 replace 属性的话,当点击时,会调用 router.replace(),而不是 router.push()[默认模式];

    • active-class属性:
      设置激活a元素后应用的class,默认是router-link-active

    • exact-active-class属性:
      链接精准激活时,应用于渲染的 的 class,默认是router-link-exact-active;

    路由的默认路径

    ​ 上面的案例还有一个不太好的实现:默认情况下, 进入网站的首页, 我们希望渲染首页的内容,但是我们的实现中, 默认没有显示首页组件, 必须让用户点击才可以,如何可以让路径默认跳到到首页, 并且渲染首页组件呢?

    我们在routes中又配置了一个映射:
    
    -   path配置的是根路径: /			
    
    - redirect是重定向, 也就是我们将根路径重定向到/home的路径下, 这样就可以得到我们想要的结果了			
    
      ```javascript
       {   
           //路由默认路径
           path:'/',
               redirect:'/home'
       }
      ```
    
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HdNQQYP9-1667816366247)(assets/image-20221029204811975.png)]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注意:

    1. 在实际开发中,我们会把组件分为两类, 一类是路由组件,一类是普通组件, 我们会把普通组件放在components目录下, 而路由组件则放在views或者pages目录下
    2. 通过调用 app.use(router),我们可以在任意组件中以 this.$router 的形式访问它,并且以 this.$route 的形式访问当前路由。this.$router 与直接使用通过 createRouter 创建的 router 实例完全相同。

    2.3 嵌套路由

    ​ 目前我们匹配的Home、About等都属于第一层路由,我们在它们之间可以来回进行切换。但是呢,我们Home页面本身,也可能会在多个组件之间来回切换:比如Home中包括Product、Ad,它们可以在Home内部来回切换;这个时候我们就需要使用嵌套路由,在Home中也使用 router-view 来占位之后需要渲染的组件。

    HomeProduct.vue

    <template>
        <ul>
            <li v-for=" p in productList">{{p}}li>
        ul>
    template>
    
    <script>
    export default {
        name: 'HomeProduct',
    
        data() {
            return {
                productList:["小米手机","华为电脑","康佳相机"],
    
            };
        },
    };
    script>
    
    <style lang="css" scoped>
        
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    HomeAd.vue

    <template>
        <ul>
            <li v-for=" ad in adList">{{ad}}li>
        ul>
    template>
    
    <script>
    export default {
        name: 'HomeAd',
    
        data() {
            return {
                adList:["全场商品打5折","为了回馈老客户,让利大行动","你还在等什么!心动不如行动"],
    
            };
        },
    };
    script>
    
    <style lang="css" scoped>
    
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    Home.vue

    <template>
        <h2>Home内容...h2>
        <nav class="nav">
            <ul>
                <li ><router-link   to="/home/product"  >Productrouter-link>li>
                <li ><router-link  to="/home/ad" >Adrouter-link>li> 
            ul>
        nav>
        <div>
            <router-view>router-view>
        div>
    template>
    
    <script>
    export default {
        name: 'Home',
    
        data() {
            return {
                
            };
        },
        methods: {
            
        },
    };
    script>
    
    <style lang="css" scoped>
        
    style>
    
    • 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

    修改router/index.js

    {   
        path:'/home',
            component:Home,
                children:[
                    {
                        path:'',
                        component:HomeProduct
                    },
                    {
                        path:'product',
                        component:HomeProduct
                    },
                    {
                        path:'ad',
                        component:HomeAd
                    }
                ]
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    注意:

    1. 子级的path不需要加/

    2.4 路由懒加载

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

    ​ Vue Router 支持开箱即用的动态导入,这意味着你可以用动态导入代替静态导入:这是因为component可以传入一个组件,也可以接收一个函数,该函数 需要放回一个Promise;
    而import函数就是返回一个Promise;
    在这里插入图片描述

    2.5 动态路由匹配

    2.5.1 带参数的动态路由匹配

    ​ 很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数

    User.vue

    <template>
       <h2>用户:h2>
    template>
    
    <script>
    export default {
        name: 'User',
        data() {
            return {
                
            };
        },
    };
    script>
    
    <style lang="css" scoped>
        
    style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    配置路由:

    { 
        path: '/users/:id',
        component: ()=>import('../views/User') 
    },
    
    • 1
    • 2
    • 3
    • 4

    现在像 /users/johnny/users/jolyne 这样的 URL 都会映射到同一个路由。

    获取动态路由的值:

    路径参数用冒号 : 表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 this.$route.params 的形式暴露出来。因此,我们可以通过更新 User 的模板来呈现当前的用户 ID:

    <template>
       <h2>用户:{{$route.params.id}}h2>
    template>
    
    
    • 1
    • 2
    • 3
    • 4
    • 在template中,直接通过 $route.params获取值;
      在这里插入图片描述

    • 在created中,通过 this.$route.params获取值;

    • 在setup中,我们要使用 vue-router库给我们提供的一个hook的useRoute;
      该Hook会返回一个Route对象,对象中保存着当前路由相关的值

    你可以在同一个路由中设置有多个 路径参数,它们会映射到 $route.params 上的相应字段。例如:

    匹配模式匹配路径$route.params
    /users/:username/users/eduardo{ username: 'eduardo' }
    /users/:username/posts/:postId/users/eduardo/posts/123{ username: 'eduardo', postId: '123' }

    注意:

    使用 $route 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 props 配置来解除这种行为:

    我们可以将下面的代码

    替换成:

    props 设置为 true 时,route.params 将被设置为组件的 props

    2.5.2 404 Not found 路由

    ​ 对于哪些没有匹配到的路由,我们通常会匹配到固定的某个页面。比如NotFound的错误页面中,这个时候我们可编写一个动态路由用于匹配所有的页面;

    ​ 常规参数只匹配 url 片段之间的字符,用 / 分隔。如果我们想匹配任意路径,我们可以使用自定义的路径参数正则表达式,在 路径参数后面的括号中加入 正则表达式 :

    // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
    { path: '/:pathMatch(.*)', 
      component: ()=>import('../views/NotFound') 
    },
    
    • 1
    • 2
    • 3
    • 4

    我们可以通过 $route.params.pathMatch获取到传入的参数:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aSgJ2PKi-1667816366266)(assets/image-20221030103501207.png)]

    注意:

    这里还有另外一种写法:

    { path: '/:pathMatch(.*)*', 
     component: ()=>import('../views/NotFound')
    },
    
    • 1
    • 2
    • 3

    在/:pathMatch(.*)后面又加了一个 *

    它们的区别在于解析的时候,是否解析 /:

    • path: '/:pathMatch(.*)'写法

    • path: '/:pathMatch(.*)*'写法

    2.6 query方式的参数

    ​ 我们使用动态路由的方式可以传递数据, 但是我们也可以像url传递参数的方式,在to的后面使用?传递参数。

    ​ 我们修改ad的路由,把广告的id和标题作为参数传递给HomeAd组件, 并在HomeAd组件展示.

    HomeAd.vue

    <template>
        <ul>
            <li v-for=" ad in adList">
                <router-link to="/home/ad/detail?id=ad.id&title=ad.title">{{ad.title}}router-link>
            li>
        ul>
        <hr/>
        <router-view/>
    template>
    
    <script>
    export default {
        name: 'HomeAd',
    
        data() {
            return {
                adList:[
                    {id:1,title:'全场商品打5折'},
                    {id:2,title:'为了回馈老客户,让利大行动'},
                    {id:3,title:'你还在等什么!心动不如行动'}
                ],
    
            };
        },
    };
    script>
    
    <style lang="css" scoped>
    
    style>
    
    • 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

    我们发现 {{ad.title}}使用?给组件传递的id和title数据。

    接下来,我们创建一个AdDetail组件,用于展示

    AdDetial.vue

    <template>
        <div>
            <p>编号:{{$route.query.id}}p>
            <p>标题:{{$route.query.title}}p>
        div>
    template>
    
    <script>
    export default {
        name: 'AdDetial',
    
        data() {
            return {
                
            };
        },
        mounted(){
            //console.log(this.$route);
        }
    };
    script>
    
    <style lang="scss" scoped>
    
    style>
    
    • 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

    我们可以使用this.$route获取当前路由的配置
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCLEoWjU-1667816366276)(assets/image-20221030113537866.png)]

    在template标签中可以使用{{$route.query.title}}获取传递的参数

    但是我们会发现,不管我们点击那个广告导航,AdDetail组件显示都是ad.idad.title

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ipsde6pa-1667816366277)(assets/image-20221030113746315.png)]

    为什么会是这样呢? 原来是我们的 {{ad.title}}使用的是to属性,所以双引号的只作为普通字符串. 那怎么把ad.id与ad.title作为一个变量呢?

    我们可以使用模板字符串:

    <router-link :to="`/home/ad/detail?id=${ad.id}&title=${ad.title}`">{{ad.title}}router-link>
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NcI0yw9X-1667816366278)(assets/image-20221030114134117.png)]

    虽然我们使用to的模板字符串的写法,进行传参,但是如果我们需要传递多个参数,那模板字符串的写法就很不美观,那我们可以使用to的对象写法:

       <li v-for=" ad in adList">
                
                <router-link :to="{
                  path:'/home/ad/detail',
                  query:{
                    id:ad.id,
                    title:ad.title
                  }
                }">{{ad.title}}router-link>
            li>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBsiNNhG-1667816366283)(assets/image-20221030115012707.png)]

    我们可以关闭TypeScript的验证,然后重启vscode

    2.7 命名路由

    除了 path 之外,你还可以为任何路由提供 name。这有以下优点:

    • 没有硬编码的 URL
    • params 的自动编码/解码。
    • 防止你在 url 中出现打字错误。
    • 绕过路径排序(如显示一个)

    要链接到一个命名的路由,可以向 router-link 组件的 to 属性传递一个对象:

     <li><router-link  :to="{ name: 'user', params: { id: '101' }}">Userrouter-link>li> 
    
    • 1

    注意:

    如果使用params传递参数, 不能写path,只能写name

    2.8 编程式导航

    ​ 除了使用 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

    2.8.1 导航到不同的位置

    在 Vue 实例中,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

    想要导航到不同的 URL,可以使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。

    当你点击 时,内部会调用这个方法,所以点击 相当于调用 router.push(...)

    声明式编程式
    router.push(...)

    该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

    // 字符串路径
    router.push('/users/eduardo')
    
    // 带有路径的对象
    router.push({ path: '/users/eduardo' })
    
    // 命名的路由,并加上参数,让路由建立 url
    router.push({ name: 'user', params: { username: 'eduardo' } })
    
    // 带查询参数,结果是 /register?plan=private
    router.push({ path: '/register', query: { plan: 'private' } })
    
    // 带 hash,结果是 /about#team
    router.push({ path: '/about', hash: '#team' })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    注意:如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path

    const username = 'eduardo'
    // 我们可以手动建立 url,但我们必须自己处理编码
    router.push(`/user/${username}`) // -> /user/eduardo
    // 同样
    router.push({ path: `/user/${username}` }) // -> /user/eduardo
    // 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
    router.push({ name: 'user', params: { username } }) // -> /user/eduardo
    // `params` 不能与 `path` 一起使用
    router.push({ path: '/user', params: { username } }) // -> /user
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.8.2 替换当前位置

    它的作用类似于 router.push,唯一不同的是,它在导航时不会向 history 添加新记录,正如它的名字所暗示的那样——它取代了当前的条目。

    声明式编程式
    router.replace(...)

    也可以直接在传递给 router.pushrouteLocation 中增加一个属性 replace: true

    router.push({ path: '/home', replace: true })
    // 相当于
    router.replace({ path: '/home' })
    
    • 1
    • 2
    • 3
    2.8.3 页面的前进后退

    router的go方法

    该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步,类似于 window.history.go(n)

    // 向前移动一条记录,与 router.forward() 相同
    router.go(1)
    
    // 返回一条记录,与 router.back() 相同
    router.go(-1)
    
    // 前进 3 条记录
    router.go(3)
    
    // 如果没有那么多记录,静默失败
    router.go(-100)
    router.go(100)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    router也有back:
    通过调用 history.back() 回溯历史。相当于 router.go(-1);

    router也有forward:
    通过调用 history.forward() 在历史中前进。相当于 router.go(1);

    2.8.4 动态管理路由的其他方法

    删除路由有以下三种方式:

    方式一:添加一个name相同的路由;

    方式二:通过removeRoute方法,传入路由的名称;

    方式三:通过addRoute方法的返回值回调;

    路由的其他方法补充:

    • router.hasRoute():检查路由是否存在。
    • router.getRoutes():获取一个包含所有路由记录的数组。

    2.9 路由守卫

    vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。路由守卫就是路由跳转过程中的一些钩子函数,在路由跳转的时候,做一些判断或其它的操作。 类似于组件生命周期钩子函数。
    
    • 1

    ​ 所谓的路由守卫可以简单的理解为一座房子的门口的保安,想要进入这个房子就必须通过保安的检查,要告诉路由守卫你**从哪里来(from)?要到哪里去(to)?然后保安再告诉你下一步该怎么做(next())?**如果你的确是这个房子主人允许进入的人,那就让你进入,否则就要打电话给房子主人,跟房主商量(登录注册),给你权限。

    分类: 全局的,单个路由独享的,或者组件级的。

    2.9.1 全局守卫

    ​ 所谓全局路由守卫,就是小区大门,整个小区就这一个大门,你想要进入其中任何一个房子,都需要经过这个大门的检查。

    vue-router全局有三个守卫:

    1. router.beforeEach 全局前置守卫 进入路由之前
    2. router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用
    3. router.afterEach 全局后置钩子 进入路由之后
    2.9.1.1 全局前置守卫

    你可以使用 router.beforeEach 注册一个全局前置守卫:

    beforeEach 它有两个参数:

    • to:即将进入的路由Route对象;
    • from:即将离开的路由Route对象;

    它有返回值:

    • false:取消当前导航;
    • 不返回或者undefined:进行默认导航;
    • 返回一个路由地址:
    • 可以是一个string类型的路径;
    • 可以是一个对象,对象中包含path、query、params等信息;

    可选的第三个参数:next(不推荐使用)

    • 在Vue2中我们是通过next函数来决定如何进行跳转的;
    • 但是在Vue3中我们是通过返回值来控制的,不再推荐使用next函数,这是因为开发中很容易调用多次next;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qOIaVKf5-1667816366295)(assets/image-20221031202544204.png)]

    ​ 比如我们完成一个功能,只有登录后才能访问home/product和home/ad,如果没有登录,只能访问home和about, 前端怎么知道用户是否登录呢? 比如我们在登录成功之后, 后台会产生一个唯一的字符串,这个字符串我们称为token, 并响应给客户端,客户端会把这个token保存在浏览器的本地存储对象:localStorage中.

    ​ 我们首先创建一个登录页面,

    Login.vue

     <template>
        <div>
            <form action="/home" ref="loginForm">
                <label for="username">用户名:</label>
                <input id="username"  v-model="username"/>
                <hr/>
                <label for="pwd">密码:</label>
                <input id="pwd"  v-model="password" type="password"/>
                <hr/>
                <button type="button" @click="doLogin">登录</button>
            </form>
        </div>
    </template>
    
    <script>
    export default {
        name: 'Login',
    
        data() {
            return {
                username:'',
                password:''
            };
        },
    
        mounted() {
            
        },
    
        methods: {
            doLogin(){
                if(this.username==='admin' && this.password==='123'){
                    localStorage.setItem("token",this.username)
                    alert('登录成功!!!');
                    this.$refs.loginForm.submit();
                }else{
                    alert('登录失败!!!');
                    this.$refs.loginForm.reset();
                }
            }
        },
    };
    </script>
    
    <style lang="scss" scoped>
    
    </style>
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47

    并在router/index.js配置一下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lvy5x7z4-1667816366296)(assets/image-20221101062849597.png)]

    在App.vue的导航我们添加一个登录的按钮

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4OfAHW2-1667816366301)(assets/image-20221101062936603.png)]

    并实现跳转到Login.vue

     toLogin(){
          this.$router.push({
            path:'/login'
          });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    现在我们使用全局前置守卫,来实现登录的访问控制, 在router/index.js文件中创建一个前置路由守卫:

    //全局的前置路由守卫  --- 初始化的时候被调用,每次路由切换之前被调用
    router.beforeEach((to,from) =>{
        if(to.path === '/home/product' || to.path === '/home/ad'){
            if(!localStorage.getItem("token")){
                return {
                    path:'/login'
                };
            }
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其他路由守卫,请参考https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

    2.10 不同的历史模式

    	在创建路由器实例时,`history` 配置允许我们在不同的历史模式中进行选择。
    
    • 1
    2.10.1 Hash 模式

    ​ hash 模式是用 createWebHashHistory() 创建的:

    import { createRouter, createWebHashHistory } from 'vue-router'
    
    const router = createRouter({
      history: createWebHashHistory(),
      routes: [
        //...
      ],
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ​ 它在内部传递的实际 URL 之前使用了一个哈希字符(#)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。

    2.10.2 HTML5 模式

    ​ 用 createWebHistory() 创建 HTML5 模式,推荐使用这个模式:

    import { createRouter, createWebHistory } from 'vue-router'
    
    const router = createRouter({
      history: createWebHistory(),
      routes: [
        //...
      ],
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当使用这种历史模式时,URL 会看起来很 “正常”,例如 https://example.com/user/id。漂亮!

    不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id,就会得到一个 404 错误。这就丑了。

    不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html 相同的页面。漂亮依旧!

    nginx服务器:

    location / {
      try_files $uri $uri/ /index.html;
    }
    
    • 1
    • 2
    • 3

    rom) =>{
    if(to.path === ‘/home/product’ || to.path === ‘/home/ad’){
    if(!localStorage.getItem(“token”)){
    return {
    path:‘/login’
    };
    }
    }
    });

    
    其他路由守卫,请参考https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
    
    
    
    ### 2.10 不同的历史模式
    
      		在创建路由器实例时,`history` 配置允许我们在不同的历史模式中进行选择。
    
    #### 2.10.1 Hash 模式
    
    ​		hash 模式是用 `createWebHashHistory()` 创建的:
    
    ```javascript
    import { createRouter, createWebHashHistory } from 'vue-router'
    
    const router = createRouter({
      history: createWebHashHistory(),
      routes: [
        //...
      ],
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    ​ 它在内部传递的实际 URL 之前使用了一个哈希字符(#)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。

    2.10.2 HTML5 模式

    ​ 用 createWebHistory() 创建 HTML5 模式,推荐使用这个模式:

    import { createRouter, createWebHistory } from 'vue-router'
    
    const router = createRouter({
      history: createWebHistory(),
      routes: [
        //...
      ],
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当使用这种历史模式时,URL 会看起来很 “正常”,例如 https://example.com/user/id。漂亮!

    不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id,就会得到一个 404 错误。这就丑了。

    不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html 相同的页面。漂亮依旧!

    nginx服务器:

    location / {
      try_files $uri $uri/ /index.html;
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    1038. 从二叉搜索树到更大和树
    java面试题
    (一)模型量化与RKNN模型部署
    Linux命令(99)之rz
    es笔记六之聚合操作之指标聚合
    电脑重装系统后如何在防火墙设置允许浏览器访问网络
    开发测试平台难吗?
    详解设计模式:外观模式
    Java并发编程: Thread常见方法
    Python与开源GIS:开始使用GDAL
  • 原文地址:https://blog.csdn.net/H215919719/article/details/127736449