07b4790988dc7ff920fc5ae9dd822e9324c7d4b7f3e6f5db4b021d1718b4b24ee252bab5b41dbc58fe25330b9a8b396583be077f4b4c00d4931b92881bfeb1 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import { warn } from '../util/warn'
  2. import { extend } from '../util/misc'
  3. import { handleRouteEntered } from '../util/route'
  4. export default {
  5. name: 'RouterView',
  6. functional: true,
  7. props: {
  8. name: {
  9. type: String,
  10. default: 'default'
  11. }
  12. },
  13. render (_, { props, children, parent, data }) {
  14. // used by devtools to display a router-view badge
  15. data.routerView = true
  16. // directly use parent context's createElement() function
  17. // so that components rendered by router-view can resolve named slots
  18. const h = parent.$createElement
  19. const name = props.name
  20. const route = parent.$route
  21. const cache = parent._routerViewCache || (parent._routerViewCache = {})
  22. // determine current view depth, also check to see if the tree
  23. // has been toggled inactive but kept-alive.
  24. let depth = 0
  25. let inactive = false
  26. while (parent && parent._routerRoot !== parent) {
  27. const vnodeData = parent.$vnode ? parent.$vnode.data : {}
  28. if (vnodeData.routerView) {
  29. depth++
  30. }
  31. if (vnodeData.keepAlive && parent._directInactive && parent._inactive) {
  32. inactive = true
  33. }
  34. parent = parent.$parent
  35. }
  36. data.routerViewDepth = depth
  37. // render previous view if the tree is inactive and kept-alive
  38. if (inactive) {
  39. const cachedData = cache[name]
  40. const cachedComponent = cachedData && cachedData.component
  41. if (cachedComponent) {
  42. // #2301
  43. // pass props
  44. if (cachedData.configProps) {
  45. fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps)
  46. }
  47. return h(cachedComponent, data, children)
  48. } else {
  49. // render previous empty view
  50. return h()
  51. }
  52. }
  53. const matched = route.matched[depth]
  54. const component = matched && matched.components[name]
  55. // render empty node if no matched route or no config component
  56. if (!matched || !component) {
  57. cache[name] = null
  58. return h()
  59. }
  60. // cache component
  61. cache[name] = { component }
  62. // attach instance registration hook
  63. // this will be called in the instance's injected lifecycle hooks
  64. data.registerRouteInstance = (vm, val) => {
  65. // val could be undefined for unregistration
  66. const current = matched.instances[name]
  67. if (
  68. (val && current !== vm) ||
  69. (!val && current === vm)
  70. ) {
  71. matched.instances[name] = val
  72. }
  73. }
  74. // also register instance in prepatch hook
  75. // in case the same component instance is reused across different routes
  76. ;(data.hook || (data.hook = {})).prepatch = (_, vnode) => {
  77. matched.instances[name] = vnode.componentInstance
  78. }
  79. // register instance in init hook
  80. // in case kept-alive component be actived when routes changed
  81. data.hook.init = (vnode) => {
  82. if (vnode.data.keepAlive &&
  83. vnode.componentInstance &&
  84. vnode.componentInstance !== matched.instances[name]
  85. ) {
  86. matched.instances[name] = vnode.componentInstance
  87. }
  88. // if the route transition has already been confirmed then we weren't
  89. // able to call the cbs during confirmation as the component was not
  90. // registered yet, so we call it here.
  91. handleRouteEntered(route)
  92. }
  93. const configProps = matched.props && matched.props[name]
  94. // save route and configProps in cache
  95. if (configProps) {
  96. extend(cache[name], {
  97. route,
  98. configProps
  99. })
  100. fillPropsinData(component, data, route, configProps)
  101. }
  102. return h(component, data, children)
  103. }
  104. }
  105. function fillPropsinData (component, data, route, configProps) {
  106. // resolve props
  107. let propsToPass = data.props = resolveProps(route, configProps)
  108. if (propsToPass) {
  109. // clone to prevent mutation
  110. propsToPass = data.props = extend({}, propsToPass)
  111. // pass non-declared props as attrs
  112. const attrs = data.attrs = data.attrs || {}
  113. for (const key in propsToPass) {
  114. if (!component.props || !(key in component.props)) {
  115. attrs[key] = propsToPass[key]
  116. delete propsToPass[key]
  117. }
  118. }
  119. }
  120. }
  121. function resolveProps (route, config) {
  122. switch (typeof config) {
  123. case 'undefined':
  124. return
  125. case 'object':
  126. return config
  127. case 'function':
  128. return config(route)
  129. case 'boolean':
  130. return config ? route.params : undefined
  131. default:
  132. if (process.env.NODE_ENV !== 'production') {
  133. warn(
  134. false,
  135. `props in "${route.path}" is a ${typeof config}, ` +
  136. `expecting an object, function or boolean.`
  137. )
  138. }
  139. }
  140. }