// Some code
const { location, history } = window
const createHistoryStateStore = (state = location.href)=>{
const getState = ()=>{
if (listenerSet.size === 0) throw new Error('should subscribe before getState')
return state
}
const setState = (nextState, shouldPushState) =>{ //新路由和状态
if (typeof (nextState) !== 'string') throw new Error(`non-string href: ${nextState}`)
if (nextState === state) return state //新路由和当前路由一样 不做处理
console.log('do set nextState:',nextState, 'shouldPushState:',shouldPushState,'state:',state)
const prevState = state //把当前存储的路由状态当做旧路由
state = nextState//传入的新路由为当前路由状态
shouldPushState !== 'skip' && history.pushState(null, '', state) //判断操作性质,决定是否需要手动更新histroy栈
listenerSet.forEach((listener) => listener(state, prevState)) //执行路由变更处理逻辑
return state
}
const listenerPopState = () => { setState(location.href, 'skip') } //原生浏览器操作时才触发
const listenerHashChange = () => { setState(location.href) } // js调用history.pushState()或history.replaceState() 或者直接改地址框地址 记录
const listenerSet = new Set() //存储路由变更时执行逻辑的函数集合
const subscribe = (listener) => { //绑定事件监听
listenerSet.add(listener) //存储处理函数
if (listenerSet.size !== 1) return //如果以前已经添加过监听处理,无需重复绑定
window.addEventListener('popstate', listenerPopState) //原生流量器操作
window.addEventListener('hashchange', listenerHashChange) // js操作
}
const unsubscribe = (listener) => {
listenerSet.delete(listener)// 删除存储处理函数
if (listenerSet.size !== 0) return //如果删除某个处理函数后仍存在其他处理逻辑则不需解绑
window.removeEventListener('popstate', listenerPopState)
window.removeEventListener('hashchange', listenerHashChange)
}
return { getState, setState, subscribe, unsubscribe }
}
export { createHistoryStateStore }
// e.g:
var his = createHistoryStateStore()
his.subscribe((state, prevState)=>{ console.log('state:',state, 'prevState:',prevState)})
popstate
当活动历史记录条目更改时,将触发popstate事件。如果被激活的历史记录条目是通过对history.pushState()的调用创建的,或者受到对history.replaceState()的调用的影响,popstate事件的state属性包含历史条目的状态对象的副本。
需要注意的是调用history.pushState()
或history.replaceState()
不会触发popstate
事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back()
或者history.forward()
方法)
不同的浏览器在加载页面时处理popstate
事件的形式存在差异。页面加载时Chrome和Safari通常会触发(emit )popstate
事件,但Firefox则不会
History.pushState()
history.pushState(state, title,[ url])