是什么
vue-router是Vue的官方插件库,用来管理路由
- Vue的单页面应用是基于路由和组件的,路由用于设定访问路径,和组件对应起来。
路由模式
vue-router 提供了三种运行模式:
● hash: 使用 URL hash 值来作路由。默认模式。
● history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。
● abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。
hash
vue-router 默认模式是 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会去重新加载。
hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分(/#/..),浏览器只会加载相应位置的内容,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中不包括#;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。
history
HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;
由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入”mode: ‘history’”,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
但是这种方式需要后端配置对应的URL,不然访问会出现404
abstract
为了兼容其他环境,如node环境的模式。会判断是否有window对应的api
原理
在Vue里使用vue-router只要引入vue-router组件。然后使用Vue.use()方法就会调用vue-router里的install方法
install方法
1 | import View from './components/view' |
- 这里使用了一个全局混入
Vue.mixin()把beforeCreate和destory钩子函数注册到每一个函数中,然后会调用init()方法 - 然后注册
$router和$route变量 - 然后注册
RouterView和RouterLink组件
VueRouter
接下我们本应该来看init方法,但init方法定义在 VueRouter 类里面,所以在调用init之前必先实例化VueRouter。先来看看 VueRouter
1 | export default class VueRouter { |
这里可以看见在初始化的时候this.matcher = createMatcher(options.routes || [], this)执行了createMatcher方法
createMatcher方法相关
1 | export type Matcher = { |
createMatcher(options.routes || [], this) 接收两个参数 一个为路由参数如下,另一个是new VueRouter()的实例。
1 | const routes = [ |
createMatcher首先执行的逻辑是const { pathList, pathMap, nameMap } = createRouteMap(routes)创建一个路由映射表,createRouteMap的定义在src/create-route-map中:createRouteMap函数的目标是把用户的路由配置转换成一张路由映射表,它包含 3 个部分,pathList 存储所有的 path,pathMap 表示一个 path 到 RouteRecord 的映射关系,而 nameMap 表示 name 到 RouteRecord 的映射关系。- 暴露出
match和addRoutes方法
addRoutes 方法
其的作用是动态添加路由配置,因为在实际开发中有些场景是不能提前把路由写死的,需要根据一些条件动态添加路由,所以 Vue-Router 也提供了这一接口:
1 | function addRoutes (routes) { |
addRoutes 的方法十分简单,再次调用 createRouteMap 即可,传入新的 routes 配置,由于 pathList、pathMap、nameMap 都是引用类型,执行 addRoutes 后会修改它们的值。
match 方法
1 | function match ( |
1 | function _createRoute ( |
1 | function redirect ( |
1 | function alias ( |
match 方法接收 3 个参数,其中 raw 是 RawLocation 类型,它可以是一个 url 字符串,也可以是一个 Location 对象;currentRoute 是 Route 类型,它表示当前的路径;redirectedFrom 和重定向相关,这里先忽略。
match 方法返回的是一个路径,它的作用是根据传入的 raw 和当前的路径 currentRoute 计算出一个新的路径并返回。
init方法
1 | init (app: any /* Vue component instance */) { |
1 | match (raw: RawLocation, current?: Route, redirectedFrom?: Location): Route { |
主要来看init方法: 根据history类型执行对应的方法。先定义了 setupHashListener 函数,接着执行了 history.transitionTo 方法
transitionTo()
transitionTo 定义在 src/history/base.js中 hashHistory和HTML5History继承base.js的transitionTo方法
1 | export class History { |
transtionTo方法:
- 首先会调用
route = this.router.match(location, this.current),也就是上面createMatcher类中的match方法匹配到目标路径 - 接下来会调用
confirmTransition做真正的切换,然后会执行函数内的回调函数
confirmTransition
1 | confirmTransition (route: Route, onComplete: Function, onAbort?: Function) { |
1 | function normalizeBase (base: ?string): string { |
1 | function resolveQueue ( |
1 | function extractGuards ( |
两种模式
在上面VueRouter实例的时候会判断是否支持HashHistory和HTML5History模式然后实例化。这里看一下这两种模式的源码
HashHistory
1 | export class HashHistory extends History { |
可以看到,当路由变化时,调用push/replace -> tanstionTo -> updateRoute -> this.cb方法,而this.cb方法是通过History.listen(cb)进行设置的,在init()中对其进行了设置:
监听地址栏
上面的VueRouter.push()和VueRouter.replace()是可以在vue组件的逻辑代码中直接调用的,除此之外在浏览器中,用户还可以直接在浏览器地址栏中输入改变路由,因此还需要监听浏览器地址栏中路由的变化 ,并具有与通过代码调用相同的响应行为,在HashHistory中这一功能通过setupListeners监听hashchange实现:
该方法设置监听了浏览器事件hashchange,调用的函数为replaceHash,即在浏览器地址栏中直接输入路由相当于代码调用了replace()方法。
HTML5History
1 | /* @flow */ |
1 | export function pushState (url?: string, replace?: boolean) { |
参考
https://juejin.im/post/6844903612930326541
https://juejin.im/post/6844903695365177352