原文:
https://www.cnblogs.com/xuzhudong/p/8869699.html
前端应用的主流形式:单页应用(SPA) 大型的单页应用最显著的特色之一:采用前端路由系统,经过改变URL,在不从新请求页面的状况下,更新页面视图html
更新视图但不从新请求页面是前端路由原理的核心之一,目前唉浏览器环境中这一功能的实现主要有两种方式:前端
h5 history api:
https://developer.mozilla.org/zh-CN/docs/Web/API/History
vue
vue-router是Vue.js框架的路由插件,下面咱们从它的源码入手,了解下vue-router 是如何经过这两种方式实现前端路由的html5
在vue-router中是经过mode这一参数控制路由的实现模式的:vue-router
const router = new VueRouter({
mode: 'history',
routes: [...]
})
复制代码
vue-router的实际源码:api
export default class VueRouter {
mode: string; // 传入的字符串参数,指示history类别
history: HashHistory | HTML5History | AbstractHistory; // 实际起做用的对象属性,必须是以上三个类的枚举
fallback: boolean; // 如浏览器不支持,'history'模式需回滚为'hash'模式
constructor (options: RouterOptions = {}) {
let mode = options.mode || 'hash' // 默认为'hash'模式
this.fallback = mode === 'history' && !supportsPushState // 经过supportsPushState判断浏览器是否支持'history'模式
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract' // 不在浏览器环境下运行需强制为'abstract'模式
}
this.mode = mode
// 根据mode肯定history实际的类并实例化
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: $`)
}
}
}
init (app: any /* Vue component instance */) {
const history = this.history;
// 根据history的类别执行相应的初始化操做和监听
if (history instanceof HTML5History) {
history.transitionTo(history.getCurrentLocation())
} else if (history instanceof HashHistory) {
const setupHashListener = () => {
history.setupListeners()
}
history.transitionTo(
history.getCurrentLocation(),
setupHashListener,
setupHashListener
)
}
history.listen(route => {
this.apps.forEach((app) => {
app._route = route
})
})
}
// VueRouter类暴露的如下方法实际是调用具体history对象的方法
VueRouter.prototype.beforeEach = function beforeEach (fn) {
return registerHook(this.beforeHooks, fn)
};
VueRouter.prototype.beforeResolve = function beforeResolve (fn) {
return registerHook(this.resolveHooks, fn)
};
VueRouter.prototype.afterEach = function afterEach (fn) {
return registerHook(this.afterHooks, fn)
};
VueRouter.prototype.onReady = function onReady (cb, errorCb) {
this.history.onReady(cb, errorCb);
};
VueRouter.prototype.onError = function onError (errorCb) {
this.history.onError(errorCb);
};
VueRouter.prototype.push = function push (location, onComplete, onAbort) {
this.history.push(location, onComplete, onAbort);
};
VueRouter.prototype.replace = function replace (location, onComplete, onAbort) {
this.history.replace(location, onComplete, onAbort);
};
VueRouter.prototype.go = function go (n) {
this.history.go(n);
};
VueRouter.prototype.back = function back () {
this.go(-1);
};
VueRouter.prototype.forward = function forward () {
this.go(1);
};
复制代码
从上面代码能够看出:浏览器
HTML5History
; 'hash':HashHishtory
; abstract:AbstractHistory
;> http://www.example.com/index.html#print
复制代码
#号自己以及它后面的字符称之为hash,可经过window.location.hash属性读取。它具备如下特色:bash
window.addEventListener("hashchange", funcRef, false)
服务器
因而可知,利用hash的特色,就尅来实现前端路由“更新视图但不从新请求页面”的功能。 app
源码:
HashHistory.prototype.push = function push (location, onComplete, onAbort) {
var this$1 = this;
var ref = this;
var fromRoute = ref.current;
this.transitionTo(location, function (route) {
pushHash(route.fullPath);
handleScroll(this$1.router, route, fromRoute, false);
onComplete && onComplete(route);
}, onAbort);
};
// 对window的hash进行直接赋值
function pushHash (path) {
if (supportsPushState) {
pushState(getUrl(path));
} else {
window.location.hash = path;
}
}
复制代码
transitionTo()
方法是父类中定义用来处理路由变化中的基础逻辑的,push()
方法最主要的是对window的hash进行了直接赋值
pushHash(route.fullPath)
hash的改变会自动添加到浏览器的访问历史记录中
$router.push()
-->HashHistory.push()
-->History.transitionTo()
-->History.updateRoute()
-->vm.render()