文章目录
路由简介
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> -->
<!-- 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']
路由对浏览器历史纪录的影响
浏览器的历史记录的两种方式
- 浏览器的历史记录有两种写入方式:分别为
push
和replace
, 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 <input type="text" /></li>
<li>news002 <input type="text" /></li>
<li>news003 <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'))
语句之前
配置完成之后就不会出错了