前言
学完了一堆后端的知识之后呢,又去学了下git…
嘛,现在又回到了前端的学习
前置内容:
😍Vue全家桶学习笔记_零基础入门到入坑:Vue篇✨
这个学过后端的servlet的话就再清楚不过了。
后端的内容可以参考:
另外,文章末尾还有更多学习资料献给大家~~
文章目录
路由
路由就是通过网络,把网络信息从源地址传输到目标活动地址
(大概就是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(估计至少也得十二月去了)的内容
以下是操作系统的内容,希望对大家有帮助
本科操作系统学习笔记:从入门到精通🥰