5. How do HASH urls change

In hash mode, if you call this.$router.push or click the router-link tag, the hash of the route will change accordingly. When you click the browser back, the route will return to the previous hash path. Push (location, onComplete, onAbort). In the mode hash, Push of history is HashHistory in hash.js file. Push of HashHistory will first get current, i.e. route before jump, and then execute this.transitionto. After the page is rendered, pushHash(route.fullPath) is first called. If window.history.pushState is not supported, then window.location.hash = path is called to toggle the hash function. If history.pushState is supported, the getUrl(path) method is used to get the current path through window.location.href, and then the index of # through indexOf(‘#’). The purpose is to get the baseUrl before #, and finally return the passed path and baseUrl together into a complete path. That is, calling pushState passes in a final full path. PushState determines whether the current jump is replace. If replace is replaced, history.replaceState will eventually be called, otherwise history.pushState will be called. Replace replaces the current record in histroy’s history stack, and push adds the specified URL to the history stack. A -> B, currently in B, call push(c), and click a-> B -> C to return to B, and use replace to change the history stack to A -> C, and click back to return to A. In Safari, the history stack is limited to 100 in some cases, and if there is a problem, it jumps window.location[replace? ‘replace’ : ‘assign’](URL).

// src/util/push-state.js
export function pushState (url? : string, replace? : boolean) {
  saveScrollPosition()
  // try... catch the pushState call to get around Safari
  // DOM Exception 18 where it limits to 100 pushState calls
  const history = window.history
  try {
    if (replace) {
      // preserve existing history state as it could be overriden by the user
      const stateCopy = extend({}, history.state)
      stateCopy.key = getStateKey()
      history.replaceState(stateCopy, ' ', url)
    } else {
      history.pushState({ key: setStateKey(genStateKey()) }, ' ', url)
    }
  } catch (e) {
    window.location[replace ? 'replace' : 'assign'](url)
  }
}

export function replaceState (url? : string) {
  pushState(url, true)}Copy the code

When the browser’s back button is clicked, the view changes accordingly, so vue-Router helps us listen for this event. The init function is triggered when the root Vue instance triggers beforecreate, and the callback function setupListeners is triggered after history.transitionTo is triggered for the first jump. The setupListeners function executes history.setuplisteners () and, in Hash mode, corresponds to the setupListeners function of HashHistory, This function first adds a listener via window.addEventListener. The listener type listens for popState or Hashchange events, depending on the browser’s support for history. When we click the browser’s back button, Will trigger the callback function handleRoutingEvent, handleRoutingEvent function through the getHash () get to jump the hash value as a parameter, called transitionTo complete jump

// src/history/hash.js
export class HashHistory extends History {...// this is delayed until the app mounts
  // to avoid the hashchange listener being fired too early
  setupListeners () {
    ...
    const handleRoutingEvent = () = > {
      const current = this.current
      if(! ensureSlash()) {return
      }
      this.transitionTo(getHash(), route= > {
        if (supportsScroll) {
          handleScroll(this.router, route, current, true)}if(! supportsPushState) { replaceHash(route.fullPath) } }) }const eventType = supportsPushState ? 'popstate' : 'hashchange'
    window.addEventListener( eventType, handleRoutingEvent ) ... }}Copy the code

Vue-router will automatically add /#/ when we type http://localhost:8080. The ensureSlash function is executed when classHashHistory is initialized. EnsureSlash first fetches the hash via getHash, and returns an empty string if we type a URL with no # sign, The replaceHash(‘/’ + path) method is then used. The replaceHash takes getUrl(path) as an argument when it calls replaceState or window.location.replace. ${base}#${path} will help us concatenate the initial URL with a # sign, In addition, this section uses replaceState or window.location.replace. This also prevents us from entering the url without # as the history stack

// src/history/hash.js
export class HashHistory extends History {
  constructor (router: Router, base: ? string, fallback: boolean) {
    super(router, base)
    // check history fallback deeplinking
    if (fallback && checkFallback(this.base)) {
      return} ensureSlash() } ... ensureURL (push? : boolean) {const current = this.current.fullPath
    if (getHash() !== current) {
      push ? pushHash(current) : replaceHash(current)
    }
  }
  ...
}
...
function ensureSlash () :boolean {
  const path = getHash()
  if (path.charAt(0) = = ='/') {
    return true
  }
  replaceHash('/' + path)
  return false
}

export function getHash () :string {
  // We can't use window.location.hash here because it's not
  // consistent across browsers - Firefox will pre-decode it!
  let href = window.location.href
  const index = href.indexOf(The '#')
  // empty path
  if (index < 0) return ' '
  href = href.slice(index + 1)
  return href
}

function getUrl (path) {
  const href = window.location.href
  const i = href.indexOf(The '#')
  const base = i >= 0 ? href.slice(0, i) : href
  return `${base}#${path}`
}

function pushHash (path) {
  if (supportsPushState) {
    pushState(getUrl(path))
  } else {
    window.location.hash = path
  }
}

function replaceHash (path) {
  if (supportsPushState) {
    replaceState(getUrl(path))
  } else {
    window.location.replace(getUrl(path))
  }
}
Copy the code