functionuseHistoryStateNavigation(base: string){const{ history, location }= window
// 始终指向当前页面constcurrentLocation: ValueContainer<HistoryLocation>={// 返回去掉base路径后的loaction的urlvalue:createCurrentLocation(base, location),}// 当前页面存储的状态consthistoryState: ValueContainer<StateEntry>={value: history.state }// 初始化页面路由信息if(!historyState.value){changeLocation(
currentLocation.value,{back:null,current: currentLocation.value,forward:null,// the length is off by one, we need to decrease itposition: history.length -1,replaced:true,// don't add a scroll as the user may have an anchor, and we want// scrollBehavior to be triggered without a saved positionscroll:null,},true)}//...return{location: currentLocation,// 始终指向当前页面地址state: historyState,// 始终指向当前页面的状态
push,
replace,}}
changeLocation 改变页面路由,最终页面路径的改变最后都是调用的该方法
通过调用浏览器的history.pushState|replaceState两个API改变路由路径
如果出错则通过location.replace|assign直接刷新页面
functionchangeLocation(to: HistoryLocation,state: StateEntry,replace: boolean):void{const hashIndex = base.indexOf('#')// 拼接完整的urlconst url =
hashIndex >-1?(location.host && document.querySelector('base')? base
: base.slice(hashIndex))+ to
:createBaseLocation()+ base + to //createBaseLocation:通过window.location拼接的域名try{// 调用浏览器相关API
history[replace ?'replaceState':'pushState'](state,'', url)
historyState.value = state
}catch(err){if(__DEV__){warn('Error with push/replace State', err)}else{
console.error(err)}// Force the navigation, this also resets the call count// location.assign() 方法会触发窗口加载并显示指定的 URL 的内容。
location[replace ?'replace':'assign'](url)}}
functionreplace(to: HistoryLocation, data?: HistoryState){conststate: StateEntry =assign({},
history.state,// 将传入的参数变成对象返回buildState(
historyState.value.back,// keep back and forward entries but override current position
to,
historyState.value.forward,true//调用replace),
data,{position: historyState.value.position })changeLocation(to, state,true)
currentLocation.value = to
}
exportfunctioncreateWebHashHistory(base?: string): RouterHistory {
base = location.host ? base || location.pathname + location.search :''// 强制为base基路径添加#,使得后续push时不用再添加#if(!base.includes('#')) base +='#'if(__DEV__ &&!base.endsWith('#/')&&!base.endsWith('#')){warn(`A hash base must end with a "#":\n"${base}" should be "${base.replace(/#.*$/,'#')}".`)}// 其他和history路由方式一致,调用浏览器的history API去更改hashreturncreateWebHistory(base)}
exportfunctioncreateMemoryHistory(base: string =''): RouterHistory {letlisteners: NavigationCallback[]=[]letqueue: HistoryLocation[]=[START]letposition: number =0
base =normalizeBase(base)constrouterHistory: RouterHistory ={// rewritten by Object.definePropertylocation:START,// TODO: should be kept in queuestate:{},
base,createHref:createHref.bind(null, base),replace(to){
queue.splice(position--,1)// 替换栈末尾记录,因为setLocation中会让position+1,所以这里要先-1setLocation(to)},push(to, data?: HistoryState){setLocation(to).// 数组末尾添加记录},listen(callback){// 添加回调,这里用于go API
listeners.push(callback)return()=>{const index = listeners.indexOf(callback)if(index >-1) listeners.splice(index,1)}},destroy(){
listeners =[]
queue =[START]
position =0},go(delta, shouldTrigger =true){// 这里只是更改postion和获取跳转相关记录上下文,实际跳转是setupListeners中添加的回调方法const from =this.location
constdirection: NavigationDirection =// we are considering delta === 0 going forward, but in abstract mode// using 0 for the delta doesn't make sense like it does in html5 where// it reloads the page
delta <0? NavigationDirection.back : NavigationDirection.forward
// 确保 0 < position < queue.length-1
position = Math.max(0, Math.min(position + delta, queue.length -1))if(shouldTrigger){triggerListeners(this.location, from,{// 触发setupListeners中添加的回调
direction,
delta,})}},}// 最终改变路由栈的方法functionsetLocation(location: HistoryLocation){
position++if(position === queue.length){// we are at the end, we can simply append a new entry
queue.push(location)}else{// we are in the middle, we remove everything from here in the queue// 删除postions及之后的所有记录
queue.splice(position)
queue.push(location)}}// ...return routerHistory
}