Vue Router


前言

这是几个月前写的,当时放在CSDN上的,现在放到自己的博客上,广告气息很重就是了

学完了一堆后端的知识之后呢,又去学了下git…
嘛,现在又回到了前端的学习
前置内容:
😍Vue全家桶学习笔记_零基础入门到入坑:Vue篇✨

😘Git学习笔记:告别单机开发从此处开始😝

🤑ES6学习笔记:使前端锦上添花的必看内容🧐

这个学过后端的servlet的话就再清楚不过了。
后端的内容可以参考:

❤❤❤Java基础学习笔记:零基础快速入门❤❤❤

😃😃😃Mysql学习笔记:两小时学完mysql数据库😃😃😃

😜😏😳JDBC学习笔记:半小时学会Java操作数据库😜😏😳

🍠🥩🥙JavaWeb学习笔记:后端极速入门教学🥪🌯🥟

另外,文章末尾还有更多学习资料献给大家~~

路由

路由就是通过网络,把网络信息从源地址传输到目标活动地址

(大概就是URL和页面之间的映射吧)
路由本来是后端负责的,但是随着前端的不断发展,路由也进入了前端的范围。

后端路由

嘛,就是浏览器给后端发个请求,后端直接把整个页面(包括html css js等等)响应给浏览器
这个过程中,页面已经在后端形成,并且由后端决定URL和页面之间的映射关系。

前端路由

随着前后端分离的发展
后端变得只负责提供数据和资源
前端负责页面内部所有业务(那页面表面呢?html css很多直接丢给美术老师做了..)
一个网页的大部分内容都将由前端渲染,并且一个网站中会为了多个网页多次请求

前后端分离时工作流程如图所示
(图片来自网络)
在这里插入图片描述
只能说是前端渲染,但是路由依旧是后端路由!

这种技术条件下就有了:

单页面应用程序(SPA)

SPA是建立在前后端分离的基础上的
整个站点只有一个网页(只有一个index.html),请求时会一次性请求全部内容,然后根据前端路由的映射关系,对应的url展示对应的页面内容
这种情况下,改变url,不会发起服务端请求,页面也不进行整体刷新

工作流程如图所示
(图片来自网络)
在这里插入图片描述

前端路由的基本使用

两种模式

有两种模式:第一种是hash模式,第二种是history模式
hash模式
hash模式是通过监听浏览器的onhashchange()事件变化,查找对应的路由。由createWebHashHistory()创建
请添加图片描述
并且,不会刷新页面,而是在页面上直接加载
上图使用了location.hash修改,但是由于b站这个页面本身没有使用hash模式,所以页面没有发生变化

history模式
利用了HTML5 History Interface中新增的**pushState()ReplaceState()**方法
在这里插入图片描述
相当于是pushState()把url压栈,然后back()弹出

刷新一下可以尝试访问
在这里插入图片描述
进一步了解两种模式
兼容性
hash模式兼容性比较好,history其实也不错(除了IE)
是否会发起请求(是否影响后端)
hash模式不会,直接在前端解析了#后面的内容
history模式则会发起http请求,在路由嵌套的时候会导致404
(因为现在路由配置是在前端,http请求时发给后端,后端没有相应配置,所以就会404)

vue-router实例

打开cmd,输入vue ui,然后跳转到可视化界面
在这里插入图片描述
新建一个项目,记得插件要安装vue-router

这里我们用idea打开
可以在App.vue中找到这个
在这里插入图片描述
实际上呢,这个东西实际上就可以看作

<a v-link="/">Home</a>
<a v-link="/about">About</a>

(其实在vue较早的版本中,就是用的上述语法实现的路由跳转)

相应的页面会被挂载到图中的view-router标签上

我们运行一下,在页面中点击About,页面就会变化为
在这里插入图片描述
这其中的路由配置是在router文件下的index.js中配置的
routes是创建的路由对象
在这里插入图片描述
path就是对应的相对url
component就对应的组件
name就是这个路由的名字罢了

至于下面的这一行

component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')

其实也可以在上面import然后再在下面写名称,
不过直接合在一起更方便一些不是吗


点击Home之后我们可以看url变化,就是#后面加了个/
/是Home对应的url
#说明这是hash模式

在这里插入图片描述
可以在下面这里做更多配置(设置模式等等),
但是这里就只是确定路由关系为routes,其他配置后面再提

const router = new VueRouter({
  routes
})

然后导出

export default router

在main.js里面接收,一切都大工告成

import router from './router'

简单使用vue-router

大概看了长啥样,不写的话很快就会忘,这还不得写一下加深印象和理解?
第一步:写一个router-link
这个放到app.vue就好

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>|
      <router-link to="/mine">来康康我写的东西</router-link>
    </div>
    <router-view/>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

实际上就是多加了一行:

<router-link to="/mine">来康康我写的东西</router-link>

第二步:配置路由
它对应的url是/mine,所以我们要去router里面的index.js做相应的配置
增加这一段

{
  path: '/mine',
  name: 'Mine',
  component: () => import(/* webpackChunkName: "about" */ '../views/Mine.vue')
}

第三步:配置对应的组件
可以看到其对应的组件式import导入的,所以我们还得在对应的位置添加一个Mine.vue文件

随便写写就行

<template>
  <div>
    <h1>没错这就是我写的路由</h1>
    <img src="https://img0.baidu.com/it/u=2976614615,180926968&fm=11&fmt=auto&gp=0.jpg">
  </div>
</template>

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

<style scoped>

</style>

效果如下
在这里插入图片描述
在这里插入图片描述

进一步学习路由

重定向

就是跳转到URL1,然后URL1说:“别找我,你去找URL2”
比如这样

{
  path: '/',
  redirect: '/mine'
}

访问‘/’时,被转到了’/mine’
所以,我们每次访问/,都会跳到这个页面
在这里插入图片描述

修改模式

上面提到过的两种模式:hash模式和history模式
默认情况下是hash模式,标志是url会带有#(哈希字符)

之后呢我们开始使用Vue3.x的写法了!

//Vue2.x的写法!
// const router = new VueRouter({
//   routes
// })

//Vue3.x的写法!
const router = createRouter({
  //路由
  routes: routes,
  //模式(此处为hash模式;如果要改为history模式就写createWebHistory())
  history: createWebHashHistory(),
  //router-link在被选中时的css class
  linkActiveClass: 'zhe-shi-yi-ge-class'
})

导入对应的内容

//Vue2.x的写法
//import VueRouter from 'vue-router'
//Vue3.x的写法
import { createRouter, createWebHashHistory } from 'vue-router'

至于给它的class呢,就是在App.vue里面给一个对应的就行了

.zhe-shi-yi-ge-class{
  border: 1px solid black;
}

在这里插入图片描述

不可回退约束:replace

像下面这么写就行

<router-link to="/home" replace="replace">Home</router-link> |
<router-link to="/about" replace>About</router-link>|
<router-link to="/mine" replace>来康康我写的东西</router-link>

众所周知浏览器有前进和回退的功能:
在这里插入图片描述
实现原理是压栈和出栈
但是有些东西我们不想让它入栈,这时候就可以使用replace来约束它
被replace约束则不会入栈,并且不可参与路由嵌套

触发事件进行路由跳转

这里将就用一下about.vue文件

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <button @click="toMine">点击跳转</button>
  </div>
</template>

<script>

  import router from '../router'
  export default {
    name: 'About',
    setup() {
      const toMine = () => {
      	//没错,replace是跳转,并且会用replace约束
          router.replace('/mine')
      }
      return {
        toMine
      }
    }
  }

</script>

动态路由

比如我们打开csdn首页,很明显是一个网页,但是打开多次,每次显示的内容却不一样
说明这里的路由是个变量!这就是动态路由!
在这里插入图片描述
来写一个动态路由
第一步:写路由跳转

<router-link :to="'/news/' + newsId" replace>新闻</router-link>

其实就是v-bind绑定一下,所以script部分如下:

import { ref } from 'vue'
export default {
  setup() {
    const newsId = ref('lc666')
    return {
      newsId
    }
  }
}
</script>

第二步:写路由

{
  path: '/news/:id',
  name: 'News',
  component: () => import('../views/News.vue')
}

记住这里的id,它将作为后面的键值对的键
第三步:写组件

<template>
  <div id="news">
    <h1>大新闻</h1>
    <h5>编号:{{ $route.params }}</h5>
    <img src="https://img2.baidu.com/it/u=4244264354,1813450227&fm=253&fmt=auto&app=120&f=GIF?w=300&h=300">
    <div>产品经理被打啦!!!</div>
  </div>
</template>

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

<style scoped>
  #news{
    border: #ddd solid 1px;
  }
</style>

效果图
在这里插入图片描述
如图所示,url部分是我们动态绑定的lc666,然后编号部分是一个键值对
如果我们修改编号部分的代码如下,就可以访问到其值:

<h5>编号:{{ $route.params.id }}</h5>

在这里插入图片描述
当然,我们还可以用js获取id

import {useRoute} from "vue-router";

export default {
  name: "News",
  setup() {
    const route = useRoute()
    console.log(route.params.id)
  }
}

在这里插入图片描述


扩展
这里我们是本地写的内容
但是我们是动态绑定的url,所以我们能够从服务器请求到id、文章内容之类的话,就可以实现真正的动态加载内容了

懒加载

就咱们写的这个页面来说,比如我首屏是Home界面,这个时候我又看不到About等其他三个界面。
那要是我一开始就把所有资源请求下来了,那加载岂不是慢得一批?
为什么不优化一下?只有在访问某个界面的时候才请求资源——这就是,懒加载
实际上之前也看到过懒加载的方式了:

{
   path: '/news/:id',
   name: 'News',
   component: () => import('../views/News.vue')
 }

比如上述的component的写法就是懒加载,是利用了函数的性质:只有被使用的时候才会执行

懒加载的内容在打包的时候,会被被单独分为一个文件,而不是像原来一样所有内容都在一个文件里

路由嵌套

在这里插入图片描述
比如http:localhost:8080/Mine/msg
新建一个组件命名规范如图所示
然后写一个新组件

<template>
  <div id="mine_msg">咕咕咕</div>
</template>

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

<style scoped>
  #mine_msg{
    color: aqua;
    font-size: 32px;
  }
</style>

再到路由配置里面,找到Mine,增加一个children属性

{
   path: '/mine',
   name: 'Mine',
   component: () => import(/* webpackChunkName: "about" */ '../views/Mine.vue'),
   children: [
     {
       path: 'msg',
       name: 'MineMsg',
       component: () => import('../views/MineMsg.vue')
     }
   ]
 }

在到Mine里面写,大概写成这样就行

<template>
  <div>
    <h1>没错这就是我写的路由</h1>
    <img src="https://img0.baidu.com/it/u=2976614615,180926968&fm=11&fmt=auto&gp=0.jpg">
    <div class="content">
      <div class="content-nav">
        <router-link to="/mine/msg">我的消息</router-link>
      </div>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'Mine',
  setup() {}
}
</script>

<style scoped>
  .content{
    display: flex;
    justify-content: center;
    align-items: center;
    border: 1px solid #333;
  }
  .content-nav{
    background-color: gold;
  }
</style>

效果大概是这样
在这里插入图片描述
点一下
在这里插入图片描述
大概就是这样啦


参数传递

参数传递有两种方式,一种是parmas类型,另一种是query类型

类型 传递方式 路由配置格式 传递后形成的路径格式
parmas 在path后跟上传递的参数 /router/:params /router/xxx
query 对象种使用query的key作为传递方式 /router /router?id=xxx

query类型
就是经典的?username=xxx&pswd=xxx,比如
在这里插入图片描述

说白了也就是Cookie和Session嘛….
不过vue为我们提供了一手高效便捷的封装操作
再建一个
在这里插入图片描述
路由…

{
  path: '/circle',
  name: 'Circle',
  component: () => import('../views/Circle.vue')
}

组件里面随便写点,反正也不重要

<div>雀食蟀啊</div>

然后到App.vue里面加上这个

<router-link :to="{path:'/circle', query:{name:'大帅逼',id:'666666'}}">朋友圈</router-link>

点一下就来了
在这里插入图片描述
也可以拿到上面的数据

<template>
  <div>
    <h1>雀食蟀啊</h1>
    <div>{{$route.query}}</div>
    <div>{{$route.query.name}}</div>
    <div>{{$route.params}}</div>
  </div>
</template>

注意params拿的是vue组件里面传递的参数,query拿的才是URL里面的参数(Cookie)
在这里插入图片描述
parmas类型
About里面这样写

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <button @click="toMine">点击跳转</button>
    <button @click="toCircle">朋友圈</button>
  </div>
</template>

<script>

  import router from '../router'
  import { ref } from 'vue'
  export default {
    name: 'About',
    setup() {
      const circleId = ref('2333')
      const toMine = () => {
          router.replace('/mine')
      }
      const toCircle = () => {
          router.push('/circle/' + circleId.value)
      }
      return {
        toMine,
        toCircle
      }
    }
  }

</script>

在这里插入图片描述
在这里插入图片描述
仔细观察一下,“点击跳转”按钮用的是router.replace,而“朋友圈”按钮是用的router.push,那么二者的区别是什么呢?
replace是替换栈顶,push是压入一个新的
那么参数传递大概也就是这样了


路由的高阶使用

Router和Route对象

如果要简单地概括router和route的关系的话,那么就是:
router是全局的,route是具体的某一个组件
| 名称 | 区别 |
| —— | ———————————————————— |
| router | router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router实例对象 |
| route | route是一个可跳转的路由对象,是一个局部对象,可以通过它获取相应的name,path,params,query等 |

另外,router3.x里面都还用的JS,到了4.x就都用TS了(完了没学过根本看不懂)

路由守卫

大概就是你进入这个页面的时候,了解“你是谁”“你从哪里来”“你要到哪里去”
(大雾),并且决定是否“放行
全局路由前置守卫
说白了就是全局的前门守卫

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes: routes,
  linkActiveClass: 'zhe-shi-yi-ge-class'
})
//全局路由的前置守卫,三个参数:去、来、是否放行的函数
router.beforeEach((to, from, next)=>{
  console.log("贫僧来自")
  console.log(from)
  console.log("欲前往")
  console.log(to)
  //放行
  next()
})
export default router

在这里插入图片描述
记得一定要有next(),不然无法成功跳转
既然有前置的那也有后置的,把beforeEach改成afterEach就行了,但是还是前置的常用一些

有全局那应该还有局部的,这就是路由元信息
通过设置路由元信息可以把任意信息附加到路由上,比如过渡名称
谁可以访问这个路由等等,这些效果通过meta属性实现:

{
  path: '/circle',
  name: 'Circle',
  component: () => import('../views/Circle.vue'),
  meta: {title: '呜呼呼是朋友圈诶'}
}

然后我们就能在路由守卫中的对象参数to中在这里插入代码片的属性中找到这个title

router.beforeEach((to, from, next)=>{
  document.title = to.meta.title
  console.log("贫僧来自")
  console.log(from)
  console.log("欲前往")
  console.log(to)
  //放行
  next()
})

在这里插入图片描述
当然,其他没有设置meta.title的自然是undefined了

当然,守卫的主要作用之一其实是判断用户登录状态并做出响应的跳转!

其他守卫的形式
除了上述介绍的在路由中写守卫的方式,还有就是通过事件监听的方式来做

import {onBeforeRouteLeave, onBeforeRouteUpdate} from 'vue-router'

引入之后自己写一下事件监听就好了(参数依旧是那三个

keep-alive

组件A跳转到组件B时,创建组件B,销毁组件A。再由A返回B时,又创建A,销毁B…如果这样的过程多,那么开销就太大,不如让AB始终存在,就免去了创建销毁的成本

<!--  一般的写法-->
<!--  <router-view/> -->

<!-- vue2.x的写法 -->
<!--  <keep-alive>-->
<!--    <router-view></router-view>-->
<!--  </keep-alive>  -->

<!-- vue3的写法 -->
  <router-view v-slot="{ Component }">
      <keep-alive name="fade">
        <component :is="Component"></component>
      </keep-alive>
  </router-view>

用插槽的话能够动态匹配(虽然我也快忘完了)
那怎么体现它没有销毁重建呢,比如你可以加一个勾选框或者输入框之类的,输入一些东西然后跳转过去再回来,发现输入内容还在,这就说明还是原来那个没有销毁

附带的生命周期钩子
这两个钩子是专门服务于被keep-alive包裹的router-view
分别是:onActived和onDeactived,也就是当访问某个页面和离开某个页面时

常用的属性
| 属性 | 数据类型 | |
| ——- | —————————- | ———————————- |
| include | String,RegExp(正则),Array | 只有名称匹配的组件才会被渲染 |
| exclude | String, RegExp, Array | 任何名称匹配的组件都不会被渲染 |
| max | String | 最多可以渲染多少组件实例 |

比如:

  <router-view v-slot="{ Component }">
      <keep-alive name="fade" exclude="{'News', 'Home'}">
        <component :is="Component"></component>
      </keep-alive>
  </router-view>
</template>

这样一来,它就不会保存News和Home,News和Home每次还是会销毁和重新创建

后记

今天是2021.10.22
一晃就学了半年的前端了,半年收获还是挺多的…
唔,vue-router的大致内容就是这些了,由于本人才疏学浅,也难以将所有的知识全部概括,所以要完全掌握vue-router还需要各位自行学习了
这之后我还会继续更新 操作系统VueX(估计至少也得十二月去了)的内容
以下是操作系统的内容,希望对大家有帮助
本科操作系统学习笔记:从入门到精通🥰


文章作者: Serio
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Serio !
  目录