Hash与History的区别

2,719 阅读4分钟

hash模式

hash就是指url尾巴后的#号以及后面的字符,由于hash值的变化不会导致浏览器向服务器发出请求,而且hash改变会触发onhashchange事件。

hash的特点:

hash虽然出现url中,但不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。hash本来拿来做页面定位的,如果拿来做路由的话,原来锚点功能就不能用了。hash模式又叫做前端路由,因为改变hash值不会向后端发送请求

  window.location.hash='qq'//设置 url 的 hash,会在当前url后加上'#qq'

var hash = window.location.hash //'#qq'

window.addEventListener('hashchange',function(){
  //监听hash变化,点击浏览器的前进后退会触发
})

history模式

history不会在URL中出现#号,与HTTP保持一致以/开头,所以当history发生改变时会重新加载页面。

想要不刷新页面的改变路由history提供了两个APLI(利用pushState()和replaceState()方法来修改pathame,不会刷新页面)--这个两个方法接收三个参数;stateObj,title,url

history模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中。

    history也可以直接改变URL但是会刷新页面
    widow.location.pathname ='/first'   //设置URL的history值为‘/first’
    
var history = window.location.history //'/first'
    
history API
  1. 切换历史状态包括back()后退、foward()前进、go()跳转,会在浏览记录中前进后退,如果超出,会使调用的方法无效果
    history.back();      /后退一次
    history。forward();  //前进一次
    history.go();       //刷新当前页面
    history.go(-2);       //后退两次
    history.go(2);       //前进两次
注意:移动到以前访问过的页面时,页面通常是从浏览器缓存中加载,而不是重新要求服务器发送新的网页。

2. 修改历史状态包括pushState,replaceState

    window.history.puahState(stateObj,title,url);
    //state: 需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
    //title:标题,现在基本上没用,一般传null或者”“
    //url:设定新的历史记录的url,新的url与当前url的origin必须是一样的(同一个域),否则会出错误,浏览器地址栏会显示这个新网址
    
    实例1
    如果当前网址为www.com/first,用history.pushState()方法在浏览历史中添加一个新记录
    window.history.pushState({first;1},null,'second');
    浏览器地址栏会立刻显示为www.com/second,但是不会跳转到second页面,甚至不会检查second是否存在,当访问一个新地址(baidu.com),回退一次页面url将显示second,再退一次会显示first
    
    实例2
    如果当前url是www.com/a/
    window.history.pushState(null,null,'./qq/')
    url会变成:www.com/a/qq/  //前面一个路由必须以”/“结尾,否则不会拼接
    window.history.replaceState(stateObj,title,url);
    //与pushState参数基本相同,但replaceState()是修改当前历史记录,而pushState()是创建新的历史记录
    
    实例1
    如果当前网页是:www.com
    
    history.pushState(null,null,'/html.1');
    //www.com/html.1
    history.pushState(null,null,'/html.2');
    //www.com/html.2
    history.replaceState(null,null,'html.3');
    //www.com/html.3   replaceState会替换刚刚那个pathname
    
    history.back();  //后退一次
    //www.com/html.1
    history.back();  //后退一次
    //www.com
    history.go(2)   //前进两次
    //www.com/html.3
  1. popstate事件

当浏览历史(history对象)出现变化时,就会触发popstate事件,但pushState()方法和replaceState()方法不会触发该事件,只有当用户点击浏览器倒退和前进按钮,或者使用history.back()、history.forward()、history.go()方法时才会触发。该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。

    window.addEventListener('popstate',cunction(){
       console.log(111);
    })
    
    history.back():   //111

监听history的变化

其实大多时候,我们都想改变URL且不让浏览器发出请求,但在history里面,调用pushState()和replaceState()会改URL且不刷新页面,但是没有监听的他们的方法,就不能监听到路由的变化执行相应的js。

history不像hash那样有onhashchange监听,所以我们可以自己写个监听事件,先完成一个订阅-发布模式,然后重写history.puahState和history.replaceState并添加消息通知。

    class Store {            //订阅池
  constructor(name) {
    this.store = []     //该事件下被订阅的对象集合
  }
  bind() {             //添加订阅者
    Store.watch.add(this)
  }
  dispatch() {        //通知订阅者有变化
    this.store.forEach((item) => {
      if (typeof item.update === 'function') {   //判断store对象里面对应事件是否是个方法
        item.update.apply(item)     //触发订阅者更新函数
      }
    })
  }
}
Store.watch = null;         //创建一个空状态的订阅者

class Watch {
  constructor(name, fn) {
    this.name = name;      //订阅消息的名称
    this.callBack = fn;    //订阅消息发生改变时->订阅者执行的回调函数
  }
  add(dep) {              //将订阅者放入订阅池
    dep.store.push(this);
  }
  update() {              //将订阅者更新方法
    var cb = this.callBack;//赋值为了不改变函数内调用的this
    cb(this.name);
  }
}

//改写pushState和replaceState方法
var addHistoryMethod = (function () {   //自执行函数返回return的结果,如果是函数就返回函数
  var historyStore = new Store();
  return function (name) {              //通过historyMethod()传入一个name
    if (name === 'historychange') {     //判断调用了什么方法,又返回一个函数
      return function (name, fn) {
        var event = new Watch(name, fn) //把Watch实例给event
        Store.watch = event;            //把把实例给Store.watch以便在bind里面调用add方法
        historyStore.bind();
        Store.watch = null;            //置空供下一个订阅者使用
      }
    } else if (name === 'pushState' || name === 'replaceState') {
      var method = window.history[name]; //history.name
      return function () {
        method.apply(window.history, arguments);//history表示指向的this为history,arguments获取传递过来的实参
        historyStore.dispatch();
      }
    }
  }
}())

//设置监听样式
window.addHistoryListener = addHistoryMethod('historychange');
window.history.pushState = addHistoryMethod('pushState');
window.history.replaceState = addHistoryMethod('replaceState');

调用监听history

    window.history.pushState(null,null,'/second')
    window.addHistoryListener('history',function() {
        console.log('history改变了')
    })