Vue——路由

文章目录

路由简介

1.路由就是一组key-value的对应关系。 路由——route
2.多个路由,需要经过路由器的管理。 路由器——router

路由的作用:SPA

路由是为了实现 SPA (single page web application)应用,即单页面开发
单页面开发:

  • 单页面开发整个应用只有一个完整的页面

  • 单页面开发即不跳转其他页面的页面开发

  • 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。

  • 数据需要通过ajax请求获取。

  • 单页面开发的例子(路由实现)
    在这里插入图片描述
    原理解释:不同的页签对应着不同的请求地址,然后Router(路由器)根据请求地址在所管理的路由中查找请求地址对应的组件,将该组件显示在显示区。

路由的理解

—个路由就是一组映射关系(key - value)
key为路径, value可能是function或componente

路由分类

前端路由

  • key为路径, value是component,用于展示页面内容。
  • 工作过程:当浏览器的路径改变时,对应的组件就会显示。

后端路由

  • key为路径, value是 function,用于处理客户端提交的请求。
  • 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处
    理请求,返回响应数据。

路由的使用

路由配置—— vue-router

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

  • 安装:npm i vue-router
    (注意版本问题:vue2中需要使用 vue-router的3版本;vue3中可以使用 vue-router的4版本)
  • 在mian.js中进行配置:
// 引入router
import VueRouter from 'vue-router' 
Vue.use(VueRouter)
  • router配置
    引入路由插件之后就可以在vue实例中配置router(路由器)
    配置对象router的值必须是一个路由器,那么我们需要先创建一个路由器:
    创建src/router/index.js,在index页面中创建一个路由
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'

// 创建并暴露一个路由器,管理路由
export default new VueRouter({
    
    
    // 路由
    routes: [
        {
    
    
            path: '路径',
            component:要展示的组件,
        },
    ]
})

之后再main.js中引入

// 引入路由器
import router from './router'
// 创建vue实例对象
new Vue({
    
    
  router:router,
})

这样路由的框架就搭好了,接下来是路由的使用。

路由的使用

编写路由

  • 首先可以写几个路由,方便后面的使用:
    src/router/index.js
// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'

// 引入组件,作为key-value的value值
import AboutVue from '@/components/About.vue'
import HomeVue from '@/components/Home.vue'

// 创建并暴露一个路由器,管理路由
export default new VueRouter({
    
    
    // 路由
    routes: [
        {
    
    
            path: '/about',
            component:AboutVue,
        },
        {
    
    
             path: '/home',
            component:HomeVue,
        }
    ]
})

触发路由 router-link

路由写完之后,需要触发路径才能显示相应的组件,那如何触发路径呢?,使用 vue-router 为我们提供的 router-link标签
<router-link active-class="active" to="请求路径">About</router-link>

  • to属性中放置请求的路径,与路由route中的key值对应。
  • router-link标签生成的其实就是我们的导航区
  • active-class,可以添加动态样式,当该页签别选中时该页签的样式。

eg:

<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active"  to="/home">Home</router-link>

注:router-link最终会被解析成a标签

组件的显示——router-view

经过前面两步,我们已经配置好路由,以及路由的触发,还差最后一步,路由出发之后通过路由器查找到的组件应该显示在哪里,即组件的显示位置问题
Vue中借助router-view标签指定组件的显示位置
<router-view></router-view>在哪,组件就显示在哪。

路由过程总结

当我们触发路由之后(提供路由的key值),路由器为我们匹配路由(即通过组件的key值找value值),就最后需要将匹配的组件显示在页面上(显示value值

案例

效果:路由切换
在这里插入图片描述
在这里插入图片描述
src/router/index.js

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'

// 引入组件,作为key-value的value值
import AboutVue from '@/components/About.vue'
import HomeVue from '@/components/Home.vue'

// 创建并暴露一个路由器,管理路由
export default new VueRouter({
    
    
    // 路由
    routes: [
        {
    
    
            path: '/about',
            component:AboutVue,
        },
        {
    
    
             path: '/home',
            component:HomeVue,
        }
    ]
})

main.js

/**
 * 该文件是整个项目的入口文件
 */

// 引入vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
// 引入VueRouter插件,进行配置
import VueRouter from 'vue-router' 
// 引入路由器
import router from './router'
// 关闭vue生产提示
Vue.config.productionTip = false

Vue.use(VueRouter)


// 创建vue实例对象
new Vue({
    
    
  el: "#root",
  render: h => h(App),
  router:router,
})

App.vue

<template>
  <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
        <div class="page-header">
          <h2>Vue Router Demo</h2>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <!-- 原始使用a标签跳转多个页面,多页面应用-->
          <!-- <a class="list-group-item active" href="./about.html">About</a>
            <a class="list-group-item" href="./home.html">Home</a> -->

          <!-- Vue中借助router-link标签实现路由的切换 -->
          <router-link class="list-group-item" active-class="active" to="/about">About</router-link>
          <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <!-- Vue中借助router-view指定组件的显示位置-->
            <router-view></router-view>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
      
      
  name: "App",
}
</script>
<style lang="css" scoped>
</style>

About.vue

<template>
  <h2>我是About的内容</h2>
</template>

<script>
export default {
      
      
    name:'AboutVue'
}
</script>

<style>

</style>

Home.vue

<template>
    <h2>我是Home的内容</h2>
</template>

<script>
export default {
      
      
    name: 'HomeVue'
}
</script>

<style>
</style>

路由使用注意点

组件的分类

  • 路由组件:按路由规则,由路由器渲染出来的组件我们叫做路由组件(上述的About和Home组件就是路由组件)
  • 一般组件:我们正常使用组件标签渲染在模板中的组件叫做一般组件

路由组件和一般组件一般分开放。
一般将路由组件放在pages文件夹中,而一般组件放在components文件夹中

不使用的路由组件

对于不显示的路由组件实际上是销毁了,如上述例子当显示About组件时,Home组件看不见了实际上Home组件是被销毁了,而不是隐藏了。

路由组件多出来两个属性

路由组件的实例对象比一般组件多出来两个属性route和router
route:路由属性,里面放置的是和该组件有关的路由
router:路由器,放置的是全局的路由器

eg:Home组件
在这里插入图片描述

嵌套(多级)路由

嵌套路由效果展示
在这里插入图片描述

多级路由就是在路由组件中再次按路由规则编写代码,上述图片就是在Home组件中再次编写路由。
嵌套路由的编写规则:

路由器文件的编写

最外层的路由是一级路由,在一级路由里面可以编写二级路由,二级路由的编写就是嵌套路由,桥套路由需要写在一级路由的children配置项中:
注意:二级路由的路径前面无需写 / ,vue的路由已经帮我们配置好了

在这个例子中需要向home组件编写二级路由:
router/index.js

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'

// 引入组件,作为key-value的value值
import AboutVue from '@/pages/About.vue'
import HomeVue from '@/pages/Home.vue'
import NewsVue from '@/pages/News.vue'
import MessageVue from '@/pages/Message.vue'

// 创建并暴露一个路由器,管理路由
export default new VueRouter({
    
    
    // 路由
    routes: [
        {
    
    
            path: '/about',
            component: AboutVue,
        },
        {
    
    
            // 一级路由
            path: '/home',
            component: HomeVue,
            children: [
                {
    
    
                    // 二级路由,子路由的路径前面无需写/,vue的路由已经帮我们配置好了
                    path: 'news',
                    component:NewsVue
                },
                {
    
    
                    path: 'message',
                    component:MessageVue
                },
            ]
        }
    ]
})

触发路由 router-link,显示路由

由于是在home组件中的二级路由,所以在home组件中编写router-link
to属性路径要写 /home/xxx
显示路由的标签<router-view/>正常写
home.vue

<template>
    <div>
        <h2>我是Home的内容</h2>
        <div>
            <ul class="nav nav-tabs">
                <li>
                    <router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
                </li>
                <li>
                    <router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
                </li>
            </ul>
            <router-view>
            </router-view>
        </div>
    </div>
</template>

<script>
export default {
      
      
    name: "HomeVue",
}
</script>

<style scoped>
</style>

About.vue:

<template>
  <h2>我是About的内容</h2>
</template>

<script>
export default {
      
      
  name: 'AboutVue',
  mounted() {
      
      
    console.log("About组件挂载完毕",this)
    window.aboutRoute = this.route
      window.aboutRouter = this.router
    }
}
</script>

<style>

</style>

News.vue

<template>
    <ul>
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
    </ul>
</template>

<script>
export default {
      
      
    name: "NewsVue"
}
</script>

<style scoped>
</style>

问题

问题一:<router-link><router-view/>的匹配问题

有没有想过这样一个问题:
一个项目会有很多的<router-link>标签和 <router-view/>,我们知道<router-link>标签的请求结果会在 <router-view/>标签中显示,但是怎么确定某一个<router-link>标签的请求结果在哪一个 <router-view/>标签中显示呢?

如果一个组件的html模板中有<router-link>标签和一个<router-view/>标签,那么该<router-link>标签的请求结果一定会在该<router-view/>标签中显示吗?
答案是否定的。

一个<router-link>标签的请求结果在哪一个 <router-view/>标签中显示,不是取决于这两个标签是否写在同一个页面,而是取决于<router-link>标签中的请求地址,即<router-link>标签的to的请求地址写的是哪,该<router-link>标签的请求结果就显示在该请求地址的最底层的上一层的组件的 <router-view/>标签中。

例子

eg:

  • 如果 有
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>

那么该<router-link>标签的请求结果就显示在home组件中

    • 如果 有
<router-link class="list-group-item" active-class="active" to="/home">News</router-link>

那么该<router-link>标签的请求结果就显示在app组件中
(home组件的上一层就是app组件了,app组件在路径中不会显示)

  • 如果 有
<router-link class="list-group-item" active-class="active" to="/home/news/nefu">News</router-link>

那么该<router-link>标签的请求结果就显示在news组件中

代码演示

home.vue组件

<template>
    <div>
        <h2>我是Home的内容</h2>
        <div>
            <ul class="nav nav-tabs">
                <li>
                    <router-link class="list-group-item" active-class="active" to="/news">News</router-link>
                </li>
                <li>
                    <router-link class="list-group-item" active-class="active" to="/message">Message</router-link>
                </li>
            </ul>
            <!-- <router-view>
            </router-view> -->
        </div>
    </div>
</template>

<script>
export default {
      
      
    name: "HomeVue",
}
</script>

<style scoped>
</style>

可以看出里面两个 <router-link>的返回结果都会显示在app组件的<router-view>中。
app.vue:

<template>
  <div>
    <div class="row">
      <Banner />
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <router-link class="list-group-item" active-class="active" to="/about">About</router-link>
          <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <!-- Vue中借助router-view指定组件的显示位置-->
            <router-view></router-view>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Banner from './components/Banner.vue'
export default {
      
      
  name: "App",
  components: {
      
      
    Banner
  }
}
</script>
<style lang="css" scoped>
</style>

router/index.js

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'

// 引入组件,作为key-value的value值
import AboutVue from '@/pages/About.vue'
import HomeVue from '@/pages/Home.vue'
import NewsVue from '@/pages/News.vue'
import MessageVue from '@/pages/Message.vue'

// 创建并暴露一个路由器,管理路由
export default new VueRouter({
    
    
    // 路由
    routes: [
        {
    
    
            path: '/about',
            component: AboutVue,
        },
        {
    
    
            // 一级路由
            path: '/home',
            component: HomeVue,
        },
        {
    
    
            path: '/news',
            component:NewsVue
        },
        {
    
    
            path: '/message',
            component:MessageVue
        },
    ]
})

演示结果:
在这里插入图片描述

该案例就表示了虽然<router-link>写在了home组件中,但是结果没有显示在home组件中。

问题二:一个页面可以有多个<router-view/>

可以,只是内容会重复显示而已,有几个<router-view/>就显示几份结果。
eg:所有有<router-view/>的页面都写了两份

在这里插入图片描述

路由传递 query参数

<router-link to="/home/message/detail?id=666&title=你好呀"></router-link>像这种格式的参数就是query参数。

传递方法

字符串写法

:to="``"
<!-- query参数,to的字符串写法 -->
 <router-link :to="`/home/message/detail?id=${message.id}&title=${message.title}`">{
   
   {message.title}}
 </router-link>

对象写法

:to="{}"
  <router-link :to="{
         path: '/home/message/detail',
         query: {
             id: message.id,
             title:message.title
         }
     }">
         {
   
   {message.title}}
     </router-link>

接收方法

我们知道设置路由之后组件实例身上就会多一个$route参数,query参数就携带在里面。
组件中接受方法是:$route.query.id$route.query.title

        <li>消息编号:{
   
   {$route.query.id}}</li>
        <li>消息标题:{
   
   { $route.query.title}}</li>

eg:效果:(涉及到三级路由)
在这里插入图片描述
代码:
message.vue:

<template>
    <div>
        <ul>
            <li v-for="message in messageList" :key="message.id">
                <!-- query参数,to的字符串写法 -->
                <!-- <router-link :to="`/home/message/detail?id=${message.id}&title=${message.title}`">{
    
    {message.title}}
                </router-link> -->
                &nbsp;&nbsp;
                <!-- query参数,to的对象写法 -->
                <router-link :to="{
                    path: '/home/message/detail',
                    query: {
                        id: message.id,
                        title:message.title
                    }
                }">
                    {
   
   {message.title}}
                </router-link>
            </li>
        </ul>
        <hr />
        <router-view></router-view>

    </div>
</template>

<script>
export default {
      
      
    name: "MessageVue",
    data() {
      
      
        return {
      
      
            messageList:[
                {
      
       id: '001', title: '消息001' },
                {
      
       id: '002', title: '消息002' },
                {
      
       id: '003', title: '消息003' },
            ]
        }
    }
}
</script>

<style scoped>
</style>

detail.vue:

<template>
    <ul>
        <li>消息编号:{
   
   {$route.query.id}}</li>
        <li>消息标题:{
   
   { $route.query.title}}</li>
    </ul>
</template>

<script>
export default {
      
      
    name:'DetailVue'
}
</script>

<style>

</style>

router/index.js

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'

// 引入组件,作为key-value的value值
import AboutVue from '@/pages/About.vue'
import HomeVue from '@/pages/Home.vue'
import NewsVue from '@/pages/News.vue'
import MessageVue from '@/pages/Message.vue'
import DetailVue from '@/pages/Detail.vue'

// 创建并暴露一个路由器,管理路由
export default new VueRouter({
    
    
    // 路由
    routes: [
        {
    
    
            path: '/about',
            component: AboutVue,
        },
        {
    
    
            // 一级路由
            path: '/home',
            component: HomeVue,
            children: [
                {
    
    
                    // 二级路由,子路由的路径前面无需写/,vue的路由已经帮我们配置好了
                    path: 'news',
                    component:NewsVue
                },
                {
    
    
                    path: 'message',
                    component: MessageVue,
                    children: [
                        {
    
    
                            path: 'detail',
                            component: DetailVue,
                        },
                    ]
                },
            ]
        },
    ]
})

命名路由

直接在路由的配置项中添加name属性即可。
eg:
router/index.js

// 该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'

// 引入组件,作为key-value的value值
import AboutVue from '@/pages/About.vue'
import HomeVue from '@/pages/Home.vue'
import NewsVue from '@/pages/News.vue'
import MessageVue from '@/pages/Message.vue'
import DetailVue from '@/pages/Detail.vue'

// 创建并暴露一个路由器,管理路由
export default new VueRouter({
    
    
    // 路由
    routes: [
        {
    
    
            name:'about',
            path: '/about',
            component: AboutVue,
        },
        {
    
    
            // 一级路由
            name:'home',
            path: '/home',
            component: HomeVue,
            children: [
                {
    
    
                    // 二级路由,子路由的路径前面无需写/,vue的路由已经帮我们配置好了
                    name:'news',
                    path: 'news',
                    component:NewsVue
                },
                {
    
    
                    name:'message',
                    path: 'message',
                    component: MessageVue,
                    children: [
                        {
    
    
                            name:'detail',
                            path: 'detail',
                            component: DetailVue,
                        },
                    ]
                },
            ]
        },
    ]
})

命名路由的作用

简化跳转,给路由命名之后可以直接通过路由名称进行页面跳转:
简写前:

 <router-link :to="{
    path: '/home/message/detail',
     query: {
         id: message.id,
         title:message.title
     }
 }">

可以简写成

  <router-link :to="{
      name: 'detail',
      query: {
          id: message.id,
          title:message.title
      }
  }">

如果使用路由名称简化路径能且只能使用 :to="{name: 'detail'}”这种格式。

路由传递 params 参数

to="`/home/message/detail/666/你好呀`"

666和你好呀是参数这种传递参数的方法为params 参数

使用路由传递 params 参数:

router-link传递参数

字符串写法

格式:

<router-link :to="`/home/message/detail/${message.id}/${message.title}`">{
   
   {message.title}}
</router-link>

对象写法

格式:

 <router-link :to="{
     name: 'detail',
     params: {
         id: message.id,
         title:message.title
     }
 }">
     {
   
   {message.title}}
 </router-link>

注意:如果使用对象写法传递params参数必须使用name传递路径,不能使用path了

router/index.js对参数进行接收

使用占位符接收传过来的参数,并存储在$route的params上

  children: [
      {
    
    
          name:'detail',
          path: 'detail/:id/:title',
          component: DetailVue,
      },
  ]

接收方法

我们知道设置路由之后组件实例身上就会多一个$route参数,params参数就携带在里面。
组件中接受方法是:$route.params.id$route.params.title

        <li>消息编号:{
   
   {$route.params.id}}</li>
        <li>消息标题:{
   
   { $route.params.title}}</li>

路由的参数props配置

之前通过$route.params.id$route.query.id的形式在组件中接收参数,为了简化组件中参数的接收,vue为我们提供的路由的props配置
路由中可以对传递给组件的参数进行配置。

props的第一种写法(对象写法):

  • 传递参数:值为对象,该对象中的所有key-value都会以props的形式传给该路由所指向的组件:props:{a:1,b:'hello'}
  • 组件接收参数:props:['a','b']
    eg:参数传递:
children: [
          {
    
    
                name:'detail',
                path: 'detail/:id/:title',
                component: DetailVue,
                // props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传给 DetailVue的组件。
                props: {
    
     a: 1, b: 'hello' }
            },
           
        ]

DetialVue组件接收:

 props:['a','b']

问题:传递的参数只能是固定的死数据

props的第二种写法(布尔值写法——params参数)

  • props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给该路由所指向的组件:props:true
    注意只能是params参数

  • 组件接收参数:props:['参数名','参数名']

eg:
发送参数

children: [
  {
    
    
      name: 'detail',
      path: 'detail/:id/:title',//params参数
      component: DetailVue,
      // props的第二种写法,值为布尔值
      props: true
  }
]

DetialVue组件接收:

 props:['id','title']

props的第三种写法(函数写法)

任意参数都可用
(以query参数为例)
eg:

  • 路由中传递参数
 children: [
       {
    
    
             name: 'detail',
             path: 'detail/:id/:title',
             component: DetailVue,
			 // 第三种写法函数写法,将传递的参数以对象的形式返回
			 props($route) {
    
    
			     return {
    
    
			         id: $route.query.id,
			         title:$route.query.title
			     }
			 }
	  }
	]

可以使用解构赋值简写为:

props({
     
     query:{
     
     id,title}}) {
    
    
     return {
    
    id,title}
 }
  • 组件中接收参数
props:['id','title']

路由对浏览器历史纪录的影响

浏览器的历史记录的两种方式

  • 浏览器的历史记录有两种写入方式:分别为pushreplace, push是追加历史记录,replace是替换当前记录.
  • 记录浏览器历史记录的方式:栈的方式
    eg:当浏览器记录方式为push
    进行如下操作:
    在这里插入图片描述
    浏览器的历史记录方式为push,所以每进行一次路由,路由地址都会存储在栈中,所以浏览器的记录如下
    在这里插入图片描述
    当我们点击浏览器左上角的回退按钮的时候,就会从栈顶到栈底依次回退。

eg:当浏览器记录方式为replace
进行如下操作:
在这里插入图片描述
假设所有的路由都是replace的,即所以每进行一次路由,后一次的路由地址都会覆盖前一次的路由地址。
那么浏览器的历史记录内容为:
在这里插入图片描述
只有当前一个页面不可前进也不可回退

设置路由跳转的浏览器历史记录

  • 路由跳转的浏览器的历史记录方式默认为push
  • 开启replace模式:为router-link添加replace属性<router-link replace>News</router-link>
    完整写法
    <router-link :replace="true">News</router-link>

哪个路由需要,就开启哪个路由的replace模式即可,如果都需要开启就都添加replace属性。

编程式路由导航

引入

之前我们都是使用<router-link :to=""></router-link>实现路由跳转的。我们知道router-link最终会被解析成a标签,但是如果我们不希望这个按钮元素被解析成a标签而是被解析成button呢,如果我们希望不进行点击就可以进行路由跳转呢?
显然router-link就不能帮助我们解决需求,这时候可以使用编程式路由导航。

编程式路由导航——路由跳转(push、replace)

还记得我们配置完router之后,vm和组件实例对象身上就会出现两个属性:$route和$router, 编程式路由导航就需要用到$router.
$router的意思是路由器,它可以帮助我们实现路由的跳转:
$router的原型身上有push和replace方法可以帮助我们实现路由的跳转
在这里插入图片描述

  • push和replace对应着浏览器对路由的记录的不同方式, push是追加历史记录的路由,replace是替换当前记录的路由。
  • 使用方法:
    this.$router.push({})
    this.$router.replace({})
    他们的参数和<router-link :to="">中to的对象写法的参数一样。
    eg:
//编程式路由导航
//在<script>标签中编写
this.$router.push({
    
    
         name: 'detail',
         query: {
    
    
             id: message.id,
             title: message.title
         }           
    })

等价于

    <!-- 在<template>标签中编写 -->
<router-link :to="{
      name: 'detail',
      query: {
          id: message.id,
          title:message.title
      }
  }">
  </router-link>

编程式路由导航实现使用button标签进行路由

message.vue

<template>
    <div>
        <ul>
            <li v-for="message in messageList" :key="message.id">
                <!-- query参数,to的对象写法 -->
                <router-link :to="{
                    name: 'detail',
                    query: {
                        id: message.id,
                        title:message.title
                    }
                }">
                    {
   
   {message.title}}
                </router-link>
                <button @click="pushShow(message)">push查看</button>
                <button @click="replaceShow(message)">replace查看</button>
            </li>
        </ul>
        <hr />
        <router-view></router-view>

    </div>
</template>

<script>
export default {
      
      
    name: "MessageVue",
    data() {
      
      
        return {
      
      
            messageList:[
                {
      
       id: '001', title: '消息001' },
                {
      
       id: '002', title: '消息002' },
                {
      
       id: '003', title: '消息003' },
            ]
        }
    },
    methods: {
      
      
        pushShow(message) {
      
      
            this.$router.push({
      
      
                    name: 'detail',
                    query: {
      
      
                        id: message.id,
                        title: message.title
                    }           
               })
        },
        replaceShow(message) {
      
      
            this.$router.replace({
      
      
                name: 'detail',
                query: {
      
      
                    id: message.id,
                    title: message.title
                }
            })
        }
    }
}
</script>

<style scoped>
</style>

在这里插入图片描述

编程式路由导航——路由的前进和后退

即实现浏览器左上角的前进和后退的功能:
在这里插入图片描述
需要调用$router身上的back()和forward()方法

回退:this.$router.back()
前进:this.$router.forward()

编程式路由导航——go

this.$router.go(参数)
传入的参数是可以是正也可以是负数:
若为正数表示前进n步;若是负数表示后退n步。
eg: this.$router.go(3)表示前进3步。

缓存路由组件

正常情况下,路由组件别切走之后再切换回来,假如切走之前input框中有内容,切换来之后input框就没有内容了:
在这里插入图片描述
这是因为路由组件不显示的时候实际上是被销毁了,被销毁后input框中的内容自然也会消失。再次显示的时候路由组件再被创建,当然是一个全新的输入框。

那如果希望路由切走的时候保存数据不被销毁呢?

  • 只需要确保组件不被销毁即可。vue为我们提供了 <keep-alive></keep-alive>让不展示的路由组件保持挂载,不被销毁。
  • 使用方法:将 <keep-alive></keep-alive>标签包裹在你希望不被销毁的路由组件的展示区外面,上面的例子就是包裹在news组件展示位置<router-view/>的标签外面。
    news组件再Home组件中展示,即在home组件中使用<keep-alive></keep-alive>包裹<router-view/>
  • includes属性,<keep-alive>标签有includes属性可以指明对那些组件起作用。因为<router-view/>显示的可能是多种路由组件,希望哪个路由组件缓存就将该 组件的名字 写在includes中即可.

eg:

一个组件直接 include=“组件名”

<keep-alive include="NewsVue">
      <router-view>
      </router-view>
  </keep-alive>

如果有多个组件使用数组形式 :include="['组件名','组件名']"

<keep-alive :include="['NewsVue','MessageVue']">
      <router-view>
      </router-view>
  </keep-alive>

eg:
Home.vue

<template>
    <div>
        <h2>我是Home的内容</h2>
        <div>
            <ul class="nav nav-tabs">
                <li>
                    <router-link class="list-group-item" active-class="active" to="/home/news">News
                    </router-link>
                </li>
                <li>
                    <router-link class="list-group-item" active-class="active" to="/home/message">
                        Message</router-link>
                </li>
            </ul>
            <keep-alive include="NewsVue">
                <router-view>
                </router-view>
            </keep-alive>
        </div>
    </div>
</template>

<script>
export default {
      
      
    name: "HomeVue",
}
</script>

<style scoped>
</style>

效果:
在这里插入图片描述

vue的两个生命周期钩子——路由组件独有

路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
具体名字:

  • activated(){}路由组件被激活时触发。即切换到该路由组件界面时触发
  • deactivated(){}路由组件失活时触发。即离开到该路由组件界面时触发

应用案例

在这里插入图片描述

需求:我们不希望news路由组件别切走后组件消失,但是又希望路由组件切走时候定时器能停止,当再切换来的时候定时器再继续工作。
可以使用activated(){}deactivated(){}两个生命周期钩子实现。
news.vue

<template>
    <ul>
        <li :style="{opacity}">欢迎学习</li>
        <li>news001&nbsp;<input type="text" /></li>
        <li>news002&nbsp;<input type="text" /></li>
        <li>news003&nbsp;<input type="text" /></li>
    </ul>
</template>

<script>
export default {
      
      
    name: "NewsVue",
    data() {
      
      
        return {
      
      
            opacity:1
        }
    },
    // mounted() {
      
      
    //     this.timer = setInterval(()=> {
      
      
    //         this.opacity -= 0.01
    //         if (this.opacity <= 0)
    //             this.opacity = 1
    //     },16)
    // },
    // beforeDestroy() {
      
      
    //     clearInterval(this.timer)
    // },
    activated() {
      
      
        console.log("切换到news组件,news组件被激活了")
        this.timer = setInterval(()=> {
      
      
            this.opacity -= 0.01
            if (this.opacity <= 0)
                this.opacity = 1
        },16)
    },
    deactivated() {
      
      
        console.log("离开news组件,news组件失活了")
        clearInterval(this.timer)
    }
}
</script>

<style scoped>
</style>

路由守卫

全局路由守卫

全局前置路由守卫

全局前置路由守卫即,每次路由切换之前进行判断是否放行。

  • 函数:router.beforeEach((to, from, next) => {})
    to:到哪个路由路径去
    from:从哪个路由路径来
    next():进行放行
  • 调用时机:
    初始化调用,每次路由组件切换前调用

需求:当localstoage中的用户名为yang时才能访问news和message
即:
在这里插入图片描述

全局前置路由守卫设置:(在router/index.js中进行设置)

const router =  new VueRouter({
    
    
    // 路由
    routes: [......]
    })
// 全局前置路由守卫——初始化调用,每次路由组件切换前别调用
router.beforeEach((to, from, next) => {
    
    
    if (to.name === 'news' || to.path === '/home/message') {
    
    
        if (localStorage.getItem('name') === 'yang') {
    
    
            // 放行
            next()
        } else {
    
    
            alert('用户名不对无权限')
        }
    } else {
    
    
        // 放行
        next()
    }
    
})

export default router 

简写

在前置路由守卫中,我们通过to,from的name或path属性匹配路由路径,进而对该路由进行权限校验。
可以进行简写,在路由中添加一个配置,如果该配置属性为true就进行路由权限校验,如果为false就无需进行路由权限校验。
那么将该自己添加的配置放在哪呢?
在route的meta中添加自己设置的配置。
我们将meta中的信息称为路由元信息即由程序员自定义的信息。

eg:

 {
    
    
    name:'about',
    path: '/about',
    component: AboutVue,
    meta:{
    
    isAuth:false}
},

在需要进行路由鉴权的路由中设置 meta:{isAuth:true}
在不需要进行路由鉴权的路由中设置 meta:{isAuth:false}

路由守卫判断:

// 全局前置路由守卫——初始化调用,每次路由组件切换前别调用
router.beforeEach((to, from, next) => {
    
    
    if (to.meta.isAuth) {
    
    
        if (localStorage.getItem('name') === 'yang') {
    
    
            // 放行
            next()
        } else {
    
    
            alert('用户名不对无权限')
        }
    } else {
    
    
        // 放行
        next()
    }

全局后置路由守卫

全局后置路由守卫,每次路由切换完之后执行。

  • 函数:router.afterEach((to, from,) => {})
    to:到哪个路由路径去
    from:从哪个路由路径来
  • 调用时机:
    初始化调用,每次路由组件切换后进行调用

需求:路由跳转之后页面的title进行改变
即:
在这里插入图片描述

  • 为每个路由添加meta中的title属性。
 routes: [
        {
    
    
            name:'about',
            path: '/about',
            component: AboutVue,
            meta:{
    
    title:'关于'}
        },
        ......
        ]
  • 全局后置路由守卫设置
// 全局后置路由守卫——初始化调用,每次路由组件切换后调用
router.afterEach((to, from) => {
    
    
   console.log(to,from)
   document.title = to.meta.title||'路由系统'  
})

独享守卫

独享路由守卫没有后置路由守卫只有一个前置路由守卫

  • 只想对一个路由进行守卫的时候可以使用独享路由守卫
  • 直接写在路由的配置项中
  • 函数:beforeEnter:(to,from,next)=>{}

eg:只对newsVue组件进行限制,当localstorage中的name不为yang时不能访问newsVue

{
    
    
     // 二级路由,子路由的路径前面无需写/,vue的路由已经帮我们配置好了
     name:'news',
     path: 'news',
     component: NewsVue,
     meta: {
    
     isAuth: true, title: '新闻' },
     beforeEnter: (to,from,next) => {
    
    
         // 进入NewsVue之前进行校验
             if (to.meta.isAuth) {
    
    
                 if (localStorage.getItem('name') === 'yang') {
    
    
                     // 放行
                     next()
                 } else {
    
    
                     alert('用户名不对无权限')
                 }
             } else {
    
    
                 // 放行
                 next()
             }
     }
 },

效果:
在这里插入图片描述

组件内守卫

在组件的实例对象中编写组件内路由守卫

路由进入时守卫

路由进入时守卫:通过路由规则,进入该组件时被调用(通过路由规则即通过普通路由的页面标签进入的不算)

  • 函数:beforeRouteEnter(to,from,next) {}
    to:到哪个路由路径去
    from:从哪个路由路径来
    next:放行
  • 调用时机:
    进入该组件时被调用

eg:为about组件添加路由守卫,当localstorage中的name不为yang时不能访问aboutVue

aboutVue

<template>
  <h2>我是About的内容</h2>
</template>

<script>
export default {
      
      
  name: 'AboutVue',

  // 通过路由规则,进入该组件时被调用(通过路由规则即通过普通路由的页面标签进入的不算)
   beforeRouteEnter(to,from,next) {
      
      
    if (to.meta.isAuth) {
      
      
      if (localStorage.getItem('name') === 'yang') {
      
      
        // 放行
        next()
      } else {
      
      
        alert('用户名不对无权限')
      }
    } else {
      
      
      // 放行
      next()
    }
  },
}
</script>

记得设置:路由中跳转到aboutVue组建的meta中的isAuth为true

        {
    
    
            name:'about',
            path: '/about',
            component: AboutVue,
            meta:{
    
    isAuth: true,title:'关于'}
        },

效果:

在这里插入图片描述

路由离开时守卫

路由离开时守卫:通过路由规则,离开该组件时被调用

  • 函数:beforeRouteLeave(to,from,next) {}
    to:到哪个路由路径去
    from:从哪个路由路径来
    next:放行
  • 调用时机:
    离开该组件时被调用
 // 通过路由规则,离开该组件时被调用
  afterRouterLeave(to, from, next) {
    
    
    next()
  }

路由器的两种工作模式

路径中的hash值(注意这只是前端中的hash值)

我们编写代码,项目运行后,浏览器的url地址中总是包含#
在这里插入图片描述
我们将路径中从#开始一直到路径结束的部分称为路径中的hash值.

  • hash值的特点:不会随着http请求发给服务器
    即如果向后端发送:http://localhost:8080/#/home请求,后端收到的请求实际是:http://localhost:8080

路由器的hash和history工作模式

路由器有两种工作模式模式:

  • 一种是hash工作模式,即路径中有哈希值的路由。(路由器默认的工作模式是hash工作模式)
  • 另一种是history工作模式,即路径中没有哈希值的路由。

修改路由器的工作模式:
在路由器中使用mode配置进行修改,该配置项有两个可选值‘hash’‘history’

const router = new VueRouter({
    
    
    mode:'history',
    ......
}

项目的打包部署简介

项目上线前需要先进行打包,打包即将.vue文件转换成浏览器能够识别的.html.css.js文件

  • 打包:在终端运行npm run build
    打包完成之后生成一个dist文件,里面放的是打包后的内容,包括.html.css.js等静态资源
  • 部署:打包完成之后需要部署到服务器上项目才能运行。
    部署就是需要把打包后的代码放到服务器中,一般放到静态资源中(static文件夹)
    eg:模拟一个服务器:(使用node.js的exoress框架)
    在这里插入图片描述
    package.json
{
    
    
  "name": "yang",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    
    
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    
    
    "express": "^4.18.1"
  }
}

server.js

const express = require('express')

const app = express()
// 设置静态资源路径
app.use(express.static(__dirname+'/static'))

app.get('/person', (req, res) => {
    
    
    res.send({
    
    
        name: 'tom',
        age:18
    })
})

app.listen(5005, (err) => {
    
    
    if (!err) {
    
    
        console.log("服务器启动成功")
    }
})

将前端打包的内容放到static文件夹中:

之后运行服务器:node server
访问http://localhost:5005/(会自动寻找static中的index.html),
在这里插入图片描述
这样我们编写的前端路由就部署到服务上了。

两种工作模式的区别

  • 路径问题:hash工作模式的路径中有#;history工作模式的路径中没有#
  • 兼容新问题:hash工作模式的兼容性比history更好
  • 若以后将地址通过第三方手机app分享,若app校验严格,则hash工作模式的地址会被标记为不合法
  • 项目部署问题:
    使用hash工作模式的项目部署到服务器上刷新没有问题,可以正常运行:
    在这里插入图片描述

但是使用history的工作模式的项目部署到服务器上刷新就出现了问题:
在这里插入图片描述
这实际上是因为一新页面就会像后端请求路径,但是后端服务器没有配置前端的请求路径,所以一请求就会出错。
但是hash工作模式的hash值是不会传递给服务器的所以服务器,服务器收到的请求是 http://localhost:5005就不会请求出错。

但是如果必须使用hash工作模式刷新问题又应该怎么解决?
由后端进行处理:
后端人员可以一个个编写请求路径进行处理
也可以直接借助中间件:(这里以nodejs的解决方案为例)

  • 安装中间件:npm i connect-history-api-fallback
  • 引入:
    server.js
const history = require('connect-history-api-fallback');
app.use(history())

注意app.use(history)一定要在app.use(express.static(__dirname+'/static'))语句之前
配置完成之后就不会出错了

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/mantou_riji/article/details/125930286