本文總結自:
最初接觸前端路由是vue-router,當時僅僅覺得可以通過router-link改變頁面,覺得很神奇呀,用多了就習慣了。
后面接觸了node,通過express知曉了后端路由,這是我就開始疑問了:
為什么使用那些前端框架的時候,前端url改變了視圖,但是卻沒有向后臺發送請求?
首先我們需要一些前置知識。
路由/前端路由/后端路由?
路由:通過不同的url地址展示不同的內容或者頁面。
前面提到過,我最初接觸到路由是通過express框架,我們先看一段代碼:
app.post('/category/add', function (req, res, next) {
// do something
})
如果你沒有接觸過node也沒關系,因為真的不難理解。
定義了一個path('/categoty/add'),當有人通過(即域名+path),例如:
http://www.example.com/category/add
發起post請求時,就會進入后臺定義好的回調函數,進行邏輯處理,譬如取得post傳遞過來的實體數據,對數據庫進行增刪查改,然后返回一個渲染好的html頁面或者是json數據等等。
后端路由(不考慮提供API服務返回數據),這一過程由服務器控制完成的,直接噴射一個html給前端,瀏覽器頁面刷新。
那前端路由是什么呢?還是通過不同的url地址展示不同的內容或者頁面,但是這一過程都是由前端完成的,我們的頁面或視圖(模塊)是在前端編寫好,通過url變化去切換而已。
為什么需要前端路由:
因為后臺每次返回一個新頁面都會進行全局刷新,而在單頁面應用中,大部分頁面結構不變,只改變部分內容的使用,我們可以通過前端路由改變頁面內容,后臺只需要通過ajax提供數據即可。
現在,我們來看看前端路由到底是如何實現的。
實現簡易前端路由
首先我們需要了解HTML5為我們提供的history API,準確的說是這里面的兩個方法:
history.pushState和history.replaceState
history.pushState
帶有三個參數:一個狀態對象,一個標題(現在被忽略了),以及一個可選的URL地址。下面將對這三個參數進行細致的檢查:
實例代碼(來自Web開發中 前端路由 實現的幾種方式和適用場景)
//假設當前網頁URL為:http://tonylee.pw
window.history.pushState(null, null, "http://tonylee.pw?name=tonylee");
//url變化:http://tonylee.pw -> http://tonylee.pw?name=tonylee
window.history.pushState(null, null, "http://tonylee.pw/name/tonylee");
//url變化:http://tonylee.pw -> http://tonylee.pw/name/tonylee
window.history.pushState(null, null, "?name=tonylee");
//url變化:http://tonylee.pw -> http://tonylee.pw?name=tonylee
window.history.pushState(null, null, "name=tonylee");
//url變化:http://tonylee.pw -> http://tonylee.pw/name=tonylee
window.history.pushState(null, null, "/name/tonylee");
//url變化:http://tonylee.pw -> http://tonylee.pw/name/tonylee
window.history.pushState(null, null, "name/tonylee");
//url變化:http://tonylee.pw -> http://tonylee.pw/name/tonylee
//錯誤的用法:
window.history.pushState(null, null, "http://www.tonylee.pw?name=tonylee");
//error: 由于跨域將產生錯誤
history.replaceState
pushState()和replaceState()參數一樣。
兩個方法的主要區別就是:pushState()是在history棧中添加一個新的條目,replaceState()是替換當前的記錄值。關于API的詳細解釋可以戳這里
我們需要知道的是,就是兩個方法可以改變瀏覽器的url,但是不會重新加載頁面,沒錯!!!這些 URL 不會直接傳給服務器,而是會被瀏覽器消化處理掉.
這個就是我們需要的。
這樣我們就可以通過這兩個方法改變url了。
改變之后呢?切換視圖呀!
那我們就需要在這兩個方法被調用的時候,觸發一個方法去切換視圖,好在HTML5也給我們提供了一個事件。
window.onpopstate
瀏覽器本身會自帶一個popstate事件,但是只有在我們點擊返回或前進按鈕時才會正常觸發。
很顯然,在我們單頁應用中是需要你去大部分情況下都是需要去點擊某個Link調用pushState,而這樣是無法觸發popstate事件的,需要重寫一下pushState,并且給他也定義一個事件,這里就叫他onpushstate吧,比如下面這樣。
(function(history){
var pushState = history.pushState;
history.pushState = function(state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({state: state});
}
return pushState.apply(history, arguments);
}
})(window.history)
window.onpopstate = history.onpushstate = function(event) {
// change view
}
如此一來,進行pushState操作會觸發onpushstate事件,我們可以在onpushstate事件的回調中進行視圖切換了,而前進后退我們可以通過popstate來操作。
別忘了給a標簽做一些必要操作,阻止默認跳轉,而是通過pushState改變url,然后由于pushState被調用,又會觸發onpushstate事件,其內的邏輯代碼就會被執行,比如,切換視圖。
var elements = document.getElementsByTagName('a');
for(var i = 0, len = elements.length; i < len; i++) {
elements[i].onclick = function (event) {
event.preventDefault();
var route = event.target.getAttribute('href');
history.pushState({page: route}, route, route)
}
}
前端路由的基本實現就是以上了,代碼來自余博倫-知乎首頁,現在再回頭看vue-router/react-router是否清晰了一點呢,當然其內部實現遠遠不是這么簡單。
小知識-pjax
pjax是一種基于ajax+history.pushState的新技術,該技術可以無刷新改變頁面的內容,并且可以改變頁面的URL。pjax是ajax+pushState的封裝,同時支持本地存儲、動畫等多種功能。目前支持jquery、qwrap、kissy等多種版本。
眾所周知,Ajax可以實現頁面的無刷新操作——優點;但是,也會造成另外的問題,無法前進與后退!曾幾何時,Gmail似乎借助iframe搞定,如今,HTML5讓事情變得如同過家家般簡單。
當執行Ajax操作的時候,往瀏覽器history中塞入一個地址(使用pushState)(這是無刷新的);于是,返回的時候,通過URL或其他傳參,我們就可以還原到Ajax之前的模樣。