hash定义

hash这个玩意是地址栏上#及后面部分,代表网页中的一个位置,#后面部分为位置标识符。页面打开后,会自动滚动到指定位置处。

位置标识符 ,一是使用锚点,比如<a name="demo"></a>,二是使用id属性,比如 <span id="demo" ></span>

带hash的请求

当打开http://www.example.com/#print服务器实际收到的请求地址是http://www.example.com/,是不带hash值的。

那么你真想带#字符咋办,转义啊, #转义字符为%23。也许有人会说,我咋知道这个转义啊,呵呵哒encodeURIComponent。

hashchange事件

The HashChangeEvent interface可以看到hashchange事件的参数HashChangeEvent继承了Event,仅仅多了两个属性

  • oldURL 先前会话历史记录的URL
  • newURL 当前会话历史记录的URL

    简单的调用方式,

    ```js

    window.onhashchange = function(e){

    console.log('old URL:', e.oldURL)

    console.log('new URL', e.newURL)

    }

[hash | CAN I USE](https://caniuse.com/#search=hash) 上可以看到除了IE8一下和那个尴尬的Opera Mini,hashchange事件都是支持得很好。那么怎么做到兼容,用MDN的代码做个引子js

;(function(window) {

// exit if the browser implements that event

if ("onhashchange" in window) { return; }

var location = window.location,

oldURL = location.href,

oldHash = location.hash;

// check the location hash on a 100ms interval

setInterval(function() {

var newURL = location.href,

newHash = location.hash;

  1. // if the hash has changed and a handler has been bound...
  2. if (newHash != oldHash && typeof window.onhashchange === "function") {
  3. // execute the handler
  4. window.onhashchange({
  5. type: "hashchange",
  6. oldURL: oldURL,
  7. newURL: newURL
  8. });
  9. oldURL = newURL;
  10. oldHash = newHash;
  11. }

}, 100);

})(window);

```

hash history 简单版本实现

从上面可以得知,我们的实现思路就是监听hashchange事件,这里先抛开兼容性问题。

1 首先监听hashchange事件,定义个RouterManager函数

  • bind(this)让函数this指向RouterManager实例
  • 取到oldURL和newURL,同时查找一下是否注册,然后加载相关路由

    1. function RouterManager(list, index) {
    2. if (!(this instanceof RouterManager)) {
    3. return new RouterManager(arguments)
    4. }
    5. this.list = {} || list
    6. this.index = index
    7. this.pre = null
    8. this.current = null
    9. win.addEventListener('hashchange', function (ev) {
    10. var pre = ev.oldURL.split('#')[1],
    11. cur = ev.newURL.split('#')[1],
    12. preR = this.getByUrlOrName(pre),
    13. curR = this.getByUrlOrName(cur)
    14. this.loadWithRouter(curR, preR)
    15. }.bind(this))
    16. }

    2 定义添加,删除,加载,和初始化等方法

  • add的时候,判断是不是string, 如果是,重新构造一个新的router实例配置
  • load这里主要是用来还原直接输入带hash的地址,比如 http://ex.com/#music
  • loadWithRouter是最终渲染的入口
  • getByUrlOrName,你可以通过名字和path查找路由,name是方便日后扩展
  • setIndex设置默认路由地址
  • go, back, forward同history的方法
  • init里面会检测地址是不是带hash,然后走不通的逻辑。history.replaceState这是因为,如果不这么做, http://ex.com/跳转到http://ex.com/#/music会产生两条历史记录,这是我们不期望的。

    1. RouterManager.prototype = {
    2. add: function (router, callback) {
    3. if (typeof router === 'string') {
    4. router = {
    5. path: router,
    6. name: router,
    7. callback: callback
    8. }
    9. }
    10. this.list[router.name || router.path] = router
    11. },
    12. remove: function (name) {
    13. delete this.list[name]
    14. },
    15. get: function (name) {
    16. return this.getByUrlOrName(name)
    17. },
    18. load: function (name) {
    19. if (!name) {
    20. name = location.hash.slice(1)
    21. }
    22. var r = this.getByUrlOrName(name)
    23. this.loadWithRouter(r, null)
    24. },
    25. loadWithRouter(cur, pre) {
    26. if (cur && cur.callback) {
    27. this.pre = this.current || cur
    28. cur.callback(cur, pre)
    29. this.current = cur
    30. } else {
    31. this.NOTFOUND('未找到相关路由')
    32. }
    33. },
    34. getByUrlOrName: function (nameOrUrl) {
    35. var r = this.list[nameOrUrl]
    36. if (!r) {
    37. r = Object.values(this.list).find(rt => rt.name === nameOrUrl || rt.path === nameOrUrl)
    38. }
    39. return r
    40. },
    41. setIndex: function (nameOrUrl) {
    42. this.indexRouter = this.getByUrlOrName(nameOrUrl)
    43. },
    44. go: function (num) {
    45. win.history.go(num)
    46. },
    47. back: function () {
    48. win.history.back()
    49. },
    50. forward: function () {
    51. win.history.forward()
    52. },
    53. init: function () {
    54. // 直接输入是带hash的地址,还原
    55. if (win.location.hash) {
    56. /* 模拟事件
    57. var ev = document.createEvent('Event')
    58. ev.initEvent('hashchange', true, true)
    59. ev.oldURL = ev.newURL = location.href
    60. win.dispatchEvent(ev) */
    61. this.load()
    62. } else if (this.indexRouter) { // 是不带hash的地址,跳转到指定的首页
    63. if ('replaceState' in win.history) {
    64. // 替换地址
    65. win.history.replaceState(null, null, win.location.href + '#' + this.indexRouter.path)
    66. } else {
    67. win.location.hash = this.indexRouter.path
    68. }
    69. }
    70. }
    71. }

    3 公布函数

    1. RouterManager.prototype.use = RouterManager.prototype.add
    2. win.Router = RouterManager

4 页面怎么配置,简单的利用a标签href

  1. <ul>
  2. <li>
  3. <li>
  4. <a href="#/m1">菜单1</a>
  5. </li>
  6. <ul>
  7. <li>
  8. <a href="#/m11">菜单11</a>
  9. </li>
  10. <li>
  11. <a href="#/m12">菜单12</a>
  12. </li>
  13. </ul>
  14. </li>
  15. <li>
  16. <a href="#/m2">菜单2</a>
  17. </li>
  18. <li>
  19. <a href="#/m3">菜单3</a>
  20. </li>
  21. </ul>

5 注册,当然你也可以通过选择器批量注册

  1. var router = new Router()
  2. router.NOTFOUND = function (msg) {
  3. content.innerHTML = msg
  4. }
  5. router.use('/m1', function (r) {
  6. req(r.path.slice(1))
  7. })
  8. router.use('/m11', function (r) {
  9. req(r.path.slice(1))
  10. })
  11. router.use('/m12', function (r) {
  12. req(r.path.slice(1))
  13. })
  14. router.use('/m2', function (r) {
  15. req(r.path.slice(1))
  16. })
  17. router.use('/m3', function (r) {
  18. req(r.path.slice(1))
  19. })
  20. router.setIndex('/m1')
  21. router.init()

为了方便演示,定义req,ajax方法,模拟ajax请求

  1. function req(url) {
  2. ajax(url, function (res) {
  3. content.innerHTML = res
  4. })
  5. }
  6. function ajax(id, callback) {
  7. callback(
  8. {
  9. 'm1': '菜单1的主区域内容',
  10. 'm11': '菜单11的主区域内容',
  11. 'm12': '菜单12的主区域内容',
  12. 'm2': '菜单2的主区域内容',
  13. 'm3': '菜单3的主区域内容'
  14. }[id] || '404 Not Found!')
  15. }

6 demo地址

hash-Router1.0

7 源码地址

简单的前端hash路由

8 下一步

这就成了最简单最基本的路由了。让然还有很多要考虑,比如如下

  1. 动态路由匹配
  2. 嵌套路由
  3. 重定向和别名
  4. 错误捕捉
  5. 生命周期钩子
  6. 等等等

hash | CAN I USE

The HashChangeEvent interface

onhashchange | MDN

window.location.hash 使用说明

JS单页面应用实现前端路由(hash)

Ajax保留浏览器历史的两种解决方案(Hash&Pjax)

理解浏览器的历史记录

理解浏览器历史记录(2)-hashchange、pushState

Web开发中 前端路由 实现的几种方式和适用场景

自己动手写一个前端路由插件

vue-router

react-router

简单的基于hash和hashchange的前端路由的更多相关文章

  1. SPA中前端路由基本原理与实现方式

    SPA 前端路由原理与实现方式 通常 SPA 中前端路由有2中实现方式,本文会简单快速总结这两种方法及其实现: 修改 url 中 Hash 利用 H5 中的 history Hash 我们都知道 ur ...

  2. 基于hash和pushState的网页前端路由实现

    客户端路由 对于客户端(通常为浏览器)来说,路由的映射函数通常是进行一些DOM的显示和隐藏操作.这样,当访问不同的路径的时候,会显示不同的页面组件.客户端路由最常见的有以下两种实现方案:* 基于Has ...

  3. 前端路由hash、history原理及简单的实践下

    阅读目录 一:什么是路由?前端有哪些路由?他们有哪些特性? 二:如何实现简单的hash路由? 三:如何实现简单的history路由? 四:hash和history路由一起实现 回到顶部 一:什么是路由 ...

  4. 前端路由以及浏览器回退,hash & history & location

    一.前言 其实不止一次想监听浏览器的回退方法,比如 在 list.html 页滚动加载了几页列表,点到 detail.html 看详情,反回来时又得重新加载几页 H5 有背景音乐的,跳页就得重新放,体 ...

  5. 前端路由两种模式:hash、history

    随着 ajax 的使用越来越广泛,前端的页面逻辑开始变得越来越复杂,特别是spa的兴起,前端路由系统随之开始流行. 从用户的角度看,前端路由主要实现了两个功能(使用ajax更新页面状态的情况下): 记 ...

  6. 前端路由的两种模式: hash 模式和 history 模式

    随着 ajax 的使用越来越广泛,前端的页面逻辑开始变得越来越复杂,特别是spa的兴起,前端路由系统随之开始流行. 从用户的角度看,前端路由主要实现了两个功能(使用ajax更新页面状态的情况下): 记 ...

  7. 前端路由的两种模式:hash(#)模式和history模式(转)

    随着 ajax 的使用越来越广泛,前端的页面逻辑开始变得越来越复杂,特别是spa的兴起,前端路由系统随之开始流行. 从用户的角度看,前端路由主要实现了两个功能(使用ajax更新页面状态的情况下): 记 ...

  8. 从零开始搭建一个简单的基于webpack的vue开发环境

    原文地址:https://segmentfault.com/a/1190000012789253?utm_source=tag-newest 从零开始搭建一个简单的基于webpack的react开发环 ...

  9. 前端路由原理之 hash 模式和 history 模式

    什么是路由? 个人理解路由就是浏览器 URL 和页面内容的一种映射关系. 比如你看到我这篇博客,博客的链接是一个 URL,而 URL 对应的就是我这篇博客的网页内容,这二者之间的映射关系就是路由. 其 ...

随机推荐

  1. IDEA+PHP+XDebug调试配置

    XDebug调试配置 临时需要调试服务器上的PHP web程序,因此安装xdebug,下面简单记录 安装xdebug 下载最新并解压 wget https://xdebug.org/files/xde ...

  2. C#:MVC引用Log4Net生成错误日志

    第一步:引用log4net配置文件 第二步:在自己项目下新建文件夹LogNet,再在里面建立类Log.cs log.cs内容如下: 第三步:在自己项目下新建Log4Net.config Log4Net ...

  3. 微信小程序各类型的自定义组件篇

    由于本人最近在开发小程序项目,期间对小程序有花点时间去研究,同时也找了网上大牛的一些案例,在这里分享部分自定义组件,部分代码是copy大牛案例的,有对小程序有兴趣的伙伴拿走,不谢! 源码下载地址:ht ...

  4. 快看Sample代码,速学Swift语言(2)-基础介绍

    Swift语言是一个新的编程语言,用于iOS, macOS, watchOS, 和 tvOS的开发,不过Swift很多部分内容,我们可以从C或者Objective-C的开发经验获得一种熟悉感.Swif ...

  5. 《重构——改善既有代码的设计》【PDF】下载

    <重构--改善既有代码的设计>[PDF]下载链接: https://u253469.ctfile.com/fs/253469-231196358 编辑推荐 重构,一言以蔽之,就是在不改变外 ...

  6. 使用angularjs实现注册表单

    本文是在学习angularjs过程中做的相应的练习 github地址 https://github.com/2016Messi/angularjs1.6-form 演示地址 https://2016m ...

  7. Spring框架中ModelAndView、Model、ModelMap区别

    原文地址:http://www.cnblogs.com/google4y/p/3421017.html SPRING框架中ModelAndView.Model.ModelMap区别   注意:如果方法 ...

  8. windos10安装mongodb并配置

    想了想还是把这个写上吧,毕竟网上的教程有不少坑的. 首先下载mongodb,如果你嫌官网慢,那么你可以去我的百度云下载 链接:http://pan.baidu.com/s/1pKEWTBX 密码:v3 ...

  9. Function:html结构转字符串形式显示

    //Html结构转字符串形式显示 支持<br>换行 function ToHtmlString(htmlStr) { return toTXT(htmlStr).replace(/\&am ...

  10. androidSD卡操作

    1.获取SD卡目录:File file = Environment.getExternalStorageDirectory(); 2.获取SD卡路径:String path = Environment ...