原文:
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()