Vue-Router源码解析:hashRouter

本文详细介绍了 Vue Router 的 hash 模式工作原理,包括如何利用 HTML5 的 history 对象及 hashchange 事件实现页面跳转而不刷新,以及 push 和 replaceState 方法的具体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

回顾

如果有小伙伴想要回顾之前的知识的话,点击下面的链接查看之前所有的源码分析流程👇
《Vue-Router源码解析:目录》

hashRouter

VueRouter有两种模式可以选择(其实有3种,哈哈),hashhistory。其实两者的实现原理都是差不多的,我们这里挑hash来讲解吧。
在不使用路由系统的网页中,如果要跳转就必须要改变url并且刷新页面。但是我们在使用路由系统的网页中(React-Router-Dom也是一样),不难发现,虽然url变化了,但是页面却没有刷新,这是怎么做到的?

在html5中有一个对象history,它有三个方法,history.pushState()history.back()history.go(),这三个方法都可以做到改变URL且不刷新页面。那VueRouter是怎么知道url被改变了呢?相对应的还有两个监听器popstatehashchange,可以来监听url的变化。其中hash router就是使用了hashchange这个监听器。

我们先来看看hash router的构造器

constructor

  constructor (router: Router, base: ?string, fallback: boolean) {
    super(router, base)
    // check history fallback deeplinking
    if (fallback && checkFallback(this.base)) {
      return
    }
    ensureSlash()
  }

这里使用了一个ensureSlash方法,进入。

ensureSlash

function ensureSlash (): boolean {
  const path = getHash()
  if (path.charAt(0) === '/') {
    return true
  }
  replaceHash('/' + path)
  return false
}

首先使用getHash获取一个path,进入getHash

getHash

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('#')
  // empty path
  if (index < 0) return ''

  href = href.slice(index + 1)

  return href
}

由于火狐浏览器一些兼容性的问题,不能直接使用window.location.hash来获取hash部分的path,这里VueRouter使用了原生的字符串截取的方法获取hash部分的path。我们以一开始输入浏览器的地址为:https://www.abc.com/为例,这里getHash后的值是:'',一个空字符串。

回到ensureSlash

function ensureSlash (): boolean {
  const path = getHash()
  if (path.charAt(0) === '/') {
    return true
  }
  replaceHash('/' + path)
  return false
}

由于path是空字符串,所以这边直接调用了replaceHash,进入。

replaceHash

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

一开始先检测浏览器对pushStateapi的兼容情况,如果兼容的话就会调用getUrl传入‘/’并调用replaceStategetUrl的结果传入replaceState的参数中。
先来看看getUrl

getUrl

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

这边的代码很简单,就是在https://www.abc.com/后边加上了#/,所以最后的结果是https://www.abc.com/#/

我们再来看看replaceState

replaceState

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

其实调用的是pushState只不过第二个参数传入的为true

pushState


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)
  }
}

由于参数replace传入的事true,所以最后调用的是window.history.replaceState这个API,这个API会改变当前URL,但是不会产生一条历史记录,也就是说你按浏览器的回退键是不能返回上一个页面的。

所以在hashRouter的构造器中的ensureSlash就是在你访问页面时如果没有带上#就自动帮你带上#

我们再来看看一个平时用的最多的API,push,看看hashRouter是怎么实现push的。

push

  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    const { current: fromRoute } = this
    this.transitionTo(
      location,
      route => {
        pushHash(route.fullPath)
        handleScroll(this.router, route, fromRoute, false)
        onComplete && onComplete(route)
      },
      onAbort
    )
  }

假设此时用户是这么调用的this.$router.push("/foo"),所以此时的location/foo

此时VueRouter会调用this.transitionTo去开始执行一些钩子函数,然后开始resolve组件,在完成这些之后会调用pushHash(route.fullPath)这个方法去改变url,其实pushHash最终调用的就是pushState,只不过replace传入的是false

总结

其实hashRouter的实现还是比较简单的,说白了就是HTML5的history对象,以及hashchange监听器。当然了,historyRouter实现也差不多,总体来说还是比较简单的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的前端小黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值