目录
一、路由的push 与 replace切换
无痕模式 从push替换成replace模式
<router-link replace class="list-group-item" active-class="active" :to="{name:'guanyu'}">About</router-link>
<router-link replace class="list-group-item" active-class="active" to="/home">Home</router-link>
点击后 不能后退也不能前进,如果触发一次replace事件就会替换上一次的历史记录!导致上一条的历史记录消失
二、编程式路由导航
在Message组件种添加push按钮和replace按钮
<template>
<div>
<ul>
<li v-for="e in messgeList" :key="e.id">
<!-- 跳转路由并携带params参数 to的字符串写法 -->
<!-- <router-link :to="`/home/message/detail/${e.id}/${e.title}`">{{ e.title }}</router-link> -->
<!-- 跳转路由并携带params参数 to的对象写法 -->
<router-link :to="{
// path:'/home/message/detail',
// 如果使用params参数 那么就不允许使用path 必须使用name
name:'xiaoxi',
query: {
id: e.id,
title: e.title
}
}">
{{ e.title }}
</router-link>
<button @click="pushShow(e)">push查看</button>
<button @click="replaceShow(e)">replace查看</button>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Message',
data() {
return {
messgeList: [
{id:'001', title:'消息001'},
{id:'002', title:'消息002'},
{id:'003', title:'消息003'},
]
}
},
methods: {
pushShow(e) {
this.$router.push({
name:'xiaoxi',
query: {
id: e.id,
title: e.title
}
})
},
replaceShow(e) {
this.$router.replace({
name:'xiaoxi',
query: {
id: e.id,
title: e.title
}
})
}
}
}
</script>
在Banner组件种添加 前进和后退 以及 go按钮
go也就是给出参数来前进和后退多少步
<template>
<div>
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
<button @click="back">后退</button>
<button @click="forward">前进</button>
<button @click="test">测试一下go</button>
</div>
</div>
</template>
<script>
export default {
name:'Banner',
methods: {
back() {
this.$router.back()
},
forward() {
this.$router.forward()
},
test() {
// 数字是几 就前进几步 -2 就后退两步
this.$router.go(-2)
}
}
}
</script>
三、缓存路由组件
当我们在输入框内进行写入信息后,再次切换组件,会发现信息没有,原因就是每次切换组件的时候,组件都是重新进行销毁然后再次创建的,所以不会保留信息
不管news还是message组件都是在Home的展示区中, 所以最终都是Home的内容
我们只需要多一个标签进行包含即可!这里最好还是要写include来指定组件,不然所以组件都会被保存缓存,没有意义!指定组件名嗷!!!
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
同时缓存多个组件,不被销毁
<keep-alive :include="['News', 'Message']">
四、新增的两个生命周期钩子
当我们在News组件新增内容的时候
可以看到在组件切换的过程中,虽然数据被保存了,但是我们想要的效果是定时器能够停下来,并且数据还能够被保存,不让这样消耗太大
<template>
<div>
<ul>
<li :style="{opacity}">欢迎学习Vue</li>
<li>news001 <input type="text"></li>
<li>news002 <input type="text"></li>
<li>news003 <input type="text"></li>
</ul>
</div>
</template>
<script>
export default {
name: 'News',
data() {
return {
opacity:1
}
},
beforeUnmount() {
console.log('New组件即将被销毁')
clearInterval(this.timer)
},
mounted() {
this.timer = setInterval(() => {
console.log('@')
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
}, 16)
}
}
</script>
所以这里就有两个个新增的生命周期钩子,激活(用你就激活)和失活(切走就失活)
activated激活生命周期钩子 使用时就被激活
deactivated 失活生命周期钩子 二者就跟用mounted和beforeUnmount一样使用即可
<template>
<div>
<ul>
<li :style="{opacity}">欢迎学习Vue</li>
<li>news001 <input type="text"></li>
<li>news002 <input type="text"></li>
<li>news003 <input type="text"></li>
</ul>
</div>
</template>
<script>
export default {
name: 'News',
data() {
return {
opacity:1
}
},
// 激活
activated() {
console.log('News组件被激活了')
this.timer = setInterval(() => {
console.log('@')
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
}, 16)
},
// 失活
deactivated() {
console.log('News组件失活了')
clearInterval(this.timer)
}
}
</script>
本章节的素材已上传至Gitee:
五、路由守卫
要求只有当学校满足'wshha'规则的时候才能进行访问News组件 和 Message组件
暴露就需要再路由守卫判断后 才能进行暴露了
5.1 前置路由守卫
// 全局前置路由守卫
// 在每一次路由切换之前 就进行调用
// to 去哪
// from 从哪去
// next 放行!!!
router.beforeEach((to, from, next) => {})
用前置路由守卫来书写判断条件是否当前的路由路径允许放行!!!
// 全局前置路由守卫
// 在每一次路由切换之前 就进行调用
// to 去哪
// from 从哪去
// next 放行!!!
router.beforeEach((to, from, next) => {
console.log(to, from)
if (to.path === '/home/news' || to.path === '/home/message') {
// 进行判断是否放行!
if (localStorage.getItem('school') === 'wshha') next()
else alert('学校名字不对, 无权限查看!')
}
else next()
})
export default router // 暴露就需要再路由守卫判断后 才能进行暴露了
但是这还是有点不好,假如我们要对12个路由路径进行判断,那就要手写12个判断,有点过于麻烦!那么我们就需要在路由配置规则上配置自己的判断信息来观察是否可以添加该路由!
routes: [
// 一级路由
{
name: 'guanyu',
path: '/about',
component: About,
peiqi: false
},
]
能够看出来并没有我们配置的peiqi属性,也就是说router路由配置的选项都是别人给我们提供好的,但是这个meta这个对象里面就是专门给我提供自定义属性用的一个容器!!!
routes: [
// 一级路由
{
name: 'guanyu',
path: '/about',
component: About,
meta: { isAuth: false }
},
]
确实出现了我们自定义的属性 isAuth:false
然后配置到我们需要进行判断的News 路由 和 Message路由中,可以直接判断需要判断的路径是否要进行权限鉴定即可!这样就不需要再手写每一个路由路劲,就十分方便
children: [
{
name: 'xinwen',
path: 'news',
component: News,
meta: { isAuth: false }
},
{
name: 'xinxi',
path: 'message',
component: Message,
meta: { isAuth: false },
children: [
{
name: 'xiaoxi',
path: 'detail',
component: Detail,
// 解构赋值的连续写法
props({ query: { id, title } }) {
return { id: id, title: title }
}
}
]
},
]
router.beforeEach((to, from, next) => {
console.log(to, from)
// 判断是否需要鉴定权限
if (to.meta.isAuth) {
// 进行判断是否放行!
if (localStorage.getItem('school') === 'wshha') next()
else alert('学校名字不对, 无权限查看!')
}
else next()
})
5.2 后置路由守卫
// 后置路由守卫
router.afterEach((to, from, next) => {
console.log('后置路由守卫', to, from, next)
})
这里next显示时undefined 证明后置路由守卫是不存在next的
虽然后置路由守卫用的不多,但是在项目中确实也有一席之地,再点击后进行页签的标题切换!给每个路由都配置好自己的标题后:
const router = new VueRouter({
routes: [
// 一级路由
{
name: 'guanyu',
path: '/about',
component: About,
meta: { title: '关于' }
},
{
name: 'jia',
path: '/home',
component: Home,
meta: { title: '首页' },
// 二级路由 不用加 /
children: [
{
name: 'xinwen',
path: 'news',
component: News,
meta: { isAuth: false, title: '新闻' }
},
{
name: 'xinxi',
path: 'message',
component: Message,
meta: { isAuth: false, title: '信息' },
children: [
{
name: 'xiaoxi',
path: 'detail',
component: Detail,
meta: { title: '消息' },
// 解构赋值的连续写法
props({ query: { id, title } }) {
return { id: id, title: title }
}
}
]
},
]
},
]
})
如果是放在前置路由守卫中,就会存在一个bug!当我点击后其实新闻界面并没有弹出来,但是标题已经被前置守卫给渲染好了, 所以这里要采用后置路由守卫才是最好的!
// 后置路由守卫
router.afterEach((to, from, next) => {
console.log('后置路由守卫', to, from, next)
document.title = to.meta.title
})
这里的标题就没有改变!
5.3 独立路由守卫
只单独再News路由中设置独立路由守卫,进行判断,其他路由组件都是正常点击!
children: [
{
name: 'xinwen',
path: 'news',
component: News,
meta: { isAuth: true, title: '新闻' },
// 独立路由守卫
beforeEnter: (to, from, next) => {
console.log('前置路由守卫', to, from)
// 判断是否需要鉴定权限
if (to.meta.isAuth) {
// 进行判断是否放行!
if (localStorage.getItem('school') === 'wshha') next()
else alert('学校名字不对, 无权限查看!')
}
else next()
}
},
]
独享路由守卫没有独享后置路由守卫!!!
5.4 组件内 路由守卫
// 通过路由规则进行该组件时被调用
beforeRouteEnter(to, from, next) {
console.log('通过路由进入,已经进入了,放行后展示')
next()
},
// 通过路由规则进行该组件时被调用
beforeRouteLeave(to, from, next) {
console.log('通过路由进入,离开时展示,放行后才让离开当前组件')
next()
}
那么我们就可以通过这种办法,再组件内进行路由判断!
// 通过路由规则进行该组件时被调用
beforeRouteEnter(to, from, next) {
console.log('通过路由进入,已经进入了,放行后展示')
// 判断是否需要鉴定权限
if (to.meta.isAuth) {
// 进行判断是否放行!
if (localStorage.getItem('school') === 'wshha') next()
else alert('学校名字不对, 无权限查看!')
}
else next()
},
// 通过路由规则进行该组件时被调用
beforeRouteLeave(to, from, next) {
console.log('通过路由进入,离开时展示,放行后才让离开当前组件')
next()
}
}
六、路由器的两种工作模式
6.1 hash模式
hash是默认的路由工作状态,后面的路劲就全部都是hash值
6.2 history模式
当路由器使用history工作模式就没有# 会变的非常干净!
const router = new VueRouter({
mode: 'history',
routes: []
})
这里就是考虑到hash模式下兼容性比较好,history模式下兼容性只是略差,那么我们项目写完了要上线!就要进行文件打包,因为浏览器只认识.html .css .js
6.3 Vue组件打包
在package.json文件中可以看到我们之前一直都是用serve,那是用内置的创建一个8080的服务器,我们可以用build来进行打包生成最纯粹的.html .css .js
使用npm run build进行打包
会得到一个dist文件
但是该文件的html还不能直接打开,因为打包后的文件要部署,交给一个服务器
6.4 创建一个微型服务器
那么我们现在就手动搭建一个服务器:
- 创建一个demo文件夹后再vscode打开
- 终端输入:npm init 初始化 Node 项目
- 设置名字:hha_text-server
- 一直回车即可!
- 安装 Express: npm i express
- 然后创建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 进行启动服务器
再浏览器输入localhost:5005即可跳转到我们服务器请求的页面!
当前时history模式下,由于再点击的时候一直没有网络请求,但是一旦我们刷新,整个路径都会被当作请求交给服务器,就会报错!就是因为没有#来进行分割!
所以现在就是要重新调成hash模式再继续进行:
const router = new VueRouter({
mode: 'hash',
routes: []
})
然后重新打包后交给static静态资源的文件夹,重新启动服务器,再次访问5005就会发现路由器编程了hash模式多了一个#, 就会发现刷新后不会再出现任何问题!!!
就是因为#后面的这部分都不会被算做资源去找服务器要,不会被带过去请求服务器!
6.5 解决history路径请求问题
当然,由于history的路径好看,所以总有解决办法!
npm i connect-history-api-fallback
再server.js文件内进行引入
const express = require('express')
const history = require('connect-history-api-fallback');
const app = express()
app.use(history())
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('服务器启动成功!')
})
能够发现此时的页面不论怎么刷新都不会出现报错的问题了!!!
总结:
1.<router-link>
的replace属性
-
作用:控制路由跳转时操作浏览器历史记录的模式
-
浏览器的历史记录有两种写入方式:分别为
push
和replace
,push
是追加历史记录,replace
是替换当前记录。路由跳转时候默认为push
-
如何开启
replace
模式:<router-link replace .......>News</router-link>
2.编程式路由导航
-
作用:不借助
<router-link>
实现路由跳转,让路由跳转更加灵活 -
具体编码:
//$router的两个API this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退
3.缓存路由组件
-
作用:让不展示的路由组件保持挂载,不被销毁。
-
具体编码:
<keep-alive include="News"> <router-view></router-view> </keep-alive>
4.两个新的生命周期钩子
-
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
-
具体名字:
-
activated
路由组件被激活时触发。 -
deactivated
路由组件失活时触发。
-
5.路由守卫
-
作用:对路由进行权限控制
-
分类:全局守卫、独享守卫、组件内守卫
-
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() //放行 } }) //全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from)=>{ console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } })
-
独享守卫:
beforeEnter(to,from,next){ console.log('beforeEnter',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ next() }else{ alert('暂无权限查看') // next({name:'guanyu'}) } }else{ next() } }
-
组件内守卫:
//进入守卫:通过路由规则,进入该组件时被调用 beforeRouteEnter (to, from, next) { }, //离开守卫:通过路由规则,离开该组件时被调用 beforeRouteLeave (to, from, next) { }
6.路由器的两种工作模式
-
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
-
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
-
hash模式:
-
地址中永远带着#号,不美观 。
-
若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
-
兼容性较好。
-
-
history模式:
-
地址干净,美观 。
-
兼容性和hash模式相比略差。
-
应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
-