利用pushState, popState和location.hash等方法自己实现一个小型路由

简介:


这篇文章主要是记录下HTML5中history提供的pushState, replaceStateAPI。最后通过这些API自己实现小型的路由。

关于window.history提供的API请参见Mozilla文档

其中history提供的pushState和replaceState2个API提供了操作浏览器历史栈的方法。

其中pushState:

 
 
  1. history.pushState(data, null'#/page=1'); 
  2.  
  3. pushState接收3个参数,第一个参数为一个obj,表示浏览器 
  4.  
  5. 第二个参数是document.title的值,一般设定为`null
  6.  
  7. 第三个参数string,用以改变 当前url  

pushState方法在改变url的同时向浏览器历史栈中压入新的历史记录。

接收url的参数为string类型,用以改变当前地址栏的url.需要注意的一点就是这个参数不能和跨域,即协议,域名,端口必须都是相同的,如果出现跨域的情况,即会提示:

 
 
  1. Uncaught DOMException: Failed to execute 'pushState' on 'History': A history state object with URL 'http://www.baidu.com/' cannot be created in a document with origin 'http://commanderXL.com' and URL 

Example:

 
 
  1. 打开www.baidu.com 
  2.  
  3. history.pushState(nullnull'?page=1'
  4.  
  5. //地址栏变成 www.baidu.com/?page=1 
  6.  
  7. history.pushState(nullnull'#page=2'); 
  8.  
  9. //地址栏变成 www.baidu.com/#page=2  

其中replaceState:

 
 
  1. history.replaceState(nullnull'#page=2'); 

replaceState接收的参数pushState相同,但是最终的效果是:地址栏url会根据接收的参数而变化,但是浏览器并未在当浏览历史栈中增加浏览器的历史记录,而是替换当前的浏览器历史记录。

通过pushState和replaceState虽然能改变URL,但是不会主动触发浏览器reload。

window对象还提供popstate方法:

 
 
  1. window.addEventListener('popstate'function() { 
  2.  
  3. });  

这个方法用以监听浏览器在不同历史记录中进行切换,而触发相应的事件。

在浏览器提供的history对象上还有go, back方法,用以模拟用户点击浏览器的前进后退按钮。在某个web应用当中,比如点击了<a>标签,发生了页面的跳转。这时调用history.back();方法后页面回退,同时页面发生刷新,这时window.onpopstate无法监听这个事件。但是如果是通过pushState或者replaceState来改变URL且不发生浏览器刷新的话,再使用history.back()或history.go(),这样popstate事件会被触发。

 
 
  1. history.pushState({page: 1}, null'?page=1'); 
  2.  
  3. history.pushState({page: 2}, null'?page=2'); 
  4.  
  5. history.back(); //浏览器后退 
  6.  
  7. window.addEventListener('popstate'function(e) { 
  8.  
  9.   //在popstate事件触发后,事件对象event保存了当前浏览器历史记录的状态. 
  10.  
  11.   //e.state保存了pushState添加的state的引用 
  12.  
  13.   console.log(e.state); //输出 {page: 1} 
  14.  
  15. });  

PS: 通过pushState在url上添加?page=1可以通过location.search去获取search的内容。不过如果通过location.search去改变url的话是会主动触发浏览器reload的。这个特性可以和下面将的关于hash的内容对比下。

API大致了解了,那么这些方法可以运用到哪些地方呢?一个比较常用的场景是就在单页应用中,通过这些API完成前端的路由设计,利用pushState, replaceState可以改变url同时浏览器不刷新,并且通过popstate监听浏览器历史记录的方式,完成一系列的异步动作。

 
 
  1. <a data-href="/post"></a> 
  2.     <a data-href="/login"></a> 
  3.      
  4.     //路由 
  5.     const Router = []; 
  6.      
  7.     const addRoute = (path = '', handle = () => {}) => { 
  8.         let obj = { 
  9.             path, 
  10.             handle 
  11.         } 
  12.          
  13.         Router.push(obj); 
  14.     } 
  15.      
  16.      
  17.     //添加路由定义 
  18.     addRoute('/post'function() { 
  19.         //do something 
  20.     }); 
  21.      
  22.     addRoute('/login'function() { 
  23.         //do something 
  24.     }) 
  25.      
  26.      
  27.     //路由处理 
  28.     const routeHandle = (path) => { 
  29.         Router.forEach((item, index) => { 
  30.             if(item.path === path) { 
  31.                 item.handle.apply(null, [path]); 
  32.                 return true
  33.             } 
  34.         }) 
  35.         return false
  36.     } 
  37.      
  38.      
  39.     //拦截默认的a标签行为 
  40.     document.addEventListener('click'function(e) { 
  41.         let dataset = e.target.dataset; 
  42.         if(dataset) { 
  43.             if(routeHandle(dataset.href)) { 
  44.                 //阻止默认行为 
  45.                 e.preventDefault(); 
  46.             } 
  47.         } 
  48.     })  

大致的实现思路就是,通过<a>添加路由信息,然后拦截<a>标签的默认行为,并与注册的路由信息进行匹配。若匹配成功调用对应的handle方法.

不过pushState和replaceState方法在低版本的IE浏览器下兼容性不是很好。所以可以进行降级使用hash来进行路由设计。

hash?请戳我。

可以通过location.hash获取url上第一个#(fragment)及后面的内容。同时还能通过location.hash改写其内容,且不会主动触发浏览器reload。 有些功能是不是和pushState和replaceState一样? 所以为了兼容到低版本的浏览器,可以通过监听#变化来进行路由设计。

那么如何去监听呢? 比较粗暴的一种方式就是polling。

 
 
  1. var oldHash = location.hash; 
  2. setTimeInterval(function() { 
  3.     if(oldHash !== location.hash) { 
  4.          
  5.         //do something 
  6.      
  7.         oldHash = location.hash; 
  8.     } 
  9. }, 100);  

不过,H5还提供了一个API: hashchange。它的就可以直接代替上面的polling方法,来监听#的变化。

 
 
  1. window.addEventListener('hashchange'function() { 
  2.         routeHandle(locaiton.hash); 
  3.     });  

这个小型的路由设计可以参见我的github.

稍微总结下:

上面主要介绍了history提供的一些API,hash的相关知识。在平时可以运用到SPA当中,Gmail就是通过hash来进行路由设计的。它相对于页面跳转来说:

  1. 页面只需要加载一次。后面的页面切换可以通过ajax去请求数据。页面体验更加流畅;
  2. 可以利用本地缓存,优化页面体验。在不同页面切换的过程中更加流畅;
  3. 可进行按需加载...

等等一些实用的好处吧。


作者:佚名

来源:51CTO

目录
打赏
0
0
0
0
16429
分享
相关文章
Ant Design Pro:设置哈希hash路由
Ant Design Pro:设置哈希hash路由
256 0
《vue2进阶篇:路由》第10章:vue-router,包括基础路由、嵌套路由、路由的query参数和params参数、命名路由、router-link的replace属性、编程式路由、缓存路由组件
《vue2进阶篇:路由》第10章:vue-router,包括基础路由、嵌套路由、路由的query参数和params参数、命名路由、router-link的replace属性、编程式路由、缓存路由组件
229 2
浅谈前端路由原理hash和history
该文章详细解析了前端路由的两种模式——Hash模式与History模式的工作原理及其实现方式,并通过实例代码展示了如何在实际项目中运用这两种路由模式。
Vue学习之--------路由的query、params参数、路由命名(3)(2022/9/5)
这篇文章详细介绍了Vue路由中的query参数、命名路由、params参数以及props配置的使用方式,并通过实际项目案例展示了它们在开发中的应用和测试结果,同时解释了`<router-link>`的`replace`属性如何影响浏览器历史记录。
Vue学习之--------路由的query、params参数、路由命名(3)(2022/9/5)
Vue 路由的hash模式和history模式有什么区别?
在Vue.js框架中,路由管理是单页面应用(SPA)不可或缺的功能。Vue 路由提供了两种模式:hash模式和history模式,这两种模式主要负责处理URL的变更而无需重新加载整个页面,实现前端路由的功能。
302 19
|
11月前
|
前端路由机制实现hash-history
前端路由机制实现hash-history
64 1
vue-router 中常用的 hash 和 history 路由模式实现原理
vue-router 中常用的 hash 和 history 路由模式实现原理
165 0

热门文章

最新文章