概述
vue-router是vue-cli中管理路由的模块
通过URL,实现URL和组件之间的一一对应,通过URL进行组件 切换
应用场景:
单页面应用 single page application(SPA) 移动端应用很广泛, 没有真实的页面跳转,只是针对不用URL在页面内容区域做了不同的渲染
只是加载一次JS、CSS文件,降低了页面切换时候的 HTTP请求
安装方式
1.一般在vue-cli初始化(vue init webpack 项目名称)时候,会有提示 是否安装vue-router,选择y回车,即可安装
2.后续手动安装,命令行到指定目录
输入:
npm install vue-router --save //安装vue-router并添加到依赖
使用
以将代码放置在入口文件main.js为例
import Vue from 'vue'
import App from './app'
//1.引入模块
import VueRouter from 'vue-router'
//2.将vue-router作为Vue的插件
Vue.use(VueRouter)
//3.创建vue-router实例
let router = new VueRouter({
...
})
//4.将路由配置信息router挂载到Vue实例
new Vue({
el: '#app',
router,
template: '<app />'
components:{
App //App为跟组件 App.vue
}
})
配置路由
let router = new VueRouter({ ... })
说明:为什么命名router
router是我们的路由实例,命名为变量router是为了方便简写,在Vue实例上有个键名叫router,两者相同则可以写一个键名即可
1.简单配置路由
new VueRouter({
mode:'history', //模式分为 hash,history两种,默认为hash
linkActiveClass:'xxx', //当前处在激活状态下的router-link标签上的状态class名
scrollBehavior(to,from,saveposition){ //滑动信息
...
},
routes:[ //路由调整信息
{
path:'/', //这个是路径
component:xxx, //这个是对应import过来的单文件组件,作为模板
alias:'/index', //访问另一个路径,扔可以访问,但是不会触发按钮的激活类名
name:'indexpage' ,
meta:{ //定义针对当前路由的自定义数据
needlogin:true
}
},{
path:'/about',
component:xxx
},{
path:'*', //通配符,标识没有配置路由的路径情况,一般作为404
redirect:{ //重新定向到一个已经存在的路由路径
path:'xxxx'
}
}
]
})
另外,配置路由,引入模板时候,在vue-cli中 @标识src文件夹,可以在webpack.base.conf.js 查看其声明
2.关于配置路由中的mode选择
new VueRouter({
mode:'hash', //如果选择hash模式,链接中需要带#
routes:[
...
]
})
建议:
一般低版本浏览器使用hash模式
高版本浏览器使用history模式
3.关于配置路由中的重定向redirect
redirect((to)=>{ //通过箭头函数,参数to,可以获取path路径,可以更加丰富我们重定向的逻辑
if(to.path=='xxx'){
return '/my'; //跳转到我的
}else{
return '/'; //跳转到首页
}
})
//redirect书写的方式
redirect:'/about'
redirect:{path:'/about'}
redirect:{name:'xxx'} //通过routes中的name,也可以定位
4.存在二级路径嵌套时候
默认显示第一项的需求
在二级嵌套中,如果希望父路由默认显示第一个子路由信息,且子路由为激活状态
则可以将父路由path:'',将子路由的第一项path设置为父路由原有path
...
{
path: '/about',
component: 'xxx',
children:[
{
path:'/about/xxx', //二级子菜单
component:'xxxx'
}
]
}
...
使用children嵌套数据,将子路由对象放进去
5.路由是我们配置浏览器跳转的基本配置,在模板页面中我们需要使用连接来触发
<router-link>我是个按钮</router-link>
router-link属性:
to="xxx" //相当于a标签的href,填写跳转链接
tag="li" //将<router-link>渲染为什么标签,以便浏览器去渲染
event="hover" //如果要添加一个触发事件,则用event来声明,默认click
active-class="xxx"
//如果标签对应的,路由路径为当前状态,则vue-router会默认为其添加激活的状态类名
//如果你想自定义自己的激活状态class名,则用这个
//另外,自定义状态类名有2个位置,一个在路由配置中,一个在这个触发按钮上,
//同时配置,按钮的优先级高,会剔除掉路由上的激活状态配置类名
exact
//如果不采用相对于根路径 /xxx 那么有可能存在,父路由,子路由同时处于激活class名状态下
//此时,添加exact 严格模式,以便只有一个路由显示激活class名
6.对应path有router-link,对应的component,应该渲染到这里<router-view>
<router-link to="xxxx" tag="li">
<a>按钮1</a> //如果router-link要嵌套a标签,则a标签不用再写href了
</router-link>
<router-link to="xxxx" tag="li">
<a>按钮2</a>
</router-link>
//多个按钮对应一个router-view
<router-view></router-view>
7.命名视图
在路由配置的routes中 path和component是一一对应的,
如果我们希望某一个path对应多个component时候
如:
//在路由配置文件中
path:"/about",
components:{ //注意 components 是复数形式
default: "模板1", //不写name的view-router为default
slider: "模板2"
}
//在单文件组件中
//当我们访问 /about 时候,下面2个router-view会同时显示
<router-view></router-view>
<router-view name="slider"></router-view>
//在router-view中可以添加class名,
<router-view class="father-box"></router-view> //router-view中的所有结构都有一个父级class名为father-box
//在单文件组件中
<template>
<div class="son-box">
<p>hello</p>
</div>
</template>
//渲染后
<div class="father-box son-box"> //类名会进行合并
<p>hello</p>
</div>
8.路由配置中的scrollBehavior
在页面onload或者浏览器前进,后退时候触发
scrollBehavior(to,from,saveposition ){
//to 目标路由
//from 当前路由
//saveposition 当前位置
//可以将当前 saveposition 作为返回值,达到页面跳转,返回后恢复滚动条位置的效果
//滚动条滑动顶部位置
return { x: 0, y: 0 };
}
8.动态路由
在路由配置中,所有的path都是以一个可预测的状态存在
如果我们希望添加一个格式,不清楚具体数据,需要使用动态路由
//在路由配置中
...
{
path:'/students/:userId?', //其可以匹配 /students,/students/1,/students/2
component:'xxx'
}
...
//在单文件组件页面
通过 this.$route.params.userId 可以获取其信息,使得后续逻辑上更加丰富
9.路由的自定义属性
...
{
path:'xxxx',
component:'xxxx',
meta:{
needlogin:true
}
}
...
//在单文件组件中,我们可以通过$route中获取
this.$route.meta.needlogin
- 动态路由的传参的新方式,配置
props: true
- 通常方式
//---路由部分
routes:[
{
path:'/categories/edit/:id?'
component:'xxx'
}
]
//---单文件组件中
created(){
console.log(this.$route.params.id);
}
- 新的传递params方式:
路由中props为true,代表将连接中任何参数,以props方式注入到新组件中去
//---路由部分
routes:[
{
path:'/categories/edit/:id?'
component:'xxx',
props: true
}
]
//---单文件组件中
props:[
'id'
]
router 与 $route
router是我们创建的VueRouter实例
$route是当前单文件组件的路由实例
router实例方法 实现 【编程式导航】
//直接跳转
router.push('/about')
router.push({ path: '/about' })
//动态路由,单文件页面通过 this.$route.params.userId 获取传递信息
router.push({ name: 'about', params: { userId: '123' }})
//查询字符串方式,用户访问有权限的页面,直接跳转到登录页,
//登录完成后,通过this.$route.query.redirect 获取用户之前想去的页面,然后通过push,再次跳转
//实现 未登录状态->登录->登录后自动跳转用户之前想去的 权限限制页面
router.push({ path: '/login', query: { redirect: '/user' }})
其他方法:
router.repalce( '/xxx' )
//将历史记录中的当前步骤替换掉,并且完成跳转
router.go(number)
// 填写具体数字,针对history的当前步数,向前是负值,向后是正值,但是只能在历史步数内,超过则不作操作
vue-router生命周期 钩子
全局钩子函数
所有路由进入,出去都会触发此钩子函数
- beforeEach
还没有进入路由内,所有不能获取vue实例
如果此时此刻想获取,可以通过router.app方式,得到vue实例
let router = new VueRouter({...})
router.beforeEach(function(to,from,next){
console.log(to); //目标导航
console.log(from); //离开导航
next(); //执行则路由跳转
//实际例子,根据路由情况,判断是否需要登录
//根据目标路径下的 自定义属性meta(如果你设置了),如
...
{
path:'/xxx'
component:'xxx',
meta:{ //meta提供开发者挂载自定义数据
needLogin:true //xxx页面需要登录
}
}
...
//如果跳转到xxx页面,则跳转到登录/login,否则路由正常跳转
if(to.meta.needLogin){
next('/login');
}else{
next();
}
})
- afterEach
rourer.afterEach(function(to,from){
//路由跳转后,我们可以做一些操作
//例如: 根据path或者meta添加新属性,去判断,改变文档标题
if(to.path=='/'){
window.document.title = 'xxx'
}else if{
...
}else{
...
}
});
局部钩子函数
beforeEnter 针对某一个路由,进行处理
...,
{
path:'xxx'
component:'xxx',
beforeEnter(to,from,next){
//只是针对xxx这个path,做操作
next(); //next必须执行,否则会卡住
}
},
...
单文件组件中的钩子函数
优先顺序从【0】开始
<script>
data(){
return{
msg:'hello'
}
},
created(){
console.log("创建完成:");
console.log(this.$el);
},
beforeCreate(){ 【2】
//组件执行的第一个钩子函数
},
beforeRouteEnter(to,from,next){【1】
//访问导航时候,执行这个路由执行的第一的函数
//这个时候组件还没有初始化,无法访问this
console.log(this) //undefined
next(function(vm){ //next中函数的参数,参数vm就是vue实例
//vm就是当前组件实例
console.log(vm.msg); //'hello'
})
},
beforeRouteUpdata(to,from,next){
//这个是路由嵌套情况下,点击子导航触发
console.log('beforeRouteUpdata');
next(); //不写这个钩子函数不会继续执行
console.log(this) //可以访问组件实例
},
beforeRouteLeave(to,from,next){
//离开主导航(主组件)时候触发
}
</script>
路由的细节问题
- 同一个路由地址,query参数不同,点击通过$router.push从当前路由地址,跳转到当前路由地址,参数变化
//传值
this.$router.push({path:"/menLink",query:{alert:"页面跳转成功"}})
//用query获取值
<p>提示:{{this.$route.query.alert}}</p>
页面不重新渲染的情况,只需要对 <router-view></router-view> 增加属性key
<router-view :key="$route.fullPath" ></router-view>
1-2. 当两个不同的path,对应相同的component的时候;
在两个路由之间切换,不会改变router-view视图更新
如:
{
path: '/create',
component:Categories
},{
path: '/edit/:id',
component:Categories
}
解决方法:
绑定key属性
<router-view :key="$route.path"></router-view>
1-3.当两个相同的path,对应相同的component的时候;
在两个路由之间切换,不会触发created的生命周期函数,不会对router-view进行视图更新
如:
{
path: '/article/:id',
component:Article
}
id=1 与 id=2 两个情况,切换时候碰到
解决方法:
需要针对id进行watch,之后强制触发created中的数据获取函数
如:
watch:{
id : function(){
//请求对应ID下的数据,进行视图渲染
this.getDataByIdFn();
}
},
created(){
//默认进入获取
this.getDataByIdFn();
}
- ios 和 safari下,相同path但query修改,跳转后,会出现页面不渲染的情况,因此使用window.location.reload刷新页面
this.$router
.push({ path: "/college", query: { DeptInfo: id } })
.then(() => {
window.location.reload();
});
生命周期相关
组件内路由
- beforeRouteEnter
beforeRouteEnter(to, from, next){
// 无法获取this,只能通过vm获取
next(vm => {
vm.routeFrom = from.name
vm.routeTo = to.name
})
}
- beforeRouteLeave
beforeRouteLeave(to, from, next){
this.routeFrom = from.name
this.routeTo = to.name
}