目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改的部分 DOM,来减少原先因为多页应用的页面跳转带来的巨量性能损耗。它们都有自己的典型路由解决方案,@angular/router、react-router、vue-router。
一般来说,这些路由插件总是提供两种不同方式的路由方式:Hash 和 History,有时也会提供非浏览器环境下的路由方式 Abstract。Hash 和 History 除了外观上的不同之外,还一个区别是:Hash 方式的状态保存需要另行传递,而 HTML5 History 原生提供了自定义状态传递的能力,我们可以直接利用其来传递信息。
Hash
相关 Api
Hash 方法是在路由中带有一个 #,主要原理是通过监听 # 后的 URL 路径标识符的更改而触发的浏览器 hashchange 事件,然后通过获取 location.hash 得到当前的路径标识符,再进行一些路由跳转的操作。
- location.href:返回完整的 URL
- location.hash:返回 URL 的锚部分
- location.pathname:返回 URL 路径名
- hashchange 事件:当 location.hash 发生改变时,将触发这个事件
比如访问一个路径 http://sherlocked93.club/base/#/page1,那么上面几个值分别为:
1 | # http://sherlocked93.club/base/#/page1 |
简单实现
1 | class RouterClass{ |
如果希望使用脚本来控制 Hash 路由的后退,可以将经历的路由记录下来,路由后退跳转的实现是对 location.hash 进行赋值。但是这样会引发重新引发 hashchange 事件,第二次进入 render 。所以我们需要增加一个标志位,来标明进入 render 方法是因为回退进入的还是用户跳转
1 | class RouterClass{ |
HTML5 History Api
相关 Api
HTML5 提供了一些路由操作的 Api,在此就列举一下常用 Api 和他们的作用,详情请参照 MDN historyAPI
- history.go(n):路由跳转,比如n为 2 是往前移动2个页面,n为 -2 是向后移动2个页面,n为0是刷新页面
- history.back():路由后退,相当于 history.go(-1)
- history.forward():路由前进,相当于 history.go(1)
- history.pushState():添加一条路由历史记录,如果设置跨域网址则报错
- history.replaceState():替换当前页在路由历史记录的信息
- popstate 事件:当活动的历史记录发生变化,就会触发 popstate 事件,在点击浏览器的前进后退按钮或者调用上面前三个方法的时候也会触发。
简单实现
在需要路由跳转的地方使用 history.pushState 来入栈并记录 cb,前进后退的时候监听 popstate 事件拿到之前传给 pushState 的参数并执行对应 cb。
1 | class RouterClass { |
小结
Hash 模式是使用 URL 的 Hash 来模拟一个完整的 URL,因此当 URL 改变的时候页面并不会重载。History 模式则会直接改变 URL,所以在路由跳转的时候会丢失一些地址信息,在刷新或直接访问路由地址的时候会匹配不到静态资源。因此需要在服务器上配置一些信息,让服务器增加一个覆盖所有情况的候选资源,避免浏览器‘404’页面的出现