路由的介绍
路由简介:
1、定义:
(1)路由就是一组key-value的对应关系
(2)路由要经过路由器的管理
2、路由工作原理简介:
(1)目的:为了实现导航区和展示区内容的切换
(2)Vue中的router代表路由,可以实现页面不全部刷新进行切换,根据不同的route规则展示不同的组件。
vue-router
1、vue-router的理解:vue的一个插件库,专门用来实现SPA应用;
2、对SPA应用的理解
(1)单页Web应用(single page web application , SPA)
(2)整个应用只有一个完整的页面。
(3)点击页面中的导航链接不会刷新页面,只会做页面的局部更新;
(4)数据需要通过ajax请求获取。
3、什么是路由?
(1)一个路由就是一组映射关系(key-value)
(2)key为路径,value可能是function或component
4、路由分类:
(1)后端路由:
-
理解:value是function,用于处理客户端提交的请求;
-
工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
(2)前端路由:
-
理解:value是component,用于展示页面内容;
-
工作过程:当浏览器的路径发生改变时,对应的组件就会显示;
路由的基本使用
1、安装vue-router
注意:在vue2中要安装vue-router3,在vue3中要安装vue-router4
安装vue-router3的命令:npm i vue-router@3
2、应用插件:Vue.use(VueRouter)
3、编写router配置项:
先创建router文件,然后建立index.js文件,并且在index.js文件中编写路由规则,在其他文件中编写路由跳转
- index.js
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../components/About.vue'
import Home from '../components/Home.vue'
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
- main.js的修改
//引入路由器
import router from './router'
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)
4、路由标签
(1)实现路由切换:<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
(2)指定展示位置:<router-view></router-view>
5、区分路由组件与一般组件
(1)路由组件:通过或放入组件内容,不用导入;
(2)一般组件:通过components导入放入组件的内容。
注意:
(1)一般开发时最好将路由组件和一般组件区分开来,所以在src中新建一个pages文件存放路由组件,router文件存放路由组件的配置
(2)组件切换时,原来的组件被销毁了,而不是隐藏了
(3)每个组件都有自己的$route属性,里面存储着自己的路由信息
(4)整个应用只有一个router,可以通过组件的$router属性获取到
嵌套路由(多级路由)
1、配置路由规则,使用children配置项:
routes:[
{
path:'/about',
component:About,
},
{
path:'./home',
component:Home,
children:[ //通过children配置子级路由
{
path:'news', //此处一定不要写
component:News
},
{
path:'message', //此处一定不要写
component:Message
}
]
}
]
2、跳转:
<router-link to = "/home/news">News</router-link>
案例
1、需求:
(1)展示About和Home两个路由组件并且它们之间可以相互切换;
(2)在Home组件中嵌套News和Message两个路由组件,并且它们之间可以相互切换;
2、实现:
(1)main.js
//引入vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router'
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)
new Vue({
render: h => h(App),
router:router
}).$mount('#app')
(2)App.vue
<template>
<div>
<div class="row">
<Banner></Banner>
</div>
<div class="row">
<div class="col-xs-2 col-xs-offset-2">
<div class="list-group">
<!-- Vue中借助router-link标签实现路由的切换 -->
<!-- class-active 表示被激活时样式, -->
<!-- to表示要到达的路径 -->
<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">
<!-- 指定组件的呈现位置 -->
<router-view></router-view>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Banner from './components/Banner'
export default {
name:'App',
components:{
Banner
}
}
</script>
(3)router文件:(路由规则)
- index.js
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../page/About.vue'
import Home from '../page/Home.vue'
import Message from '../page/Message.vue'
import News from '../page/News.vue'
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[
{
path:'news', //子路由不加/
component:News
},
{
path:'message', //子路由不加/
component:Message
},
]
}
]
})
(4)page文件:(路由组件)
- About.vue
<template>
<h2>我是About的内容</h2>
</template>
<script>
export default {
name:'About'
}
</script>
- 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:'Home'
}
</script>
- Message.vue
<template>
<ul>
<li>
<a href="./message1">message0001</a>
</li>
<li>
<a href="./message2">message0002</a>
</li>
<li>
<a href="./message3">message0003</a>
</li>
</ul>
</template>
<script>
export default {
name:'Message'
}
</script>
- News.vue
<template>
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
</template>
<script>
export default {
name:'News'
}
</script>
3、效果:
路由的query参数
1、传递参数
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<router-link active-class="active" :to="`/home/message/detail?id=${p.id}&title=${p.title}`">{{p.title}}</router-link>
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:p.id,
title:p.title
}
}">
{{p.title}}
</router-link>
2、接收参数
$route.query.id
$route.query.title
案例
1、需求:在上一个案例的基础上
(1)点击Message时,呈现‘消息00x’;
(2)点击‘消息00x’时,呈现对应的信息;
2、实现:
- /router/index.js (修改路由规则)
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../page/About.vue'
import Home from '../page/Home.vue'
import Message from '../page/Message.vue'
import News from '../page/News.vue'
import Detail from '../page/Detail.vue'
//创建并暴露一个路由器
export default new VueRouter({
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[
{
path:'news', //子路由不加/
component:News
},
{
path:'message', //子路由不加/
component:Message,
children:[
{
path:'detail',
component:Detail,
}
]
},
]
}
]
})
- Message.vue
<template>
<ul>
<li v-for="p in messageList" :key="p.id" >
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <router-link active-class="active" :to="`/home/message/detail?id=${p.id}&title=${p.title}`">{{p.title}}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:p.id,
title:p.title
}
}">
{{p.title}}
</router-link>
</li>
<hr />
<router-view></router-view>
</ul>
</template>
<script>
export default {
name:'Message',
data() {
return {
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'}
]
}
}
}
</script>
- Detail.vue (新建一个Detail路由组件用于接收Message中的信息,并将信息呈现出来)
<template>
<ul>
<li>消息编号:{{this.$route.query.id}}</li>
<li>消息标题:{{this.$route.query.title}}</li>
</ul>
</template>
<script>
export default {
name:'Detail',
}
</script>
3、效果:
命名路由
1、作用:可以简化路由的跳转
2、如何使用
(1)给路由命名:
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello',//给路由命名
path:'welcome',
component:Hello
}
]
}
]
}
(2)简化跳转:
<!-- 简化前,需要写完整的路径 -->
<router-link to="/demo/test/welcome" >跳转</router-link>
<!-- 简化后,直接通过名字进行跳转 -->
<router-link :to="{name:'hello'}" >跳转</router-link>
<!-- 简化写法配和传递参数 -->
<router-link
:to="{
name:'hello'
query:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
路由的params参数
下图中是$router中的props:
1、配置路由,声明接收params参数
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:News,
},
{
component:Message,
children:[
{
name:'xiangqing',
path:'detail/:id/:title', //使用占位符声明接收params参数
component:Detail,
}
]
}
]
}
2、传递参数
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置。
3、接收参数
$route.params.id
$route.params.title
路由props配置
1、作用:让路由组件更方便的收到参数
2、使用方法:
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
// 第一种写法:props的值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
// 第二种写法:props的值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
// 第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
router-link的replace属性
1、浏览器的两种模式: (1)模式一:push模式
每次点击都会产生新的页面。历史记录是一个栈结构,有一个指针(指针在栈顶),指向当前页面,如下图所示:
(2)模式二:replace模式
不保存历史记录,替换当前的栈顶记录
2、开启replace模式
在router-link标签中添加replace属性:replace="true"。
<router-link :replace="true" class="list-group-item" active-class="active" :to="{name:'guanyu'}">About</router-link>
编程式路由导航
1、作用:不借助<router-link>实现路由跳转,让路由跳转更加灵活。因为<router-link>最后会转化为<a>标签,但如果我们想要编写<button>标签或者实现开机过三秒跳转页面的效果就不能通过<router-link>来实现,但可以借助编程式路由导航来实现。
2、具体编码:
// router的两个API
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx,
}
})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
案例:
1、需求:
(1)不借助<router-link>通过点击实现push查看内容(即将访问记录压入栈),通过点击实现replace查看内容(即将访问记录替换栈顶元素)。
(2)添加前进后退按键(点击前进键返回之前的页面,点击后退键回到下一个页面)
2、实现:
(1)在Banner.vue组件中实现前进后退键
<template>
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Vue Router Demo</h2></div>
<button @click="forward" >前进</button>
<button @click="back" >后退</button>
</div>
</template>
<script>
export default {
name:'Banner',
methods:{
forward(){
this.$router.forward()
},
back(){
this.$router.back()
}
}
}
</script>
(2)在Message.vue的列表中添加自定义组件,通过在方法中调用$router事件来实现
<template>
<ul>
<li v-for="p in messageList" :key="p.id" >
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <router-link active-class="active" :to="`/home/message/detail?id=${p.id}&title=${p.title}`">{{p.title}}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:p.id,
title:p.title
}
}">
{{p.title}}
</router-link>
<button @click="pushShow(p)">push查看</button>
<button @click="replacehShow(p)">replace查看</button>
</li>
<hr />
<router-view></router-view>
</ul>
</template>
<script>
export default {
name:'Message',
data() {
return {
messageList:[
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'}
]
}
},
methods:{
pushShow(m){
this.$router.push({
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
})
},
replacehShow(m){
this.$router.replace({
name:'xiangqing',
query:{
id:m.id,
title:m.title
}
})
}
}
}
</script>
3、效果:
缓存路由组件
1、作用:让不展示的路由组件保持挂载状态,不被销毁(如果不使用缓存路由技术,不展示的路由就会被销毁,问题演示如下动画)
2、使用方法:
<!-- 注意,这里的include包含的使组件名!!! -->
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
3、效果:
两个新的生命周期钩子
作用:路由组件所独有的两个钩子,用于不获路由组件的激活状态。
actived
激活时触发
deactived
失活时触发
案例
1、需求:
当切换到News界面时激活News组件,可以让文字透明度产生渐变;
2、实现:
<template>
<ul>
<li :style="{opacity}" >欢迎学习Vue</li>
<li>news001</li><textarea/>
<li>news002</li><textarea/>
<li>news003</li><textarea/>
</ul>
</template>
<script>
export default {
name:'News',
data() {
return {
opacity:1
}
},
//引入两个路由独有的生命周期钩子
// 激活,显示该路由的时候被激活
activated() {
console.log('News组件激活了')
this.timer = setInterval(() => {
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
})
},
// 失活,隐藏(销毁)该路由时失活
deactivated() {
console.log('News组件失活了')
clearInterval(this.timer)
}
}
</script>
3、效果:
路由守卫
作用:保护路由的安全。
全局前置路由守卫
1、作用:每次初始化时被调用,路由切换之前被调用
(重点:路由切换前,路由切换前,路由切换前!!!)
2、使用方法:
(1)在被限制的路由组件中的meta中配置isAuth属性
{
name:'xinwen',
path:'news', //子路由不加/
component:News,
meta:{isAuth:true}, //在meta配置中设置isAuth属性来作为判断是否要拦截路由的依据,也是开启路由守卫的前提
},
(2)在index.js配置全局前置路由守卫:
// 全局前置路由守卫 --- 初始化的时候被调用,每次路由切换之前被调用
router.beforeEach((to,from,next) => {
// to 表示要去哪里
// from 表示从哪里来
// next 表示放行
if(to.meta.isAuth){
if(localStorage.name == 'bug_killer'){
next()
}
else{
alert('名称不对,无权限查看!')
}
}
else{
next()
}
})
案例
1、需求:配置全局路由守卫,当localStore中的name是‘bug_killer’才能访问News和Message。
2、实现:
/router/index.js
//该文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
//引入组件
import About from '../page/About.vue'
import Home from '../page/Home.vue'
import Message from '../page/Message.vue'
import News from '../page/News.vue'
import Detail from '../page/Detail.vue'
//创建并暴露一个路由器
const router = new VueRouter({
routes:[
{
name:'guanyu',
path:'/about',
component:About
},
{
name:'zhuye',
path:'/home',
component:Home,
children:[
{
name:'xinwen',
path:'news', //子路由不加/
component:News,
meta:{isAuth:true}, //在meta配置中设置isAuth属性来作为判断是否要拦截路由的依据,也是开启路由守卫的前提
},
{
name:'xiaoxi',
path:'message', //子路由不加/
component:Message,
meta:{isAuth:true}, //在meta配置中设置isAuth属性来作为判断是否要拦截路由的依据,也是开启路由守卫的前提
children:[
{
name:'xiangqing',
path:'detail',
component:Detail,
}
]
},
]
}
]
})
// 全局前置路由守卫 --- 初始化的时候被调用,每次路由切换之前被调用
router.beforeEach((to,from,next) => {
// to 表示要去哪里
// from 表示从哪里来
// next 表示放行
if(to.meta.isAuth){
if(localStorage.name == 'bug_killer'){
next()
}
else{
alert('名称不对,无权限查看!')
}
}
else{
next()
}
})
export default router
3、效果:
- 没有配置localStore时:
- 在localStore中添加name属性是‘bug_killer’时:
可以访问
后置路由守卫
1、作用:每次初始化时被调用,路由切换后被调用;
2、用法:(应用:切换页面时更改页面标题)
(1)给每个路由组件配置一个标题
//在meta中配置title属性
meta:{title:'关于'}
(2)配置全局后置路由
//全局后置路由守卫--每次初始化时被调用,路由切换之后被调用
router.afterEach((to,from)=>{
console.log('后置路由守卫',to,from)
document.title = to.meta.title || '硅谷系统'
})
独享路由守卫
1、作用:访问某个路由组件前被调用,初始化某个路由组件时被调用。(注意:独享路由守卫只有前置路由守卫,没有后置)
2、使用+案例:(对新闻组件进行限制,当localStore中存在(school,'atguigu')键值对时才能访问)
Note:除了将名字换为beforeEnter:其他内容与全局前置路由守卫都一样
{
name:'xinwen',
path:'news', //子路由不加/
component:News,
meta:{isAuth:true,title:'新闻'},
beforeEnter:(to,from,next)=>{
console.log('独享前置路由守卫')
if(to.meta.isAuth){
if(localStorage.getItem('school') === 'atguigu'){
next()
}
else{
alert('学校名称不对,无权查看')
}
}
else{
next()
}
}
},
组件内路由守卫
1、作用:分为进入守卫、离开守卫两个;
进入守卫:通过路由规则时,进入该组件时被调用; 离开守卫:通过路由规则时,离开该组件时被调用;
2、使用方法:
//通过路由规则,进入该组件时被调用
//这里说的路由规则要注意,因为我也可以将它作为一个普通的组件进行导入,此时不遵守路由规则
beforeRouteEnter(to,from,next) {
console.log('About--beforeRouteEnter',to,from)
if(to.meta.isAuth){
if(localStorage.getItem('name') == 'bushizhiwu'){
next()
}else{
alert('学校名称不对,无权限查看')
}
}else{
next()
}
},
//通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next) {
console.log('About--beforeRouteLeave',to,from)
next()
}
history模式与hash模式
1、对于一个url来说,什么是hash值?
#及其后面的内容就是hash值,如下图所示:
2、hash值不会包含在HTTP请求中,即:hash值不会带给服务器。
3、两种模式:
(1)hash模式:
- 地址中永远带着#号,不美观
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
- 兼容性较好
(2)history模式:
- 地址干净、美观
- 兼容性和hash模式相比略差
- 应用部署上线时,解决刷新页面服务器404的问题
4、项目上线:用vue写好项目后,需要生成纯js,html,css构成的文件,用于部署在其他服务器上完成上线。所用命令:npm run build,执行后会生成一个dist文件可以放在我们部署的服务器上。