前言
在學習 vue-router 的程式碼之前,先來簡單瞭解一下前端路由。
前端路由主要有兩種實現方法:
- Hash 路由
- History 路由
先來看看這兩種方法的實現原理。
接著我們將用它們來簡單實現一個自己的前端路由。
前端路由
Hash 路由
url 的 hash
是以 #
開頭,原本是用來作為錨點,從而定位到頁面的特定區域。當 hash
改變時,頁面不會因此重新整理,瀏覽器也不會向伺服器傳送請求。
http://www.xxx.com/#/home
複製程式碼
同時,hash
改變時,並會觸發相應的 hashchange
事件。所以,hash 很適合被用來做前端路由。當 hash 路由發生了跳轉,便會觸發 hashchange 回撥,回撥裡可以實現頁面更新的操作,從而達到跳轉頁面的效果。
window.addEventListener('hashchange', function () {
console.log('render');
});
複製程式碼
History 路由
HTML5 規範中提供了 history.pushState
和 history.replaceState
來進行路由控制。通過這兩個方法,可以實現改變 url 且不向伺服器傳送請求。同時不會像 hash
有一個 #
,更加的美觀。但是 History 路由需要伺服器的支援,並且需將所有的路由重定向到根頁面。
History 路由的改變不會去觸發某個事件,所以我們需要去考慮如何觸發路由更新後的回撥。
有以下兩種方式會改變 url:
- 呼叫 history.pushState 或 history.replaceState;
- 點選瀏覽器的前進與後退。
第一個方式可以封裝一個方法,在呼叫 pushState(replaceState)後再呼叫回撥。
function push (url) {
window.history.pushState({}, null, url);
handleHref();
}
function handleHref () {
console.log('render');
}
複製程式碼
第二個方式,瀏覽器的前進與後退會觸發 popstate
事件。
window.addEventListener('popstate', handleHref);
複製程式碼
路由實現
我們通過 <a>
標籤來進行切換路由,通過一個 <div>
標籤來裝載各路由對應的頁面內容。
參考 vue-router 的呼叫,我們會這麼地呼叫一個 Router
,將路由與對應元件作為引數傳入:
const router = new Router([
{
path: '/',
component: 'home'
},
{
path: '/book',
component: 'book'
},
{
path: '/movie',
component: 'movie'
}
]);
複製程式碼
陣列裡是各路由對應的要顯示的內容,接下來就來開始實現這個 Router
。
Hash 路由實現
Hash 路由 <a>
標籤都需要帶上 #
:
<div>
<a href="#/">home</a>
<a href="#/book">book</a>
<a href="#/movie">movie</a>
<div id="content"></div>
</div>
複製程式碼
Router
的程式碼實現如下:
class Router {
constructor (options) {
this.routes = {};
this.init();
// 遍歷,繫結檢視更新
options.forEach(item => {
this.route(item.path, () => {
document.getElementById('content').innerHTML = item.component;
});
});
}
// 繫結監聽事件
init () {
window.addEventListener('load', this.updateView.bind(this), false);
window.addEventListener('hashchange', this.updateView.bind(this), false);
}
// 更新試圖
updateView () {
const currentUrl = window.location.hash.slice(1) || '/';
this.routes[currentUrl] && this.routes[currentUrl]();
}
// 將路由與回撥函式關聯
route (path, cb) {
this.routes[path] = cb;
}
}
複製程式碼
實現效果如下:
History 路由實現
History 路由需要伺服器的支援,可以點選 這裡 的程式碼參考。
<div>
<a href="javascript:void(0);" data-href="/">home</a>
<a href="javascript:void(0);" data-href="/book">book</a>
<a href="javascript:void(0);" data-href="/movie">movie</a>
<div id="content"></div>
</div>
複製程式碼
Router
的程式碼實現如下:
class Router {
constructor (options) {
this.routes = {};
this.init();
this.bindEvent();
// 遍歷,繫結檢視更新
options.forEach(item => {
this.route(item.path, () => {
document.getElementById('content').innerHTML = item.component;
});
});
}
// 繫結點選事件
bindEvent () {
const _this = this;
const links = document.getElementsByTagName('a');
[].forEach.call(links, link => {
link.addEventListener('click', function () {
const url = this.getAttribute('data-href');
_this.push(url);
});
});
}
// 繫結監聽事件
init () {
window.addEventListener('load', this.updateView.bind(this), false);
window.addEventListener('popstate', this.updateView.bind(this), false);
}
push (url) {
window.history.pushState({}, null, url);
this.updateView();
}
// 更新試圖
updateView () {
const currentUrl = window.location.pathname || '/';
this.routes[currentUrl] && this.routes[currentUrl]();
}
// 將路由與回撥函式關聯
route (path, cb) {
this.routes[path] = cb;
}
}
複製程式碼
實現效果如下:
最後
前端路由實現方式有兩種,分別是:
- Hash 路由
- History 路由
原理都是修改 url 的同時不重新整理頁面,不向伺服器傳送請求,通過監聽特殊的事件來更新頁面。
以上實現全部原始碼參考 這裡。