Vue是渐进式框架,有个全家桶。vue-router顾名思义就是负责路由设置。
vue-router
路由分为两种:
-
后端路由,请求路由地址不同,后端响应不同的网页数据。是网页本身全部改变。
-
前端路由,请求路由地址不同,前端展示不同的组件组合。网页只进行局部的改变。
基本使用
路由提供了router-link和router-view两个标签。 router-link 负责路由触发,<router-link to="/About">Home</router-link>等效于<a to="/#About">Home</a>。 router-view 负责展示对应路由下的组件。router-view和router-link配合实现前端路由功能。
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/About">About</router-link>
</div>
<router-view/>
</div>
</template>
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import About from ......
let routes = [ {
path: '/',
name: 'Home',
component: Home
},
{
path: '/About',
name: 'About',
component: About
}
]
let router = new VueRouter({
routes
})
export default router
let app = new Vue({
el: '#app',
router: router,
......
})
//或则
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
因为组件动态调用在router业务代码中,所以组件只需要在router处引入就行,根组件再引入router即可。
路由重定向
重定向与3xx状态码有关,不过这里前端路由重定向仍然只是操作前端组件。
- 最简单的写法
{
path: '/ab',
name: 'About',
redirect: '/about',//重定向到当前地址
},
{
path: '/About',//重定向过来
name: 'About',
component: About
}
这样<router-link to="/About">和<router-link to="/ab">都会指向路由path: '/About'。
几种路由模式
三种 hash、history、abstract模式。
从面试的角度我们从应用场景进行区分。
-
hash模式:兼容所有浏览器,特点是在url地址后添加/#/,http://www.juejinjin.com/#/home中#/home是hash值。hash值得变化不会使浏览器发送请求。 -
history模式:需要H5标准的支持,因为基层依靠History API实现。路由地址中没有#。 -
abstract(抽象)模式:兼容性最强,只需要JavaScript运行环境,如果没有浏览器运行环境就会自动选择该模式。
兼容性:abstract > hash > history
我们通过向mode属性赋值来选择不同的路由模式。
const router = new VueRouter({
routes,
mode:'history'
})
两种传参
三种传参:query格式参数、params格式传参。
首先我们可以打印我们的路由属性对象:
console.log(this.$route)//this指向的组件的路由对象
我们可以看到query和params属性以及path。。
query格式参数
query格式,get请求就是使用该方式携带数据。具体写法如下:
//使用?作为开头连接url地址,以&作为每个变量的分隔符。
url?xxx=yyy&xxk=yyk.....
我们可以直接在运行的vue项目中输入我们写好的路由后添加query格式后缀。或则router-link标签属性to中给路由地址添加后缀。
<router-link to="/new?name=1312">New</router-link>
- 主要还是获取,我们通过当前路由对象this.route进行获取,对应属性就是
query:
console.log(this.$route,this.$route.query)
params格式传参
params与node后台中的传参写法一致,需要根据路由的name值进行路由匹配:
this.$router.push({
name: "Abouts",
params: {
name: "word",
age: "11",
},
});
关于接收路由path写法:
{
path: '/Abouts/:name/:age',
name: 'Abouts',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
你会发现path: '/Abouts/:name/:age'和path: '/Abouts'效果都是一样,我们params的传参都会成功,通过this.$route.params。
原则上: params是路由的一部分,必须要在路由后面添加参数名。query是拼接在url后面的参数,没有也没关系。
- 如果路由后添加了params参数格式,而实际传参不匹配就会报错,或则警告提示:
[vue-router] missing param for named route "Abouts": Expected "name" to be defined
- 其次我们应该少用params传参,因为params要求使用name进行路由匹配,一方面路由可能没有写name属性,另一方面
name容易在开发中出现重名甚至嵌套的路由中出现重名bug后不容易发现。所以路由传参优先query方式。
方法动态导航
动态导航到新路由可以通过router对象上一系列方法:push replace go back forward。
我们前面params中使用了push方法,初了push,根路由上每个方法都有便捷的功能:
//我们使用push方法完成一个$router路由对象内的路由跳转。
this.$router.push({path:"/xxxx",query:{xxx}});
this.$router.push({name:"xxxx",query:{xxx},params:{xxx}});
this.$router.replace()
$route 和 $router
$route 这个属性是只读的,里面的属性是 immutable (不可变) 的,不过可以 watch (监测变化) 它。
console.log(this.$route)//this指向的组件的路由对象
console.log(this.$router)//全局路由的实例
两者差别体现于全局和局部路由,使用时注意容易用混导致bug。
路由守卫
守卫可以理解为生命周期与路由控制的结合。我们通常通过路由守卫完成登录状态检测等路由验证操作。
前置守卫
可以是理解是生命周期是因为,守卫函数执行在路由跳转离开的前后:
//src\router\index.js
router.beforeEach((to, from, next) => {
console.log(to,from);
next();
})
//默认时等效于下面。
router.beforeEach(() => {
next();
})
- 全局前置守卫 beforeEach
beforeEach在跳转到新路由之前触发。参数to合from分别是新的路由信息与原来的路由信息,函数内部必须要通过next完成跳转路由的操作,如果不写就会报错。
- 关于next()默认是跳转到to所指的新路由,可以手动设置重定向到其他路由。
确保要调用 next 方法,否则钩子就不会被 resolved
//关于登录状态的检测
router.beforeEach((to, from, next) => {
let isD = sessionStorage.getItem('isD')
console.log(to, from, isD);
if(isD=="true"){
if(to.path!='/user'){
next('/user')
}else{
next()
}
}else{
next()
}
})
我们发现next重定向路由后,会再次执行beforeEach函数,并将to路由信息修改成'/user'路由。由此我们容易触发死循环:比如from.path!='/user'从非user页面到其他页面就会触发死循环(递归地狱),不断执行beforeEach函数。
触发bug死循环后,最好检查路由的判断逻辑。
next补充
-
next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from路由对应的地址。 -
next(error): 如果传入next的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。
全局后置钩子
官方的定义是在路由跳转后执行。
router.afterEach((to, from) => {
console.log(to,from)
debugger;
})
通过上述代码,我们可以看到:
于是我们可以进一步总结:afterEach在完成路由跳转后执行,此时视图层还未更新,只有地址栏完成了变化。
与beforeEach的区别除了触发时间意外,还有:不会接受
next函数也不会改变导航本身。