diff --git a/examples/keepalive-view/app.js b/examples/keepalive-view/app.js
index 37563aa32..341fed97e 100644
--- a/examples/keepalive-view/app.js
+++ b/examples/keepalive-view/app.js
@@ -27,7 +27,52 @@ const IndexChild2 = { template: '
index child2
' }
const Home = { template: 'home
' }
-const ViewWithKeepalive = { template: '' }
+const ViewWithKeepalive = {
+ template: ''
+}
+
+const Parent = { template: 'msg: {{ msg }}
', props: ['msg'] }
+
+const RequiredProps = {
+ template: 'props from route config is: {{ msg }}
',
+ props: {
+ msg: {
+ type: String,
+ required: true
+ }
+ }
+}
+
+// keep original values to restore them later
+const originalSilent = Vue.config.silent
+const originalWarnHandler = Vue.config.warnHandler
+
+const CatchWarn = {
+ template: `{{ didWarn ? 'caught missing prop warn' : 'no missing prop warn' }}
`,
+ data () {
+ return {
+ didWarn: false
+ }
+ },
+ beforeRouteEnter (to, from, next) {
+ let missPropWarn = false
+ Vue.config.silent = false
+ Vue.config.warnHandler = function (msg, vm, trace) {
+ if (/Missing required prop/i.test(msg)) {
+ missPropWarn = true
+ }
+ }
+ next(vm => {
+ vm.didWarn = missPropWarn
+ })
+ },
+ beforeRouteLeave (to, from, next) {
+ // restore vue config
+ Vue.config.silent = originalSilent
+ Vue.config.warnHandler = originalWarnHandler
+ next()
+ }
+}
const router = new VueRouter({
mode: 'history',
@@ -80,6 +125,24 @@ const router = new VueRouter({
]
}
]
+ },
+ {
+ path: '/config-required-props',
+ component: Parent,
+ props: { msg: 'from parent' },
+ children: [
+ {
+ path: 'child',
+ component: RequiredProps,
+ props: {
+ msg: 'from child'
+ }
+ }
+ ]
+ },
+ {
+ path: '/catch-warn',
+ component: CatchWarn
}
]
})
@@ -96,6 +159,9 @@ new Vue({
/with-guard2
/one/two/child1
/one/two/child2
+ /config-required-props
+ /config-required-props/child
+ /catch-warn
diff --git a/src/components/view.js b/src/components/view.js
index 23d48b883..e24d6fa4e 100644
--- a/src/components/view.js
+++ b/src/components/view.js
@@ -39,17 +39,32 @@ export default {
// render previous view if the tree is inactive and kept-alive
if (inactive) {
- return h(cache[name], data, children)
+ 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]
- // render empty node if no matched route
- if (!matched) {
+ 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()
}
- const component = cache[name] = matched.components[name]
+ // cache component
+ cache[name] = { component }
// attach instance registration hook
// this will be called in the instance's injected lifecycle hooks
@@ -81,25 +96,37 @@ export default {
}
}
- // resolve props
- let propsToPass = data.props = resolveProps(route, matched.props && matched.props[name])
- 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]
- }
- }
+ const configProps = matched.props && matched.props[name]
+ // save route and configProps in cachce
+ 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':
diff --git a/test/e2e/specs/keepalive-view.js b/test/e2e/specs/keepalive-view.js
index 68a38ef48..59602848e 100644
--- a/test/e2e/specs/keepalive-view.js
+++ b/test/e2e/specs/keepalive-view.js
@@ -9,7 +9,7 @@ module.exports = {
browser
.url('http://localhost:8080/keepalive-view/')
.waitForElementVisible('#app', 1000)
- .assert.count('li a', 7)
+ .assert.count('li a', 10)
.click('li:nth-child(1) a')
.assert.containsText('.view', 'index child1')
@@ -44,6 +44,17 @@ module.exports = {
.click('li:nth-child(7) a')
.assert.containsText('.view', 'index child2')
+ // missing props in nested routes with keep alive
+ // https://github.com/vuejs/vue-router/issues/2301
+ .click('li:nth-child(8) a')
+ .assert.containsText('.view', 'msg: from parent')
+ .click('li:nth-child(9) a')
+ .assert.containsText('.view', 'msg: from parent\nprops from route config is: from child')
+ .click('li:nth-child(10) a')
+ .assert.containsText('.view', 'no missing prop warn')
+ .click('li:nth-child(9) a')
+ .assert.containsText('.view', 'msg: from parent\nprops from route config is: from child')
+
.end()
}
}