首发:前端路由实现(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)
演示: 演示地址