9d4dc94e66a7123bacf7ee682da144e9e773cc6b73aeb2fcd11e9091f431ed31cb4b209666f563b0becba43a838ad3621e2bb5d42dacb803907f51714e0d0a 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /* @flow */
  2. import type VueRouter from './index'
  3. import { resolvePath } from './util/path'
  4. import { assert, warn } from './util/warn'
  5. import { createRoute } from './util/route'
  6. import { fillParams } from './util/params'
  7. import { createRouteMap } from './create-route-map'
  8. import { normalizeLocation } from './util/location'
  9. import { decode } from './util/query'
  10. export type Matcher = {
  11. match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route;
  12. addRoutes: (routes: Array<RouteConfig>) => void;
  13. };
  14. export function createMatcher (
  15. routes: Array<RouteConfig>,
  16. router: VueRouter
  17. ): Matcher {
  18. const { pathList, pathMap, nameMap } = createRouteMap(routes)
  19. function addRoutes (routes) {
  20. createRouteMap(routes, pathList, pathMap, nameMap)
  21. }
  22. function match (
  23. raw: RawLocation,
  24. currentRoute?: Route,
  25. redirectedFrom?: Location
  26. ): Route {
  27. const location = normalizeLocation(raw, currentRoute, false, router)
  28. const { name } = location
  29. if (name) {
  30. const record = nameMap[name]
  31. if (process.env.NODE_ENV !== 'production') {
  32. warn(record, `Route with name '${name}' does not exist`)
  33. }
  34. if (!record) return _createRoute(null, location)
  35. const paramNames = record.regex.keys
  36. .filter(key => !key.optional)
  37. .map(key => key.name)
  38. if (typeof location.params !== 'object') {
  39. location.params = {}
  40. }
  41. if (currentRoute && typeof currentRoute.params === 'object') {
  42. for (const key in currentRoute.params) {
  43. if (!(key in location.params) && paramNames.indexOf(key) > -1) {
  44. location.params[key] = currentRoute.params[key]
  45. }
  46. }
  47. }
  48. location.path = fillParams(record.path, location.params, `named route "${name}"`)
  49. return _createRoute(record, location, redirectedFrom)
  50. } else if (location.path) {
  51. location.params = {}
  52. for (let i = 0; i < pathList.length; i++) {
  53. const path = pathList[i]
  54. const record = pathMap[path]
  55. if (matchRoute(record.regex, location.path, location.params)) {
  56. return _createRoute(record, location, redirectedFrom)
  57. }
  58. }
  59. }
  60. // no match
  61. return _createRoute(null, location)
  62. }
  63. function redirect (
  64. record: RouteRecord,
  65. location: Location
  66. ): Route {
  67. const originalRedirect = record.redirect
  68. let redirect = typeof originalRedirect === 'function'
  69. ? originalRedirect(createRoute(record, location, null, router))
  70. : originalRedirect
  71. if (typeof redirect === 'string') {
  72. redirect = { path: redirect }
  73. }
  74. if (!redirect || typeof redirect !== 'object') {
  75. if (process.env.NODE_ENV !== 'production') {
  76. warn(
  77. false, `invalid redirect option: ${JSON.stringify(redirect)}`
  78. )
  79. }
  80. return _createRoute(null, location)
  81. }
  82. const re: Object = redirect
  83. const { name, path } = re
  84. let { query, hash, params } = location
  85. query = re.hasOwnProperty('query') ? re.query : query
  86. hash = re.hasOwnProperty('hash') ? re.hash : hash
  87. params = re.hasOwnProperty('params') ? re.params : params
  88. if (name) {
  89. // resolved named direct
  90. const targetRecord = nameMap[name]
  91. if (process.env.NODE_ENV !== 'production') {
  92. assert(targetRecord, `redirect failed: named route "${name}" not found.`)
  93. }
  94. return match({
  95. _normalized: true,
  96. name,
  97. query,
  98. hash,
  99. params
  100. }, undefined, location)
  101. } else if (path) {
  102. // 1. resolve relative redirect
  103. const rawPath = resolveRecordPath(path, record)
  104. // 2. resolve params
  105. const resolvedPath = fillParams(rawPath, params, `redirect route with path "${rawPath}"`)
  106. // 3. rematch with existing query and hash
  107. return match({
  108. _normalized: true,
  109. path: resolvedPath,
  110. query,
  111. hash
  112. }, undefined, location)
  113. } else {
  114. if (process.env.NODE_ENV !== 'production') {
  115. warn(false, `invalid redirect option: ${JSON.stringify(redirect)}`)
  116. }
  117. return _createRoute(null, location)
  118. }
  119. }
  120. function alias (
  121. record: RouteRecord,
  122. location: Location,
  123. matchAs: string
  124. ): Route {
  125. const aliasedPath = fillParams(matchAs, location.params, `aliased route with path "${matchAs}"`)
  126. const aliasedMatch = match({
  127. _normalized: true,
  128. path: aliasedPath
  129. })
  130. if (aliasedMatch) {
  131. const matched = aliasedMatch.matched
  132. const aliasedRecord = matched[matched.length - 1]
  133. location.params = aliasedMatch.params
  134. return _createRoute(aliasedRecord, location)
  135. }
  136. return _createRoute(null, location)
  137. }
  138. function _createRoute (
  139. record: ?RouteRecord,
  140. location: Location,
  141. redirectedFrom?: Location
  142. ): Route {
  143. if (record && record.redirect) {
  144. return redirect(record, redirectedFrom || location)
  145. }
  146. if (record && record.matchAs) {
  147. return alias(record, location, record.matchAs)
  148. }
  149. return createRoute(record, location, redirectedFrom, router)
  150. }
  151. return {
  152. match,
  153. addRoutes
  154. }
  155. }
  156. function matchRoute (
  157. regex: RouteRegExp,
  158. path: string,
  159. params: Object
  160. ): boolean {
  161. const m = path.match(regex)
  162. if (!m) {
  163. return false
  164. } else if (!params) {
  165. return true
  166. }
  167. for (let i = 1, len = m.length; i < len; ++i) {
  168. const key = regex.keys[i - 1]
  169. if (key) {
  170. // Fix #1994: using * with props: true generates a param named 0
  171. params[key.name || 'pathMatch'] = typeof m[i] === 'string' ? decode(m[i]) : m[i]
  172. }
  173. }
  174. return true
  175. }
  176. function resolveRecordPath (path: string, record: RouteRecord): string {
  177. return resolvePath(path, record.parent ? record.parent.path : '/', true)
  178. }