前言
在了解路由之前先了解路由及路由器的概念。
路由(routing):就是通过互联的网络把信息从源地址传输到目的地址的活动;路由通常根据路由表——一个存储到各个目的地的最佳路径的表——来引导分组转送。做成硬件之后就是路由器。
hash 模式
hash模式代码: codesandbox.io/s/x3nxq950k…
点击<a href="#111"></a>,在URL里会添加以#开头的字段,可通过window.location.hash获取该字符段,hashchange事件可监听hash值改变。
优点:完全前端路由,只改变#后面值,无刷新。
缺点:不会出现在http请求中,SEO不友好。
history 模式
history模式代码: codesandbox.io/s/oqjvqm6w0…
-
通过
window.location.pathname获取/路径字段, -
通过
history.back()、history.go()、history.forward()完成后退前进等操作, -
在HTML5新增了
pushState、replaceState,通过window.history.pushState(stateObject,title,url)将路由添加到历史堆栈中去,replaceState()修改历史堆栈的记录,可通过history.state查看当前状态记录,但这只是修改了URL的路径,不会加载刷新页面,window.onpopstate对状态监听,但只会在浏览器的前进、后退按钮或者javascript的history.back()、history.go()、history.forward()方法中触发。
优点:在同源的情况下,实现了前端完全自由,stateObject可以是任何数据类型,URL可以是相对路径也可以是绝对路径。
缺点:如果后端没有做对应的路由,在刷新时会出现404。
memory 模式
memory模式代码: codesandbox.io/s/936269l69…
该模式就是将路由存在一个对象里,在URL里看不到对应的路径,适用于手机端,但使用很少。
Vue-Router
<router-link to="/xx"></router-link>相当于一个a标签,to相当于href属性。
<router-view></router-view>则是展示的内容。
VueRouter原理 源码
源码扫了一遍,看着确实为难,但了解了大概思路,
-
首先是封装了RouterView
只接受一个name属性
render函数return一个h(component, data, children),其实就是创建要渲染的组件,只不过中间对component组件、data对象、children子集做了一些处理。 -
再者是封装了RouterLink
包含to、tag、activeClass、event等属性
LinK的render最终return的是h(this.tag, data, this.$slots.default),相当于默认创建了一个a标签,只是对a标签做了如下事件监听
//执行的函数,调用了replace或者push方法
var handler = function (e) {
if (guardEvent(e)) {
if (this$1.replace) {
router.replace(location, noop);
} else {
router.push(location, noop);
}
}
};
//默认监听click事件
var on = { click: guardEvent };
//对传入事件判断并监听
if (Array.isArray(this.event)) {
this.event.forEach(function (e) {
on[e] = handler;
});
} else {
on[this.event] = handler;
}
- 封装VueRouter
var VueRouter = function VueRouter (options) {
if ( options === void 0 ) options = {};
this.app = null;
this.apps = [];
this.options = options;
this.beforeHooks = [];
this.resolveHooks = [];
this.afterHooks = [];
this.matcher = createMatcher(options.routes || [], this);
//模式默认为‘hash’模式,可通过mode定义
var mode = options.mode || 'hash';
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false;
if (this.fallback) {
mode = 'hash';
}
if (!inBrowser) {
mode = 'abstract';
}
this.mode = mode;
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.prototype上添加了go、back、forward等方法
function createMatcher (
routes, //创建的路由表
router //new VueRouter实例
) {
var ref = createRouteMap(routes);
var pathList = ref.pathList;
var pathMap = ref.pathMap;
var nameMap = ref.nameMap;
//创建addRoutes函数,动态添加路由
function addRoutes (routes) {
//*路由映射
createRouteMap(routes, pathList, pathMap, nameMap);
}
//创建match函数,匹配路由
function match (
raw,
currentRoute,
redirectedFrom
) {
var location = normalizeLocation(raw, currentRoute, false, router);
var name = location.name;
if (name) {
var record = nameMap[name];
if (process.env.NODE_ENV !== 'production') {
warn(record, ("Route with name '" + name + "' does not exist"));
}
if (!record) { return _createRoute(null, location) }
var paramNames = record.regex.keys
.filter(function (key) { return !key.optional; })
.map(function (key) { return key.name; });
if (typeof location.params !== 'object') {
location.params = {};
}
if (currentRoute && typeof currentRoute.params === 'object') {
for (var key in currentRoute.params) {
if (!(key in location.params) && paramNames.indexOf(key) > -1) {
location.params[key] = currentRoute.params[key];
}
}
}
location.path = fillParams(record.path, location.params, ("named route \"" + name + "\""));
return _createRoute(record, location, redirectedFrom)
} else if (location.path) {
location.params = {};
for (var i = 0; i < pathList.length; i++) {
var path = pathList[i];
var record$1 = pathMap[path];
if (matchRoute(record$1.regex, location.path, location.params)) {
return _createRoute(record$1, location, redirectedFrom)
}
}
}
// no match
return _createRoute(null, location)
}
//重定向
function redirect (record,location) {...}
//别名
function alias (record,location,matchAs) {...}
function _createRoute (
record,
location,
redirectedFrom
) {
if (record && record.redirect) {
return redirect(record, redirectedFrom || location)
}
if (record && record.matchAs) {
return alias(record, location, record.matchAs)
}
return createRoute(record, location, redirectedFrom, router)
}
return {
match: match,
addRoutes: addRoutes
}
}
createMatcher 就是暴露出两个方法给VueRouter,需要做路由的映射以及动态添加路由的方法。