首先要學習一下history對象,history對象保存着用戶的上網記錄,從瀏覽器窗口打開的那一刻算起。出於安全的考慮,開發人員沒法得知用戶瀏覽過的URL。不過,藉由用戶訪問過的頁面列表,一樣能夠在不知道實際URL的狀況下實現後退與前進
go(Stirng|number)
使用go方法能夠在用戶的歷史記錄中任意跳轉,能夠向後也能夠向前。這個方法接受一個參數,表示向後或向前跳轉的頁面數的一個整數值。負數表示向後跳轉(相似瀏覽器的後退按鈕),正數表示向前跳轉(相似瀏覽器的前進按鈕)。來看下例子javascript
//後退一頁 history.go(-1) //前進一頁 history.go(1) //前進兩頁 history.go(2)
也能夠給go()方法船體一個字符串參數,此時瀏覽器會跳轉到歷史記錄中包含改字符串的第一個位置,可能後退也可能前進,具體要看哪個位置最近。若是歷史記錄中不包含該字符串,則什麼都不作。例如:html
//跳轉到最近的wrox.com頁面 history.go("wrox.com") //跳轉到最近的douban.cn頁面 history.go("douban.cn")
back()
和forward
這兩個方法能夠來代替go(),模仿瀏覽器的後退和前進功能前端
back()至關於 go(-1) 後退一個頁面html5
forward至關於go(1) 前進一個頁面java
注:接下來幾個方法是html5新增的方法api
pushState(state,title,url)
該方法的做用是 在歷史記錄中新增一條記錄,改變瀏覽器地址欄的url,可是,不刷新頁面。瀏覽器
pushState對象接受三個參數,安全
state
:一個與添加的記錄相關聯的狀態對象,主要用於popstate
事件。該事件觸發時,該對象會傳入回調函數。也就是說,瀏覽器會將這個對象序列化之後保留在本地,從新載入這個頁面的時候,能夠拿到這個對象。若是不須要這個對象,此處能夠填null
。title
:新頁面的標題。可是,如今全部瀏覽器都忽視這個參數,因此這裏能夠填空字符串。url
:新的網址,必須與當前頁面處在同一個域。瀏覽器的地址欄將顯示這個網址。舉個例子,假設當前網址是hello.com/1.html,使用puchState()方法在瀏覽記錄中添加一個新紀錄服務器
var stateObj={foo:'bar'} history.pushState(starteObj,'','2.html')
添加新紀錄後,瀏覽器的地址欄馬上顯示`hello.com/2.html
,但不會跳轉到2.html,也不會檢查2.html是否存在,它只是成爲瀏覽歷史中的最新記錄。frontend
總之,pushState()方法不會觸發頁面刷新,只是致使history對象發生變化,地址欄會有反應,使用該方法後,就可使用history.state屬性讀出狀態對象
var stateObj={foo:'bar'} history.pushState(starteObj,'','2.html') history.state //=> {foo:"bar"}
注意:若是pushState的URL參數設置了一個新的hash值,並不會觸發hashchange事件。
replaceState(state,title,url)
replaceState方法的做用是替換當前的歷史記錄,其餘的都與pushState()方法如出一轍。
假定當前網頁是example.com/example.html
。
history.pushState({page: 1}, 'title 1', '?page=1') // URL 顯示爲 http://example.com/example.html?page=1 history.pushState({page: 2}, 'title 2', '?page=2'); // URL 顯示爲 http://example.com/example.html?page=2 history.replaceState({page: 3}, 'title 3', '?page=3'); // URL 顯示爲 http://example.com/example.html?page=3 history.back() // URL 顯示爲 http://example.com/example.html?page=1 history.back() // URL 顯示爲 http://example.com/example.html history.go(2) // URL 顯示爲 http://example.com/example.html?page=3
popstate事件是window
對象上的事件,配合pushState()和replaceState()方法使用。當同一個文檔
(能夠理解爲同一個網頁,不能跳轉,跳轉了就不是同一個網頁了)的瀏覽歷史出現變化時,就會觸發popstate事件。
上面咱們說過,調用pushState()
或者replaceState()
方法都會改變當前的歷史記錄,僅僅調用pushState()
方法或replaceState()
方法 ,並不會觸發該事件,另一個條件是用戶必須點擊瀏覽器的倒退按鈕或者前進按鈕,或者使用js調用history.back()或者history.forward()等方法。
因此,記住popstate事件觸發的條件
1. 處在同一個文檔(同一個html頁面) 2. 文檔的瀏覽歷史(即history對象)發生改變
只要符合這兩個條件,popstate事件就會觸發
具體例子
//index.html <head> <script> window.onpopstate=function(){ alert('location '+document.location+',state '+JSON.stringify(event.state)) } </script> </head> <body> <!--第二步 --> <button onclick="window.history.back()">後退</button> <button onclick="window.history.forward()">前進</button> <!--第一步 --> <button onclick="window.history.pushState(null,'','1.html')">pushState</button> </body>
先點擊pushState按鈕,在點擊後退按鈕,就會觸發popstate事件
再來一個例子
//index.html <head> <script> window.onpopstate=function(){ alert('location '+document.location+',state '+JSON.stringify(event.state)) } </script> </head> <body> <a href="#one">#one</a> </body>
直接點擊a
標籤,也能夠觸發popstate事件
圖片來自mdn傳送門
前端路由的本質是監聽 URL 的變化,而後匹配路由規則,顯示相應的頁面,而且無須刷新。
目前單頁面使用的路由就只有兩種實現方式
www.test.com/##/就是Hash URL,當##
後面的哈希值發生變化時,不會向服務器請求數據,能夠經過hashchange事件來監聽到URL的變化,從而進行跳轉頁面
網上偷來的一張圖:
history模式相比hash模式更美觀,須要用到Html5新增的幾個api實現,原理以下:
繼續偷圖:
在介紹實例前先介紹下location對象,location對象提供了與當前窗口中加載的文檔有關的信息。它包含如下屬性:
屬性名 | 例子 | 說明 |
---|---|---|
host | www.hello.com:8080 | 返回服務器名稱和端口號(若是有的話) |
hostname | www.hello.com | 返回服務器名稱,不帶端口號 |
href | http://www.hello.com | 返回當前加載頁面的完整url |
pathname | /user/ming | 返回url中的目錄 |
hash | #content | 返回url中的hash,若是沒有返回空字符串 |
search | ?q=javascript | 返回Url的查詢字符串,這個字符串以問號開頭 |
咱們在下方的示例中須要用到pathname
屬性拿到訪問的路徑
一個簡單的history模式單頁面路由實現以下:
//1. 路由規則 const routes={ '/user':user, //user是引入的視圖 import user from './view/user' '/about':about } //2. 路由控制類 class Router { start() { // 點擊瀏覽器後退/前進按鈕時會觸發window.onpopstate事件, 咱們在這時切換到相應頁面 // https://developer.mozilla.org/en-US/docs/Web/Events/popstate window.addEventListener('popstate', () => { this.load(location.pathname) }) // 打開頁面時加載當前頁面 在單頁面入口文件中要調用start方法 this.load(location.pathname) } // 前往path, 變動地址欄URL, 並加載相應頁面 go(path) { // 變動地址欄URL history.pushState({}, '', path) // 加載頁面 this.load(path) } // 加載path路徑的頁面 load(path) { // 首頁 if (path === '/') path = '/foo' // 建立頁面實例 const view = new routes[path]() // 調用頁面方法, 把頁面加載到document.body中 view.mount(document.body) } }
Router類的做用是控制頁面根據當前Url切換
start()
go(path)
load(path)