9995636327f771ae9adfbce42d3b14dbb5055e998c11726edefcbd9216a364f23211c561d0556ed2e14c1f7885eb6a9cbd82591ce659087aca8f9708cd4384 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /* @flow */
  2. import type Router from '../index'
  3. import { History } from './base'
  4. import { cleanPath } from '../util/path'
  5. import { getLocation } from './html5'
  6. import { setupScroll, handleScroll } from '../util/scroll'
  7. import { pushState, replaceState, supportsPushState } from '../util/push-state'
  8. export class HashHistory extends History {
  9. constructor (router: Router, base: ?string, fallback: boolean) {
  10. super(router, base)
  11. // check history fallback deeplinking
  12. if (fallback && checkFallback(this.base)) {
  13. return
  14. }
  15. ensureSlash()
  16. }
  17. // this is delayed until the app mounts
  18. // to avoid the hashchange listener being fired too early
  19. setupListeners () {
  20. if (this.listeners.length > 0) {
  21. return
  22. }
  23. const router = this.router
  24. const expectScroll = router.options.scrollBehavior
  25. const supportsScroll = supportsPushState && expectScroll
  26. if (supportsScroll) {
  27. this.listeners.push(setupScroll())
  28. }
  29. const handleRoutingEvent = () => {
  30. const current = this.current
  31. if (!ensureSlash()) {
  32. return
  33. }
  34. this.transitionTo(getHash(), route => {
  35. if (supportsScroll) {
  36. handleScroll(this.router, route, current, true)
  37. }
  38. if (!supportsPushState) {
  39. replaceHash(route.fullPath)
  40. }
  41. })
  42. }
  43. const eventType = supportsPushState ? 'popstate' : 'hashchange'
  44. window.addEventListener(
  45. eventType,
  46. handleRoutingEvent
  47. )
  48. this.listeners.push(() => {
  49. window.removeEventListener(eventType, handleRoutingEvent)
  50. })
  51. }
  52. push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  53. const { current: fromRoute } = this
  54. this.transitionTo(
  55. location,
  56. route => {
  57. pushHash(route.fullPath)
  58. handleScroll(this.router, route, fromRoute, false)
  59. onComplete && onComplete(route)
  60. },
  61. onAbort
  62. )
  63. }
  64. replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  65. const { current: fromRoute } = this
  66. this.transitionTo(
  67. location,
  68. route => {
  69. replaceHash(route.fullPath)
  70. handleScroll(this.router, route, fromRoute, false)
  71. onComplete && onComplete(route)
  72. },
  73. onAbort
  74. )
  75. }
  76. go (n: number) {
  77. window.history.go(n)
  78. }
  79. ensureURL (push?: boolean) {
  80. const current = this.current.fullPath
  81. if (getHash() !== current) {
  82. push ? pushHash(current) : replaceHash(current)
  83. }
  84. }
  85. getCurrentLocation () {
  86. return getHash()
  87. }
  88. }
  89. function checkFallback (base) {
  90. const location = getLocation(base)
  91. if (!/^\/#/.test(location)) {
  92. window.location.replace(cleanPath(base + '/#' + location))
  93. return true
  94. }
  95. }
  96. function ensureSlash (): boolean {
  97. const path = getHash()
  98. if (path.charAt(0) === '/') {
  99. return true
  100. }
  101. replaceHash('/' + path)
  102. return false
  103. }
  104. export function getHash (): string {
  105. // We can't use window.location.hash here because it's not
  106. // consistent across browsers - Firefox will pre-decode it!
  107. let href = window.location.href
  108. const index = href.indexOf('#')
  109. // empty path
  110. if (index < 0) return ''
  111. href = href.slice(index + 1)
  112. return href
  113. }
  114. function getUrl (path) {
  115. const href = window.location.href
  116. const i = href.indexOf('#')
  117. const base = i >= 0 ? href.slice(0, i) : href
  118. return `${base}#${path}`
  119. }
  120. function pushHash (path) {
  121. if (supportsPushState) {
  122. pushState(getUrl(path))
  123. } else {
  124. window.location.hash = path
  125. }
  126. }
  127. function replaceHash (path) {
  128. if (supportsPushState) {
  129. replaceState(getUrl(path))
  130. } else {
  131. window.location.replace(getUrl(path))
  132. }
  133. }