前端三大框架 Angular
、React
、Vue
,它們的路由解決方案 angular/router
、react-router
、vue-router
都是基於前端路由原理進行封裝實現的,所以將前端路由原理進行了解和掌握是頗有必要的,由於咱們再使用的過程當中也不免會遇到一些坑,一旦咱們掌握了它的實現原理,那麼就能在開發中對路由的使用更加遊刃有餘。javascript
路由的概念起源於服務端,在之前先後端不分離的時候,由後端來控制路由,當接收到客戶端發來的 HTTP
請求,就會根據所請求的相應 URL
,來找到相應的映射函數,而後執行該函數,並將函數的返回值發送給客戶端。對於最簡單的靜態資源服務器,能夠認爲,全部 URL
的映射函數就是一個文件讀取操做。對於動態資源,映射函數多是一個數據庫讀取操做,也多是進行一些數據的處理等等。而後根據這些讀取的數據,在服務器端就使用相應的模板來對頁面進行渲染後,再返回渲染完畢的頁面。它的好處與缺點很是明顯:前端
SEO
好; 也正是因爲後端路由還存在着本身的不足,前端路由纔有了本身的發展空間。對於前端路由來講,路由的映射函數一般是進行一些 DOM
的顯示和隱藏操做。這樣,當訪問不一樣的路徑的時候,會顯示不一樣的頁面組件。前端路由主要有如下兩種實現方案:vue
Hash
History
固然,前端路由也存在缺陷:使用瀏覽器的前進,後退鍵時會從新發送請求,來獲取數據,沒有合理地利用緩存。但總的來講,如今前端路由已是實現路由的主要方式了,前端三大框架 Angular
、React
、Vue
,它們的路由解決方案 angular/router
、react-router
、vue-router
都是基於前端路由進行開發的,所以將前端路由進行了解和 掌握是頗有必要的,下面咱們分別對兩種常見的前端路由模式 Hash
和 History
進行講解。java
早期的前端路由的實現就是基於 location.hash
來實現的。其實現原理也很簡單,location.hash
的值就是 URL
中 # 後面的內容。好比下面這個網站,它的 location.hash
的值爲 '#search'
:react
https://www.word.com#search
複製代碼
此外,hash
也存在下面幾個特性:git
URL
中 hash
值只是客戶端的一種狀態,也就是說當向服務器端發出請求時,hash
部分不會被髮送。hash
值的改變,都會在瀏覽器的訪問歷史中增長一個記錄。所以咱們能經過瀏覽器的回退、前進按鈕控制hash
的切換。hashchange
事件來監聽 hash
的變化。咱們能夠經過兩種方式觸發 hash
變化,一種是經過 a
標籤,並設置 href
屬性,當用戶點擊這個標籤後,URL
就會發生改變,也就會觸發 hashchange
事件了:github
<a href="#search">search</a>
複製代碼
還有一種方式就是直接使用 JavaScript
來對 loaction.hash
進行賦值,從而改變 URL
,觸發 hashchange
事件:vue-router
location.hash="#search"
複製代碼
如下實現咱們採用第2種方式來實現。數據庫
咱們先定義一個父類 BaseRouter
,用於實現 Hash
路由和 History
路由的一些共有方法;segmentfault
export class BaseRouter {
// list 表示路由表
constructor(list) {
this.list = list;
}
// 頁面渲染函數
render(state) {
let ele = this.list.find(ele => ele.path === state);
ele = ele ? ele : this.list.find(ele => ele.path === '*');
ELEMENT.innerText = ele.component;
}
}
複製代碼
咱們簡單實現了 push
壓入功能、go
前進/後退功能,相關代碼的註釋都已經標上,簡單易懂,就不在一 一介紹,參見以下:
export class HashRouter extends BaseRouter {
constructor(list) {
super(list);
this.handler();
// 監聽 hashchange 事件
window.addEventListener('hashchange', e => {
this.handler();
});
}
// hash 改變時,從新渲染頁面
handler() {
this.render(this.getState());
}
// 獲取 hash 值
getState() {
const hash = window.location.hash;
return hash ? hash.slice(1) : '/';
}
// push 新的頁面
push(path) {
window.location.hash = path;
}
// 獲取 默認頁 url
getUrl(path) {
const href = window.location.href;
const i = href.indexOf('#');
const base = i >= 0 ? href.slice(0, i) : href;
return base +'#'+ path;
}
// 替換頁面
replace(path) {
window.location.replace(this.getUrl(path));
}
// 前進 or 後退瀏覽歷史
go(n) {
window.history.go(n);
}
}
複製代碼
Hash
模式的路由實現例子的效果圖以下所示:
前面的 hash
雖然也很不錯,但使用時都須要加上 #,並非很美觀。所以到了 HTML5
,又提供了 History API
來實現 URL
的變化。其中作最主要的 API
有如下兩個:history.pushState()
和 history.repalceState()
。這兩個 API
能夠在不進行刷新的狀況下,操做瀏覽器的歷史紀錄。惟一不一樣的是,前者是新增一個歷史記錄,後者是直接替換當前的歷史記錄,以下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
複製代碼
此外,history
存在下面幾個特性:
pushState
和 repalceState
的標題(title
):通常瀏覽器會忽略,最好傳入 null
;popstate
事件來監聽 url
的變化;history.pushState()
或 history.replaceState()
不會觸發 popstate
事件,這時咱們須要手動觸發頁面渲染;咱們一樣簡單實現了 push
壓入功能、go
前進/後退功能,相關代碼的註釋都已經標上,簡單易懂,就不在一 一介紹,參見以下:
export class HistoryRouter extends BaseRouter {
constructor(list) {
super(list);
this.handler();
// 監聽 popstate 事件
window.addEventListener('popstate', e => {
console.log('觸發 popstate。。。');
this.handler();
});
}
// 渲染頁面
handler() {
this.render(this.getState());
}
// 獲取 url
getState() {
const path = window.location.pathname;
return path ? path : '/';
}
// push 頁面
push(path) {
history.pushState(null, null, path);
this.handler();
}
// replace 頁面
replace(path) {
history.replaceState(null, null, path);
this.handler();
}
// 前進 or 後退瀏覽歷史
go(n) {
window.history.go(n);
}
}
複製代碼
History
模式的路由實現例子的效果圖以下所示:
對比點 | Hash 模式 | History 模式 |
---|---|---|
美觀性 | 帶着 # 字符,較醜 | 簡潔美觀 |
兼容性 | >= ie 8,其它主流瀏覽器 | >= ie 10,其它主流瀏覽器 |
實用性 | 不須要對服務端作改動 | 須要服務端對路由進行相應配合設置 |
本文咱們大體介紹了什麼路由、前端路由的源起、以及分析了兩種前端路由:Hash
模式和 History
模式的原理以及簡單功能實現,文中例子的代碼實現已經放到 github 上面:github.com/fengshi123/… 。經過本文對前端路由原理的掌握,這時你就能夠基礎原理基礎去閱讀 vue-router
和 react-router
的源碼實現了。
github地址爲:github.com/fengshi123/… ,上面彙總了做者全部的博客文章,若是喜歡或者有所啓發,請幫忙給個 star ~,對做者也是一種鼓勵。
一、淺談前端路由:www.jianshu.com/p/d2aa8fb95…