HTML5History

class HTML5History extends History{
  _startLocation;
  constructor(router,base){
    super(router,base)
    // Get the initial HTML5 route
    this._startLocation = getLocation(this.base)
  }
  setupListeners(){
    if(this.listeners.lenth > 0) {return
    }
    // Current routing instance
    const router = this.router
    // The rolling behavior of route switching
    const expectScroll = router.options.scrollBehavior
    // Check whether historyPushState is supported and whether the scroll behavior exists
    const supportsScroll = supportsPushState && expectScroll 
    if(supportScroll){
      // Record the current scroll position
      this.listeners.push(setupScroll())
    }
    const handleRoutingEvent = () = > {
      const current = this.current
      const location = getLocation(this.base)
      // The current route is in the initial state
      if(this.current === START && location === this._startLocation){
        return
      }
      // Redirect route
      this.transitionTo(location,route= > {
        // Scroll after jump
        if(supportScroll){
          handleScroll(router,route,current,true)}}}window.addEventListener('popstate',handleRoutingEVent)
    this.listeners.push(() = > {
      window.removeEventListener('popstate',handleRoutingEvent)
    })
  }
  // Perform the jump
  go(n){
    window.history.go(n)
  }
  push(location,onComplete,onAbort){
    const { current: fromRoute } = this
    // Perform the jump
    this.transitionTo(location,route= > {
      // Update the browsing history
      pushState(cleanPath(this.base+ route.fullPath))
      // Perform the scroll
      handleScroll(this.router,route,fromRoute,false)
      onComplete && onComplete(route)
    },onAbort)
  }
  replace(location,onComplete,onAbort){
    const { current: fromRoute } = this
    // Perform the jump
    this.transitionTo(location,route= > {
      // Replace the browsing history
      replaceState(cleanPath(this.base + route.fullPath))
      // Perform the scroll
      handleScroll(this.router,route,fromRoute,false)
      onComplete && onComplete(route)
    },onAbort)
  }
  ensureURL(push){
    // Check whether there is no jump to the current path
    if(getLocation(this.base) ! = =this.current.fullPath){
      const current = cleanPath(this.base+ this.current.fullPath)
      push ? pushState(current) : replaceState(current)
    }
  }
  // Get the current path
  getCurrentLocation(){
    return getLocation(this.base)
  }
}
Copy the code

setupScroll

function setupScroll(){
  // Use browser features to return to the scroll position of the previous page. Auto scroll manual does not scroll
  if('scrollRestoration' in window.history){
    window.history.scrollRestoration = 'manual'
  }
  // Get the domain name
  const protocolAndPath = window.location.protocol + '/ /' + window.location.host
  // Get the absolute path
  const absolutePath = window.location.href.replace(protocolAndPath,' ')
  // Copy the browser record
  const stateCopy = extend({},window.history.state)
  stateCopy.key = getStateKey()
  // Replace the browser record with the current link
  window.history.replaceState(stateCopy,' ',absolutePath)
  // Listen page changes trigger record scroll position
  window.addEvenetListener('popstate',handlePopState)
  return () = > {
    window.removeEventListener('popstate',handlePopState)
  }
}
Copy the code
getStateKey
const Time = inBrowser && window.performance && window.performance.now ? window.performance : Date
const  genStateKey = () = > Time.now().toFixed(3) // Get the current time
let _key = genStateKey()
const getStateKey = () = > _key // Get the current time as key
const setStateKey = (key) = > _key = key
Copy the code
handlePopState
function handlePopState(e){
  // Record the location
  saveScrollPosition()
  // Record the page time
  if(e.state && e.state.key){
    setStateKey(e.state.key)
  }
}
const positionStore = Object.create(null)
function saveScrollPosition(){
  // Record the scroll position of the current point in time
  const key = getStateKey()
  if(key){
    positionStore[key] = {x:window.pageXOffset,y:window.pageYOffset}
  }
}
Copy the code
handleScroll
function handleScroll(router,to,from,isProp){
  // No vue instance is mounted
  if(! router.app){return
  }
  // Determine that there is no scrolling behavior
  const behavior = router.options.scrollBehavior
  if(! behavior){return}...// The scrolling behavior must be a function
  router.app.$nextTick(() = > {
    // Get the current scroll position
    const position = getScrollPosition()
    // Returns the position to which you should scroll
    const shouldScroll = behavior.call(router,to,from,isProp ? position : null)
    if(! shouldScroll)return
    // Perform the scroll
    if(typeof shouldScroll.then === 'function'){
      shouldScroll.then(shouldScroll= > {
        scrollPosition(shouldScroll,position)
      }).catch(err= > {
        if(process.env.NODE_ENV ! = ='production'){
          assert(false,err.toString)
        }
      })
    }else{
      scrollPosition(shouldScroll,position)
    }
  })

}
Copy the code
scrollPosition
function scrollPosition(shouldScroll,position){
  const isObject = typeof shouldScroll === 'object'
  // Get the location
  if(isObject && typeof shouldScroll.selector === 'string') {/ / get the dom
    const el = hashStartsWithNumberRE.test(shouldScroll.selector) ? document.getElementById(shouldScroll.selector.slice(1)) : document.querySelector(shouldScroll.selector)
    if(el){
      let offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}
      offset = normalizeOffset(offset)
      position = getElementPosition(el,offset)
    }else if(isValidPosition(shouldScroll)){
      position = normalizePosition(shouldScroll)
    }
  }else if(isObject && isValidPosition(shouldScroll)){
    position = normalizePosition(shouldScroll)
  }
  // Perform the scroll behavior
  if(position){
    if('scrollBehavior' in document.documentElement.style){
      window.scrollTo({
        left: position.x,
        top: position.y,
        behavior: shouldScroll.behavior
      })
    }else{
      window.scrollTo(position.x,position.y)
    }
  }
}
Copy the code

getLocation

function getLocation(base){
  let path = window.location.pathname
  // Get a path other than base
  if(base && path.toLowerCase().indexOf(base.toLowerCase()) === 0){
    path = path.slice(base.length)
  }
  // Returns the current location
  return (path || '/') + window.location.search + window.location.hash
}
Copy the code

supportPushState

// Check whether the system version supports pushState
const supportPushStat = inBrowser && (function(){
  const ua = window.navigator.userAgent
  if( (ua.indexOf('Android 2.')! = = -1 || ua.indexOf('the Android 4.0')! = = -1)&& ua.indexOf('Mobile Safari')! = = -1 && ua.indexOf('chrome') = = = -1 && ua.indexOf('WindowsPhone') = = =)return false
  return window.history && typeof window.history.pushState === 'function'}) ()Copy the code
pushState
function pushState(url,replace){
  // Store the current scroll position
  saveScrollPosition()
  const history = window.history
  // Perform the jump
  try{
    if(replace){
      const stateCopy = extend({},history.state)
      stateCopy.key = getStateKey()
      // Replace the browsing record and change the record time
      histroy.replaceState(stateCopy,' ',url)
    }else{
      // Update the browsing history
      history.pushState({key:setStateKey(getStateKey())},' ',url)
    }
  }catch(e){
    window.location[replace ? 'replace' : 'assign'](url)
  }
}
Copy the code