vue router小结

[TOC]

要点:

  • 路由的不同页面实质就是动态加载不同的组件
  • 每一个xx.vue就是一个组件
  • SPA(单页应用)单核心就是前端路由,单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。 类似于服务端路由,前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容。

本文使用vue-cli搭建的应用

分类

前端路由分为两种:

  • hash模式:利用url的hash,也就是锚点#,js需要通过hashChange事件来监听url的改变,ie7以下需要轮询
  • history模式:另一种就是利用html5的history模式,像后端路由一样用/分割,但是页面并没有跳转

hash形式路由

  • hash路由形式:http://www.xxx.com/#/login
  • url 的 hash 是以 # 开头,原本是用来作为锚点,从而定位到页面的特定区域。每次 hash 值的变化,还会触发hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange来实现更新页面部分内容的操作:
    1
    2
    3
    4
    5
    function matchAndUpdate () {
    // 匹配 hash 做 dom 更新操作
    }
    window.addEventListener('hashchange', matchAndUpdate)

history模式路由

  • HTML5 规范中提供了history.pushStatehistory.replaceState来进行路由控制。通过这两个方法,可以实现改变 url 且不向服务器发送请求。
  • 使用这种模式需要服务端的支持,因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。。
  • 使用history模式需要在vue router的router/index.js中设置mode: 'history',没写这个可能会不显示

vue-router使用

目录说明:在/src目录下有个/router/目录,路由设置一般在/router/目录下文件中设置,一般/src/router/index.js是路由入口(vue-cli)

1、在/src/router/index.js中引入 import Router from 'vue-router';
2、使用插件:Vue.use(VueRouter);
3、创建路由规则,在routes属性中创建路由规则
4、将其路由对象传递给Vue的实例,options中加入 router:router
5、在app.vue中留坑<router-view></router-view>路由切换时切换的是<router-view>挂载的组件,其他内容不会改变
这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import info from '@/components/info'
import test from '@/components/test'
import test2 from '@/components/test2'
import test3 from '@/components/test3'
Vue.use(Router)
export default new Router({
mode: 'history',//没写这个可能会不显示
routes: [
{
path: '/',
component: HelloWorld
},
{
path: '/info/:id',
name: 'infoName',
component: info,
children: [
{
path: 'test1',
name: 'test1Name',
component: test
},
{
path: 'test2',
component: test2
}
]
},
{
path: '/test3',
component: test3
}
]
})

routes的参数解析

  • path:规定路由。
    在配置文件里以冒号的形式设置参数可以用来传递参数
    1
    2
    3
    4
    5
    6
    {
    path:'/params/:newsId/:newsTitle',
    name: 'test',
    meta: 'hello',
    component:Params
    }

在目标页面中可以直接用$route.params.newsId来获取参数

  • name:命名路由,可以通过name值跳转到指定路由
    1
    <router-link :to="{ name: 'infoName', params: { userId: 123 }}">User</router-link>


1
router.push({ name: 'user', params: { userId: 123 }})

跳转到name是infoName到命名路由

  • meta:存一些其他信息

  • component:映射的组件


vue-router跳转

vue-router有两种跳转页面的方式:

  • 使用内置的<router-link>组件
  • 使用router实例方法,如$route.push()
1
<router-link to="/info">跳转到info页面</router-link>

router-link相当于a标签

router-link的一些属性

  • to指定要跳转的路径,也可以使用v-bind动态绑定,html5的history模式下会拦截点击
  • tag: 规定<router-link>渲染成什么标签
  • replace:不会留下history记录,不能使用后退键
  • active-class:路由匹配成功时会自动给当前元素设置一个名为router-link-active的class

router实例的方法进行跳转

router.push

  • $router.push(locationObj, onCompleteCallback, onAbortCallback):类似于window.location.href
  • $router.replace(locationObj, onCompleteCallback, onAbortCallback):不会向history添加历史记录,而是替换掉当前的history
  • $router.go(n):类似于window.history.go(),前进后退多少步

router.push(locationObj, onCompleteCallback, onAbortCallback):在 router.push 或 router.replace 中提供 onCompleteCallback 和 onAbortCallback 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。

Vue Router 的导航方法 (push、 replace、 go) 在各类路由模式 (history、 hash 和 abstract) 下表现一致

$router$route

$router是VueRouter实例,代表路由器
http://localhost:8080/info/3/?order=1234
router

$route是当前路由的对象
下面是/info路由的$router打印出来的结果,有属性fullPathpathnameparamsquery
http://localhost:8080/info/3/?order=1234
route


嵌套路由

  • 形式:/user/foo/profile/user/foo/posts
  • 要注意,以 / 开头的嵌套路径会被当作根路径。嵌套路由的path内路径不要以/开头
  • 将嵌套的路由放在children数组中,嵌套路由的页面显示在它的父级组件的 <router-view>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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
    }
    ]
    }
    ]
    })

命名视图

同时展示多个视图时用,比如侧导航和main内容区同时显示,这时可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

1
2
3
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>


重定向

可参考:vue-router重定向链接
“重定向”的意思是,当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b

别名

别名就是访问别名到url和访问原本的url产生的效果是一样的
/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样


路由组件传参

上面已经提到过路由通过url中/info/:id这种传参方法,在目标页面用$route.params.id获取参数,这样做法缺点是增加了代码耦合性
通过 props 可以实现解耦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true },
// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})

如果 props 被设置为 true,route.params 将会被设置为组件属性

设置404页面

当所有路由规则都没有匹配到时显示

1
2
3
4
5
6
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '*', component: NotFoundComponent }
]
})


导航守卫

导航:表示路由正在发生变化的这个过程
在路由发生变化的过程中设置了几个钩子,在钩子中可以干一些事,比如路由改变前改变页面的title
钩子:

  • beforeEach:全局前置钩子,路由即将改变前,设置页面标题可以在该钩子内完成

    • to:即将进入的目标的路由对象(目标的$route)
    • from:即将离开的路由对象
    • next:调用该方法后才能进入下一个钩子,一定要调用该方法来 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): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
        1
        2
        3
        4
        5
        const router = new VueRouter({ ... })
        router.beforeEach((to, from, next) => {
        // ...
        })
  • afterEach:它是一个全局后置钩子,全局后置不会接受 next 函数也不会改变导航本身:

    1
    2
    3
    router.afterEach((to, from) => {
    // ...
    })

完整的导航解析流程:
导航被触发。
在失活的组件里调用离开守卫。
调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
在路由配置里调用 beforeEnter。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。
调用全局的 beforeResolve 守卫 (2.5+)。
导航被确认。
调用全局的 afterEach 钩子。
触发 DOM 更新。
用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。


过度效果

<router-view> 是基本的动态组件,所以我们可以用 <transition> 组件给它添加一些过渡效果:

1
2
3
<transition>
<router-view></router-view>
</transition>

通过name属性还可以定义不同的动画效果


路由懒加载

1
() => import('./Foo.vue')

把组件按组分块:
有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name。

1
2
3
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')


比较好的文章:
vue-router 源码:前端路由
前端路由简介以及vue-router实现原理
vue-router详解
vue-router官方文档

热评文章