vue route
路由钩子的执行流程, 钩子函数种类有:全局守卫、路由守卫、组件守卫
路由守卫参数
this.$route 和this.$router的区别,this.$router当前路由器,this.$route当前路由对象, $router是指整个路由实例,你可以操控整个路由,通过'$router.push'往其中添加任意的路由对象,routes:指router路由实例的routes API.用来配置多个route路由对象.
$route:是指当前路由实例('$router')跳转到的路由对象;- 路由实例可以包含多个路由对象.它们是父子包含关系
router.js中,path跳转路径,就是url上显示的,component跳转渲染后看到的组件,router-view页面因为路由跳转而需要渲染的视图区域,渲染的就是router中component对应的组件
路由模式
hash:把 app 当做视图渲染容器,当切换路由的时候触发视图容器的更新,这其实就是大多数前端框架哈希路由的实现原理。,即通过在链接后添加 # + 路由名字,根据匹配这个字段的变化,触发 hashchange 事件,动态的渲染出页面。就有点类似像 a 链接用作页面上的锚点一样,不会刷新页面。,但是兼容性棒棒,比如www.test.com/#/就是 Hash URL,当#后面的哈希值发生变化时,可以通过hashchange事件来监听到 URL 的变化,从而进行跳转页面,并且无论哈希值如何变化,服务端接收到的 URL 请求永远是www.test.com。Hash 模式相对来说更简单,并且兼容性也更好。每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用"后退"按钮,就可以回到上一个位置。,
history:去除‘#’符号,让url变好看,且还包括back、forward、go三个方法,对应浏览器的前进、后退、跳转操作,就是浏览器左上角的前进后退等按钮进行的操,美化后的hash模式,路径中会去掉#。依赖于html5的history,pushState API,所以要担心IE9及以下的版本,History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,下面会讲服务端配置。
history 模式要玩好,还需要后端配置支持。hash模式不需要配置,因为浏览器会忽略#和?后面的参数
因为我们的应用是个单页客户端应用,仅有一个入口文件就是index.html。
浏览器直接访问oursite.com/index.html可…
当时发生了下面两种情况,后端会因为url不匹配而报错:
客户ctrl + F5刷新页面,或者地址栏按enter键重新加载页面,恰好此时页面的url不是 oursite.com/index.html 客户不是通过oursite.com/index.html访… 因为对浏览器和后端来说,history模式的url就是正常的url,后端会根据匹配规则匹配相应的api接口或者静态资源。因此需要在后端添加一个匹配所有情况的配置,如果url匹配不到,就返回index.html页面。
abstract:非浏览器环境,会强制使用这个模式,例如weex
history模式服务端配置
我个人和公司都是用nginx,这里就讲nginx,配置这个的原因是当你进入某个路由之后,再次刷新页面时(或者是浏览器直接输入某个路由路径时) ,当刷新页面,浏览器就会重新dns解析,tcp协议,这个时候会根据浏览器的url去服务器找对应资源,当然我们vue-router是为单页面服务的,对应的url在服务端是肯定没有静态资源的,就会出现404,当配置了以下url重写语句,注意是重写,不是重定向,不改变url的情况重写浏览器内容,重写到index.html,因为这个index.html使我们项目的入口,index.html里面会读取当时打包好的app.js,就可以读取到路由配置,以实现我们浏览器的url对应的路由页面。
hash模式不需要配置,因为浏览器会忽略#和?后面的参数
区别:
1. hash路由在地址栏URL上有#,而history路由没有会好看一点
2. 我们进行回车刷新操作,hash路由会加载到地址栏对应的页面,而history路由一般就404报错了(刷新是网络请求,没有后端准备时会报错)。
3. hash路由支持低版本的浏览器,而history路由是HTML5新增的API。
4. hash的特点在于它虽然出现在了URL中,但是不包括在http请求中,所以对于后端是没有一点影响的,所以改变hash不会重新加载页面,所以这也是单页面应用的必备。
5. history运用了浏览器的历史记录栈,之前有back,forward,go方法,之后在HTML5中新增了pushState()和replaceState()方法(需要特定浏览器的支持),它们提供了对历史记录进行修改的功能,不过在进行修改时,虽然改变了当前的URL,但是浏览器不会马上向后端发送请求。
路由属性对象
为了下面理解的方便这里简单介绍下常用的路由对象属性,在组件内可以通过this.$route(不是$router!)进行访问.
$route.path
类型: string 字符串,对应当前路由的路径,总是解析为绝对路径,如 "/foo/bar"。
$route.params
类型: Object 一个 key/value 对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
$route.query
类型: Object 一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user = 1,如果没有查询参数,则是个空对象。
$route .name
当前路由的名称,如果有的话。这里建议最好给每个路由对象命名,方便以后编程式导航.不过记住name必须唯一!
$route.hash
类型: string 当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串。
$route.fullPath
类型: string 完成解析后的 URL,包含查询参数和 hash 的完整路径。
$route.matched
类型: Array<RouteRecord> 一个数组,包含当前路由的所有嵌套路径片段的路由记录 。路由记录就是 routes 配置数组中的对象副本 (还有在 children 数组)。 $route.redirectedFrom 如果存在重定向,即为重定向来源的路由的名字。
route的params 和query的区别
const routes = [
{ name: 'users', path: '/users/:id', component: Users },
{ path: '/books', component: Books }
]
// params使用场景
router.push('/users/123') // 跳转时
router.push({ // 另一种方式跳转
name: 'users',
params: { id: 123 }
})
// 获取id route.params.id // 123
// 实际URL地址 => localhost:8080/users
// query使用场景
router.push('books?q=123') // 跳转时
router.push({ // 另一种方式跳转
path: '/books',
query: { q: 123 }
})
// 获取query route.query.q // 123
// 实际URL地址 => localhost:8080/books?q=123
// params参数都不会显示在url地址栏中**.除了在路由中通过routes进行配置的.所以用户**刷新页面后,params参数就会丢失!**
**query参数可以正常显示在url地址栏中**.刷新页面后也不会丢失
注意,path和params不能一起用
声明式导航
1.定义: 声明式导航用router-link配合to属性实现点击切换路由
2.router-link提供了声明式导航高亮的功能(自带类名)
3.声明式导航的基本使用
在app.vue的template模板里面通过router-link配合to属性跳转到相应的页面
语法格式: xxx
声明式导航传参
第一种
1.传值在app.vue里传
// 传值的语法格式: to='/path路由路径/?参数名=值'
<router-link to='/find/?name=鸣人'>发现音乐</router-link>
2.接收值要在你跳转的find.vue页面拿
// 接收值的语法格式: {{$route.query.参数名}}
在find.vue组件里使用{{$route.query.name}}获取值
第二种
<router-link :to="{ name: 'news', params: { userId: 1111}}">click</router-link>
<router-link :to="{ path: '/news', query: { userId: 1111}}">click</router-link>
编程式导航
this.router.replace 跟push差不多,只是history中是直接取代,没法回退,push可以回退下一步
路由router.js中,children里面的路由path前面会加上副路由的路径,比如父路由 path: '/page',,子路由path:'foo',子路由的path其实是 '/page/foo',如果子路由path前面家里/会被当成根路径,跳转失败
回退
开发中遇到返回上一页的需求,有两个方法可选:
1.$router.back()
2.$router.go()
在使用中发现,使用router.go(-1)作用相同,都是返回原页面。但如果原页面路由携带参数,使用以上两个方法返回的原页面路由参数消失,此时使用$router.back(-1)返回原页面路由参数仍存在。
多视图
配合router-view使用
<h1>Named Views</h1>
<p>
<router-link to="/avenger">复仇者联盟</router-link>
</p>
<router-view ></router-view>
<router-view name="ironMan"></router-view>
<router-view name="captainAmerica"></router-view>
</div>
<!--这里我们给其中两个视图命名为ironMan和captainAmerica;没有设置name的视图,会获得默认命名为default>
const router = new VueRouter({
routes: [
{ path: '/avenger', name:'avenger',components: {
default: stanLee,
ironMan: ironMan,
captainAmerica: captainAmerica
}
//如果有多个视图需要展示时,以前的component换成components(加上s!!),写成对象形式.左边的ironMan指的就是<router-view>里设置的name="ironMan";右边的则指的是下面的组件ironMan.
}
]
})
const stanLee = { template: '<div>斯坦李</div>' }
const ironMan = { template: '<div>钢铁侠</div>' }
const captainAmerica = { template: '<div>美国队长</div>' }
任何子路由都是在其父路由的组件中切换显示,不管是多少层的路由嵌套,都是这样的理解,所以父路由需要有以下两点,二者缺一不可有组件引用,组件中有router-view组件,router-view就是用来显示子路由的
重定向 redirect 可以写 :'/home' 也可以 :{name:'home} 还可以 :to=>{里面写判断}
别名 alias 相当于另一个名字,路由也可以通过props传参给组件,比如props:{name:'abc}
路由懒加载
webpack模式 component: resolve=>require(['../home',resolve])
es 模式 component: ()=>import('../home')
路由导航
全局前置守卫,router.beforeEach 全局解析守卫 roter.beforeResolve在在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后 全局后置钩子 router.afterEach 不接受next函数,也不会改变导航本身
路由独享守卫 ,跟beforeEach用法一样
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
组件内的守卫
最后,你可以在路由组件内直接定义以下路由导航守卫:
beforeRouteEnterbeforeRouteUpdate(2.2 新增)beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
元信息
在路由中存储自己定义的属性,比如
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
//数组some方法,如果meta.requiresAuth为ture,
则返回true.此时,说明进入该路由前需要判断用户是否已经登录
if (!auth.loggedIn()) {
//如果没登录,则跳转到登录页
next({
path: '/login',
query: { redirect: to.fullPath }
//官方例子的这个小细节很好,通过query将要跳转的路由路径保存下来,
待完成登录后,就可以直接获取该路径,直接跳转到登录前要去的路由
})
} else {
next()
}
} else {
next() // 确保一定要调用 next()
}
})
next()
解释1
beforeRouteLeave (to, from, next) {
console.log('离开路路由')
if(to.fullPath==='/home'){
next();
}else{
next('/home')
}
这个是组件路由,我想实现的效果是在这个页面点击浏览器的返回按钮后要返回 /home页面而不是上一个页面,上面的代码是没问题的,而我之前的写法就一直死循环
// 下面的写法会死循环
beforeRouteLeave (to, from, next) {
console.log('离开路路由')
next('/home')
vue-router的next()方法无参和有参时是不一样的
当执行钩子函数时如果遇到next(’/home’)等时会中断当前导航, 比如当前导航是去/a,那么遇到next(’/home’)后就会把to.path改为/home,然后会重新触发这个离开的钩子, 注意:此时会重新触发执行这个钩子,而不是在这个钩子函数继续执行的
当重新触发后就会继续执行next(’/home’)所以会一直循环 。至于解决办法就是判断下,如果已经是/home了就next()。 解释2 为什么next()指定路径会出现死循环
router.beforeEach((to, from, next) => {
console.log('beforeEach');
if(true){
next('/');
}else{
next();
}
});
next()直接跳转到to.path路径,没有再执行一遍beforeEach导航钩子,next(’/’)或者next(’/login’)自己指定路径的,路由跳转的时候还执行一遍beforeEach导航钩子,所以上面出现死循环; 栗子:如我们登录页(’/login’)面进入首页(’/’),可以这么写:
router.beforeEach((to, from, next) => {
var userInfo= JSON. parse(sess ionStorage. getItem('userInfoStorage'));//获取浏览器缓存的用户信息
if(userInfo){//如果有就直接到首页咯
next() ;
}else{
if(to. path==' /login' ){//如果是登录页面路径,就直接next()
next() ;
}else{//不然就跳转到登录;
next(' /login');
}
}
});
问题 出现无限循环是因为之前我没有弄清楚next()流程 因为每次跳转到一个路由的时候都会 触发 全局守卫 由于判断条件未改变 所以 一直循环
解决方法
判断to路由的meta (isRequireAuthTrue)是否为true 判断是否登陆(isLogin)
// ('/')为登陆界面
// next() 默认跳转to的path
if(isRequireAuthTrue){
if(isLogin){
console.log('导航守卫保护');
next(); //成功则跳转
}else {
console.log('你还没有登陆!');
next({path:'/'}) //失败则跳转到登陆页面
}
}else {
next();//不需要导航守卫的则直接next跳转
}
解释3 问题描述:在调用Vue中的全局前置导航守卫beforeEach(to, from, next)中的next函数,在给next()传参数的时候出现死循环的问题!导致问题原因:其实导致这个问题的根本是没有完全理解beforeEach()和next("/somewhere")的作用首先,我们来看看next()的用法
究其根本是当执行了next("/somewhere")的时候又触发了beforeEach()这个钩子,所以就变成了无限死循环!
解决办法:
router.beforeEach((to, from, next) => {
let {path} = to;
if(path=== "/somewhere") {
next(); // 导航目的地符合预期,继续后续事情
}else {
next("/somewhere");
// 导航目的地不符合预期,重新路由新路径地址,然后会再次触发beforeEach钩子并进行二次判断
}
});
解释4 页面跳墙中使用 vue-router中的 beforeEach的死循环问题
问题展现
import Router from 'vue-router'
const router = new Router({
{path: '/', component: index },
{path: '/login', component: login},
{path: '/error', component: error},
{path: '*', component: error}
})
router.beforeEach((to, from, next) => {
const isLogin = sessionStorage.getItem('loginData')
if (isLogin) {
next()
} else {
next('/error')
}
})
最近在使用时,一直陷入死循环,当时的想法是如何将路由提取出来,脱离beforeEach的控制,之后发现不可行。上面问题再现,会出现死循环,因为 /error 会在进入前 又要进入beforeEach中 ,这样就会一直循环下去 所以就是想如何跳出这个循环即可
router.beforeEach((to, from, next) => {
const isLogin = sessionStorage.getItem('loginData')
if (isLogin) {
next()
} else {
//next('/error')
if (to.path === '/error') { //这就是跳出循环的关键
next()
} else {
next('/error')
}
}
})
这样写,其实这个会执行两次,第二次进来是以/error的路由进来的