首發:前端路由實現(history)javascript
HTML5 history
新增了兩個API
:history.pushState
和history.replaceState
css
兩個API
都接收三個參數:html
state object
):一個JavaScript對象,與用pushState()
方法建立的新歷史記錄條目關聯。不管什麼時候用戶導航到新建立的狀態,popstate
事件都會被觸發,而且事件對象的state屬性都包含歷史記錄條目的狀態對象的拷貝。title
):FireFox瀏覽器目前會忽略該參數,雖然之後可能會用上。考慮到將來可能會對該方法進行修改,傳一個空字符串會比較安全。或者,你也能夠傳入一個簡短的標題,標明將要進入的狀態。URL
): 新的歷史記錄條目的地址。瀏覽器不會在調用pushState()
方法後加載該地址,但以後,可能會試圖加載,例如用戶重啓瀏覽器。新的URL不必定是絕對路徑;若是是相對路徑,它將以當前URL爲基準;傳入的URL與當前URL應該是同源的,不然,pushState()
會拋出異常。該參數是可選的;不指定的話則爲文檔當前URL。相同之處是兩個 API
都會操做瀏覽器的歷史記錄,而不會引發頁面的刷新。前端
不一樣之處在於pushState
會增長一條新的歷史記錄,而replaceState
則會替換當前的歷史記錄。java
這裏你們能夠先F12
試試,看看地址欄發生了什麼變化jquery
window.history.pushState(null, null, "hell"); window.history.pushState(null, null, "/hell"); window.history.pushState(null, null, "#/hello"); window.history.pushState(null, null, "?name=");
注意:這裏的url不支持跨域,不然會拋出異常
index.html
git
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>前端路由實現</title> <style> .warp{ width:400px; height:400px; border:1px solid grey; margin:0 auto; } .nav{ border-bottom:1px solid grey; } .nav li{ display:inline-block; list-style:none; } .nav li a{ display:inline-block; text-decoration: none; padding:10px 15px; } .router{ padding:20px; } a{ cursor: pointer; } </style> </head> <body> <section class="warp"> <div class="nav"> <ul> <li><a href="javascript:void(0)" data-path="index">首頁</a></li> <li><a href="javascript:void(0)" data-path="news">新聞</a></li> <li><a href="javascript:void(0)" data-path="about">關於</a></li> </ul> </div> <div id="router" class="router"> <!-- 內容加載區域 --> </div> </section> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="./router.js"></script> </body> </html>
router.js
github
;(function(){ history.replaceState(null,null,'');//最開始的狀態,採用replace直接替換 $('#router').html('<p>nav1</p>') $('a').on('click',function(){ console.log(this.text) var text = this.text; $('#router').html('<p>'+ text +'</p>') history.pushState(null,null,'#/'+text); }) })()
最簡單的示例,只能監聽點擊事件,而瀏覽器中的後、前進都不能監聽地址欄的改變ajax
router.js
api
狀態版 ;(function(){ var count = [0,0,0] $('#router').html('<p>導航1:</p>'+count[0]+'<p>導航2:</p>'+count[1]+'<p>導航3:</p>'+count[2]) history.replaceState(count,null,'');//最開始的狀態,採用replace直接替換 for(var i = 0 ; i<$('a').length; i++){ $('a')[i].index = i $('a').eq(i).on('click',function(){ console.log(this.index); var index = this.index; count[index]++; $('#router').html('<p>導航1:</p>'+count[0]+'<p>導航2:</p>'+count[1]+'<p>導航3:</p>'+count[2]) history.pushState(count,null,'#/count'+count[index]);//以後的狀態,須要進行保存 }) } //監聽history其餘api致使地址欄url改變事件 window.addEventListener('popstate',function(e){ console.log(e.state); var state = e.state; $('#router').html('<p>導航1:</p>'+state[0]+'<p>導航2:</p>'+state[1]+'<p>導航3:</p>'+state[2]) }) })()
popstate
當活動歷史記錄條目更改時,將觸發popstate事件。若是被激活的歷史記錄條目是經過對history.pushState()的調用建立的,或者受到對history.replaceState()的調用的影響,popstate事件的state屬性包含歷史條目的狀態對象的副本。須要注意的是調用history.pushState()或history.replaceState()不會觸發popstate事件。只有在作出瀏覽器動做時,纔會觸發該事件,如用戶點擊瀏覽器的回退按鈕(或者在Javascript代碼中調用history.back())
router.js
;(function(){ var url = 'nav1'; history.replaceState(url,null,'');//最開始的狀態,採用replace直接替換 $('#router').html('<p>'+url+'</p>') $('a').on('click',function(){ console.log(this.text) url = this.text; $('#router').html('<p>'+ url +'</p>') history.pushState(url,null,'#/'+url); }) window.addEventListener('popstate',function(e){ console.log(e.state); url = e.state $('#router').html('<p>'+ url +'</p>') }); })()
兜兜轉轉咱們算是回到了起點,可是經過這張圖咱們會發現頁面點擊刷新按鈕會有導航和內容塊不一致的內容,因此咱們須要改進他,而且監聽load
事件
改進
;(function(){ $('a').on('click',function(){ console.log(this.text) url = this.text; $('#router').html('<p>'+ url +'</p>') history.pushState(url,null,'#/'+url); }) window.addEventListener('popstate',function(e){ console.log(e.state); url = e.state $('#router').html('<p>'+ url +'</p>') }); window.addEventListener('load',function(){ url = location.hash.slice(2) || 'nav1'; history.replaceState(url,null,''); console.log(location.hash); $('#router').html('<p>'+ url +'</p>'); }); })()
能夠看到咱們點擊刷新的時候導航和內容區域一致了。
咱們這裏仍是採用了ajax
的load
方法
router.js
;(function(){ var router = [ { 'path':'index', 'url':'./main.html' }, { 'path':'news', 'url':'./news.html' }, { 'path':'about', 'url':'./about.html' } ]; //改變頁面 function display_page(url){ $('#router').load(url) } $('a').on('click',function(){ var path = $(this).data('path'); console.log(path) for(var i in router){ if(router[i].path == path){ display_page(router[i].url); history.pushState(router[i].url,null,router[i].path); } } }) window.addEventListener('popstate',function(e){ var url = e.state; display_page(url); }); window.addEventListener('load',function(){ var start = location.href.lastIndexOf('/'); var path = location.hash.slice(start) || 'index'; console.log(path) for(var i in router){//刷新 加載 console.log(1) if(router[i].path == path){ display_page(router[i].url); history.replaceState(router[i].url,null,path); break; } if(i == router.length-1){//重定向 display_page(router[0].url); history.replaceState(router[i].url,null,router[0].path); } } }); })()
能夠看到基本是實現了history
路由功能,可是這裏有一個問題就是刷新後由於地址欄url緣由會報錯,也就是找不到這個頁面,這是因爲刷新的時候是重載,從新向網站目錄查找文件,而咱們當前目錄並無這個文件資源因此致使報錯。須要後臺攔截! 放棄!
折中
最後我仍是屈服於#
了
;(function(){ var router = [ { 'path':'index', 'url':'./main.html' }, { 'path':'news', 'url':'./news.html' }, { 'path':'about', 'url':'./about.html' } ]; //改變頁面 function display_page(url){ $('#router').load(url) } $('a').on('click',function(){ var path = $(this).data('path'); console.log(path) for(var i in router){ if(router[i].path == path){ display_page(router[i].url); history.pushState(router[i].url,null,'#/'+router[i].path); } } }) window.addEventListener('popstate',function(e){ var url = e.state; display_page(url); }); window.addEventListener('load',function(){ var path = location.hash.slice(2) || '/index'; console.log(path) for(var i in router){//刷新 加載 console.log(1) if(router[i].path == path){ display_page(router[i].url); history.replaceState(router[i].url,null,'#/' + path); break; } if(i == router.length-1){//重定向 display_page(router[0].url); history.replaceState(router[0].url,null,'#/' + router[0].path); } } }); })();
勉強的很呀
代碼: router(history)
演示: 演示地址