一、vue router安装
import VueRouter from 'vue-router'
Vue.use(VueRouter)
我们知道vue的use函数
Vue.use = function (plugin) {
......
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args);
} else if (typeof plugin === 'function') {
plugin.apply(null, args);
}
installedPlugins.push(plugin);
return this
};
看上面代码我们知道,需要到plugin中去找install函数,下面我们找到vue router的源码,找到install函数
function install (Vue) {
......
Vue.mixin({
beforeCreate: function beforeCreate () {
if (isDef(this.$options.router)) {
this._routerRoot = this;
this._router = this.$options.router;
this._router.init(this);
Vue.util.defineReactive(this, '_route', this._router.history.current);
} else {
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this;
}
registerInstance(this, this);
},
destroyed: function destroyed () {
registerInstance(this);
}
});
Object.defineProperty(Vue.prototype, '$router', {
get: function get () { return this._routerRoot._router }
});
Object.defineProperty(Vue.prototype, '$route', {
get: function get () { return this._routerRoot._route }
});
Vue.component('RouterView', View);
Vue.component('RouterLink', Link);
......
}
. 上面this._router.init(this); this指的是全局的Vue函数
上面我们看到当安装vue router时,会加载install函数,这个函数中写有Vue.mixin()的函数,里面有beforeCreate和destroyed,在安装的同时,
全局混入钩子函数 每个组件都会有这些钩子函数,执行就会走这里的逻辑,beforeCreate和destroyed就写入了vue的组件装载中,我们可以随时调用route查看相关路由信息
全局注册 router-view,router-link组件
Vue.component('RouterView', View);
Vue.component('RouterLink', Link);
这里是用来安装路由组件的
上面的安装函数中我们看到this._router.init(this)这个函数,实际上就是VueRouter.prototype.init的函数
VueRouter.prototype.init = function init (app /* Vue component instance */) {
......
this.apps.push(app);
// set up app destroyed handler
// https://github.com/vuejs/vue-router/issues/2639
app.$once('hook:destroyed', function () {
// clean out app from this.apps array once destroyed
var index = this$1.apps.indexOf(app);
if (index > -1) { this$1.apps.splice(index, 1); }
// ensure we still have a main app or null if no apps
// we do not release the router so it can be reused
if (this$1.app === app) { this$1.app = this$1.apps[0] || null; }
if (!this$1.app) { this$1.history.teardown(); }
});
// main app previously initialized
// return as we don't need to set up new history listener
if (this.app) {
return
}
this.app = app;
var history = this.history;
if (history instanceof HTML5History || history instanceof HashHistory) {
......
history.transitionTo(
history.getCurrentLocation(),
setupListeners,
setupListeners
);
}
......
};
注意:
1. app 实际上就是我们项目的vue的实例对象,一般单页面website这里只有一个app
2.由HTML5History实现路由的transitionTo函数转化
二. VueRouter的函数
var VueRouter = function VueRouter (options) {
......
this.app = null;
this.apps = [];
......
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base);
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback);
break
case 'abstract':
this.history = new AbstractHistory(this, options.base);
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, ("invalid mode: " + mode));
}
}
};
VueRouter的函数需理解:
1.app就是项目中的vue实例
2.路由的模式:
. history:依赖 HTML5 History API 和服务器配置。
. hash:使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器
. abstract:支持所有 JavaScript 运行环境,如 Node.js 服务器端。**如果发现没有浏览器的 API,路由会自动强制进入这个模式。
三、matcher
在VueRouter中我们可以看到createMatcher来处理routes
var VueRouter = function VueRouter (options) {
......
this.matcher = createMatcher(options.routes || [], this);
......
}
查看createMatcher函数
function createMatcher (
routes,
router
) {
var ref = createRouteMap(routes);
var pathList = ref.pathList;
var pathMap = ref.pathMap;
var nameMap = ref.nameMap;
function addRoutes (routes) {
createRouteMap(routes, pathList, pathMap, nameMap);
}
function match (
raw,
currentRoute,
redirectedFrom
) {}
}
我们可以看到createMatcher接收两个参数:
1、routes也就是页面中我们配置路径时的数组
2、router也就是我们的router的实例,new VueRouter()创建的实例
重要的三个函数是:
1. createRouteMap:根据路由的配置描述建立映射表,包括路径、名称到路由 record 的映射关系, 最重要的就是 createRouteMap 这个方法,这里也是动态路由匹配和嵌套路由的原理
2. addRoutes:动态添加路由配置
3. match:根据传入的 raw 和当前的路径 currentRoute 计算出一个新的路径并返回
createRouteMap---路由映射处理
function createRouteMap (
routes,
oldPathList,
oldPathMap,
oldNameMap
) {
......
// the path list is used to control path matching priority
var pathList = oldPathList || [];
// $flow-disable-line
var pathMap = oldPathMap || Object.create(null);
// $flow-disable-line
var nameMap = oldNameMap || Object.create(null);
routes.forEach(function (route) {
addRouteRecord(pathList, pathMap, nameMap, route);
});
return {
pathList: pathList,
pathMap: pathMap,
nameMap: nameMap
}
}
处理路由映射pathList,pathMap,nameMap
addRoutes--增加动态路由处理
这是在 vue2.2.0 之后新添加的 api ,或许很多情况路由并不是写死的,需要动态添加路由。我们只需要传入 routes 即可,他就能在原基础上修改
function createMatcher (
routes,
router
) {
......
function addRoutes (routes) {
createRouteMap(routes, pathList, pathMap, nameMap);
}
......
}
match
function match (
raw,
currentRoute,
redirectedFrom
) {
......
return _createRoute(null, location)
}
raw 是 RawLocation 类型,它可以是一个 url 字符串,也可以是一个 Location 对象;currentRoute 是 Route 类型,它表示当前的路径;redirectedFrom 和重定向相关。
match 方法返回的是一个路径,它的作用是根据传入的 raw 和当前的路径 currentRoute 计算出一个新的路径并返回
四、路由切换
History.prototype.transitionTo = function transitionTo (
location,
onComplete,
onAbort
) {
......
try {
route = this.router.match(location, this.current);
} catch (e) {
......
}
......
this.confirmTransition(
route,
function () {
// 更换路由然后更新updateRoute
this$1.updateRoute(route);
onComplete && onComplete(route);
this$1.ensureURL();
this$1.router.afterHooks.forEach(function (hook) {
hook && hook(route, prev);
});
},
......
);
};
接下来查看confirmTransition函数
History.prototype.confirmTransition = function confirmTransition (route, onComplete, onAbort) {
......
if (typeof to === 'object' && to.replace) {
this$1.replace(to);
} else {
this$1.push(to);
}
......
};
查看push
HTML5History.prototype.push = function push (location, onComplete, onAbort) {
....
this.transitionTo(location, function (route) {
pushState(cleanPath(this$1.base + route.fullPath));
......
}, onAbort);
};
查看pushState
function pushState (url, replace) {
......
try {
if (replace) {
......
} else {
history.pushState({ key: setStateKey(genStateKey()) }, '', url);
}
} catch (e) {
window.location[replace ? 'replace' : 'assign'](url);
}
}
可以看到最终使用的是history.pushState
路径切换方法 transitionTo,接着在回调函数中调用pushHash方法,这个方法调用的 pushState 方法底层是调用了浏览器原生 history 的方法。push 和 replace 的区别就在于一个将 url 推入了历史栈,一个没有,最直观的体现就是 replace 模式下浏览器点击后退不会回到上一个路由去 ,另一个则可以。