muwoo / blogs

📚一个前端的博客。
2.32k stars 352 forks source link

vue-router 实现 -- HTML5History #26

Open muwoo opened 6 years ago

muwoo commented 6 years ago

vue-router通过设置mode = history可以在浏览器支持 history 模式的情况下,用来开启 HTML5History 模式。我们知道在install挂载的时候,会在beforeCreate钩子内执行init方法:

init () {
    // ...
    if (history instanceof HTML5History) {
      history.transitionTo(history.getCurrentLocation())
    }
    // ...
}

当判断当前模式是 HTML5History的时候,会执行 history 对象上的 transitionTo方法。接下来我们主要分析 HTML5History 的主要功能。

constructor

vue-router实例化过程中,执行对 HTML5History 的实例化:

this.history = new HTML5History(this, options.base)

此时会执行 HTML5History 中的 constructor:

  constructor (router: Router, base: ?string) {
    // 实现 base 基类中的构造函数
    super(router, base)

    // 滚动信息处理
    const expectScroll = router.options.scrollBehavior
    const supportsScroll = supportsPushState && expectScroll

    if (supportsScroll) {
      setupScroll()
    }

    const initLocation = getLocation(this.base)
    window.addEventListener('popstate', e => {
      const current = this.current

      // 避免在有的浏览器中第一次加载路由就会触发 `popstate` 事件
      const location = getLocation(this.base)
      if (this.current === START && location === initLocation) {
        return
      }
      // 执行跳转动作
      this.transitionTo(location, route => {
        if (supportsScroll) {
          handleScroll(router, route, current, true)
        }
      })
    })
  }

可以看到在这种模式下,初始化作的工作相比 hash 模式少了很多,只是调用基类构造函数以及初始化监听事件,不需要再做额外的工作。由于在上篇文章中已经介绍了 transitionToconfirmTransition。这里不再过多介绍了。到这里好像也就没什么,那么我们来看几个之前没介绍的一下 API 吧

一些API

vue-router中我们通常会通过这种方式操作路由信息:

router.push(location, onComplete?, onAbort?)
router.replace(location, onComplete?, onAbort?)
router.go(n)
router.back()
router.forward()

具体的可以参考这里编程式导航 来一起看一下 vue-router 的统一实现:

  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.history.push(location, onComplete, onAbort)
  }

  replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.history.replace(location, onComplete, onAbort)
  }

  go (n: number) {
    this.history.go(n)
  }

  back () {
    this.go(-1)
  }

  forward () {
    this.go(1)
  }

可以看到,也都是调用了history内部的方法。

你也许注意到 router.pushrouter.replacerouter.gowindow.history.pushStatewindow.history.replaceStatewindow.history.go好像, 实际上它们确实是效仿 window.history API 的。 因此,如果你已经熟悉 Browser History APIs,那么在 Vue Router 中操作 history 就是超级简单的。 还有值得提及的,Vue Router 的导航方法 (push、 replace、 go) 在各类路由模式 (history、 hash 和 abstract) 下表现一致。

举个例子🌰:

// go
go (n: number) {
  window.history.go(n)
}

// push最终也是调用的history API
history.pushState({ key: _key }, '', url)