123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- import { warn } from '../util/warn'
- import { extend } from '../util/misc'
- import { handleRouteEntered } from '../util/route'
- export default {
- name: 'RouterView',
- functional: true,
- props: {
- name: {
- type: String,
- default: 'default'
- }
- },
- render (_, { props, children, parent, data }) {
- // used by devtools to display a router-view badge
- data.routerView = true
- // directly use parent context's createElement() function
- // so that components rendered by router-view can resolve named slots
- const h = parent.$createElement
- const name = props.name
- const route = parent.$route
- const cache = parent._routerViewCache || (parent._routerViewCache = {})
- // determine current view depth, also check to see if the tree
- // has been toggled inactive but kept-alive.
- let depth = 0
- let inactive = false
- while (parent && parent._routerRoot !== parent) {
- const vnodeData = parent.$vnode ? parent.$vnode.data : {}
- if (vnodeData.routerView) {
- depth++
- }
- if (vnodeData.keepAlive && parent._directInactive && parent._inactive) {
- inactive = true
- }
- parent = parent.$parent
- }
- data.routerViewDepth = depth
- // render previous view if the tree is inactive and kept-alive
- if (inactive) {
- const cachedData = cache[name]
- const cachedComponent = cachedData && cachedData.component
- if (cachedComponent) {
- // #2301
- // pass props
- if (cachedData.configProps) {
- fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps)
- }
- return h(cachedComponent, data, children)
- } else {
- // render previous empty view
- return h()
- }
- }
- const matched = route.matched[depth]
- const component = matched && matched.components[name]
- // render empty node if no matched route or no config component
- if (!matched || !component) {
- cache[name] = null
- return h()
- }
- // cache component
- cache[name] = { component }
- // attach instance registration hook
- // this will be called in the instance's injected lifecycle hooks
- data.registerRouteInstance = (vm, val) => {
- // val could be undefined for unregistration
- const current = matched.instances[name]
- if (
- (val && current !== vm) ||
- (!val && current === vm)
- ) {
- matched.instances[name] = val
- }
- }
- // also register instance in prepatch hook
- // in case the same component instance is reused across different routes
- ;(data.hook || (data.hook = {})).prepatch = (_, vnode) => {
- matched.instances[name] = vnode.componentInstance
- }
- // register instance in init hook
- // in case kept-alive component be actived when routes changed
- data.hook.init = (vnode) => {
- if (vnode.data.keepAlive &&
- vnode.componentInstance &&
- vnode.componentInstance !== matched.instances[name]
- ) {
- matched.instances[name] = vnode.componentInstance
- }
- // if the route transition has already been confirmed then we weren't
- // able to call the cbs during confirmation as the component was not
- // registered yet, so we call it here.
- handleRouteEntered(route)
- }
- const configProps = matched.props && matched.props[name]
- // save route and configProps in cache
- if (configProps) {
- extend(cache[name], {
- route,
- configProps
- })
- fillPropsinData(component, data, route, configProps)
- }
- return h(component, data, children)
- }
- }
- function fillPropsinData (component, data, route, configProps) {
- // resolve props
- let propsToPass = data.props = resolveProps(route, configProps)
- if (propsToPass) {
- // clone to prevent mutation
- propsToPass = data.props = extend({}, propsToPass)
- // pass non-declared props as attrs
- const attrs = data.attrs = data.attrs || {}
- for (const key in propsToPass) {
- if (!component.props || !(key in component.props)) {
- attrs[key] = propsToPass[key]
- delete propsToPass[key]
- }
- }
- }
- }
- function resolveProps (route, config) {
- switch (typeof config) {
- case 'undefined':
- return
- case 'object':
- return config
- case 'function':
- return config(route)
- case 'boolean':
- return config ? route.params : undefined
- default:
- if (process.env.NODE_ENV !== 'production') {
- warn(
- false,
- `props in "${route.path}" is a ${typeof config}, ` +
- `expecting an object, function or boolean.`
- )
- }
- }
- }
|