vue-router源码随笔
插件执行入口:
if (inBrowser && window.Vue) { window.Vue.use(VueRouter) // 传递给use的一个对象,这个对象必须实现install方法。 VueRouter.install = install
}
在Vue.use中代码:plugin.install.apply(plugin, args),在VueRouter上下文中执行install方法。args为Vue对象,不是new的对象实例哦,它也就是VueRouter.install方法中的参数。
插件给vue对象实例暴露了两个属性:
Object.defineProperty(Vue.prototype, '$router', { get () { return this._routerRoot._router } }) // this._routerRoot在beforeCreate中定义。 Object.defineProperty(Vue.prototype, '$route', { get () { return this._routerRoot._route } })
在new Vue的时候,先要new Router, 执行VueRouter的构造函数。实例很重要成员为matcher,它是一个对象,包括match和addRoutes两个方法,同时闭包了{pathList, pathMap, nameMap},它是把router对象转换为另外一种格式,很关键,看下三个属性的数据结构。
//pathList ["/foo","bar"] //pathMap "/bar": { beforeEnter: undefined, //在路由配置里调用 beforeEnter导航守护函数。 components: { //路径下对应的组件,可能有多个组件, default: {template: "<div>bar</div>"} }, // 组件实例,初始的时候没有,在beforeCreate钩子函数registerInstance(this, this)赋值的,它调用 // view组件中的data.registerRouteInstance,初始的时候不会调用,也就是instance为{} instances: {}, matchAs: undefined // alias用到 meta: undefined, name: undefined, parent: undefined, path: "/bar", // route.props == null? {}: route.components? route.props: { default: route.props } props: {}, redirect: undefined regex: /^\/bar(?:\/(?=$))?$/i } //nameMap nameMap:{ 'name':{ // name为path设置的名字 //path对应的pathMap的对象 } }
接下来执行this.history = new HashHistory(this, options.base, this.fallback),调用base.js的构造函数和HashHistory的构造函数。其中this为router实例:结构如下:
/*结构如下: {"app":null, "apps":[], "options":{
// 配置的路由信息 "routes":[ {"path":"/foo", "component": {"template":"<div>foo</div>"} }, { "path":"/bar", "component":{ "template":"<div>bar</div>"} } ] },
// 守护导航钩子函数 "beforeHooks":[], "resolveHooks":[], "afterHooks":[], "matcher":{}, "fallback":false, "mode":"hash" } */
执行base.js的构造函数之后添加了几个属性:
this.base = normalizeBase(base) // start with a route object that stands for "nowhere" this.current = START this.pending = null this.ready = false this.readyCbs = [] this.readyErrorCbs = [] this.errorCbs = [] this.listeners = []
START初始为createRoute(null, {path:'/'}),返回一个不可改变的对象,这个对象就是对路径的解析完毕之后保存的路径的各个部分。
router对象结构如下: { path:'\', param:'', query:'', hash:'', fullPath:'', name:'', raw:'', match:'' }
接下来执行checkFallback,初始时base为空字符,checkFallback它把当前的href转为为/#/,最后执行ensureSlash。getHash初始返回为'/',最终VueRouter.beforeCreate钩子函数执行完毕。
function getHash () { // We can't use window.location.hash here because it's not // consistent across browsers - Firefox will pre-decode it! // let href = window.location.href // 对第一个#之后的字符串解码。 let href = "http://www.baidu.com/#sh%20#s?sh#d%20sa" const index = href.indexOf('#') // empty path if (index < 0) return '' href = href.slice(index + 1) // decode the hash but not the search or hash // as search(query) is already decoded // https://github.com/vuejs/vue-router/issues/2708 const searchIndex = href.indexOf('?') /* 第一个#之后如果还有#或者还有?不需要解码 */ if (searchIndex < 0) { const hashIndex = href.indexOf('#') if (hashIndex > -1) { href = decodeURI(href.slice(0, hashIndex)) + href.slice(hashIndex) } else href = decodeURI(href) } else { href = decodeURI(href.slice(0, searchIndex)) + href.slice(searchIndex) } return href }