Vue-router相关知识点
SPA(single page application):单一页面应用程序,只有一个完整的页面;它在加载页面时,不会加载整个页面,而是只更新某个指定的容器中内容。单页面应用(SPA)的核心之一是:更新视图而不重新请求页面;vue-router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式。
一、原理
1.hash模式
前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容。用hash来实现路由后面加#,就不会有页面刷新的问题,也不会让浏览器发出请求。
提供hashchange来监听hash 值的变化。
2.history模式
通过pushState 和 replaceState这两个 API 可以改变 url 地址且不会发送请求。同时还有popstate事件来监听值得变化。单页路由的url就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以需要把所有路由都重定向到根页面。
二、动态路由匹配
使用 :+id/name 可以传动态的值
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
模式 | 匹配路径 | $route.params |
---|---|---|
/user/:username | /user/evan | { username: ‘evan’ } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: ‘evan’, post_id: ‘123’ } |
1.响应路由参数的变化
当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用,但是组件的生命周期钩子不会再被调用。
如果想要解决这一问题,可以用watch来监听$route对象
const User = {
template: '...',
watch: {
$route(to, from) {
// 对路由变化作出响应...
}
}
}
或者使用导航守卫
const User = {
template: '...',
beforeRouteUpdate(to, from, next) {
//to即将进入的路由,from当前导航正要离开的路由,next resolve 钩子
}
}
2.捕获所有路由或404路由
*可以匹配任意路径
路由 { path: ‘*’ } 通常用于客户端 404 错误。
3.匹配优先级
有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:路由定义得越早,优先级就越高。
二、嵌套路由
就是在router里面配置router
const router = new VueRouter({
routes: [
{
path: '/user/:id',
component: User,
children: [
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
三、编程式的路由
1.定义路由
声明式 | 编程式 |
---|---|
< router-link :to="…" > | router.push(…) |
该方法的参数可以是一个字符串路径,或者一个描述地址的对象。
// 字符串
router.push('home')
// 对象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
path和params不能共存,所以得在path里面带参数,或者只用params
const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
2.router.replace()
不会向history添加新纪录,只会替换掉。
声明式 | 编程式 |
---|---|
< router-link :to="…" replace> | router.replace(…) |
3.router.go(n)
类似 window.history.go(n)
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)
// 后退一步记录,等同于 history.back()
router.go(-1)
四、命名路由
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
五、重定向和别名
1.重定向
从 /a 重定向到 /b:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
或者采用命名
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
或者方法
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}}
]
})
2.别名
别名可以让url的显示自己定义,但是路由匹配的是之前的名字。
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
重命名和别名的区别:重命名是名字被替换,以后要找到那个路由就得靠替换后的名字找;别名是展示的时候换个名字,但是要找路由还是得拿之前的名字找
六、路由组件传参
parmas或者在path里带参数
通过 $route.params取参数
props 可以设置该参数传递是否有效
1.布尔模式
如果 props 被设置为 true,route.params 将会被设置为组件属性
2.对象模式
如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。
const router = new VueRouter({
routes: [
{
path: '/promotion/from-newsletter',
component: Promotion,
props: { newsletterPopup: false }
}
]
})
3.函数模式
可以创建一个函数返回 props。这样可以将参数转换成另一种类型,将静态值与基于路由的值结合。
const router = new VueRouter({
routes: [
{
path: '/search',
component: SearchUser,
props: route => ({ query: route.query.q })
}
]
})
七、路由守卫
1.全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
每个守卫方法接收三个参数:
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
next(error): 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
确保 next 函数在任何给定的导航守卫中都被严格调用一次。可以多次但是要在逻辑不重叠的情况下
// GOOD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
2.全局解析守卫
router.beforeResolve ,和上面类似,它在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
3.全局后置钩子
router.afterEach((to, from) => {
// ...
})
它不接收next()
4.路由独享的守卫
路由配置上定义
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
5.组件内的守卫
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`
}
}
beforeRouteEnter 守卫 不能 访问 this,但是可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。
离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
beforeRouteLeave (to, from, next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
6.完整的导航解析流程
1.导航被触发。
2. 在失活的组件里调用 beforeRouteLeave 守卫。
3.调用全局的 beforeEach 守卫。
4.在重用的组件里调用 beforeRouteUpdate 守卫 。
5.在路由配置里调用 beforeEnter。
6.解析异步路由组件。
7.在被激活的组件里调用 beforeRouteEnter。
8.调用全局的 beforeResolve 守卫。
9.导航被确认。
10.调用全局的 afterEach 钩子。
11.触发 DOM 更新。
12.调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
八、路由元信息
通过meta字段配置的,可以用来做权限控制
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
九、数据获取
1.导航完成之后获取
在created 钩子调用方法,可以展示一个 loading 状态,再使用$route.params获取数据。
2.导航完成之前获取
beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。
十、路由懒加载
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')