diff --git a/examples/keepalive-view/app.js b/examples/keepalive-view/app.js
index 2155782b8a..e68395f3fe 100644
--- a/examples/keepalive-view/app.js
+++ b/examples/keepalive-view/app.js
@@ -27,6 +27,48 @@ const IndexChild2 = { template: '
index child2
' }
const Home = { template: 'home
' }
+const Parent = { template: '' }
+
+const RequiredProps = {
+ template: 'props from route config is: {{msg}}
',
+ props: {
+ msg: {
+ type: String,
+ required: true
+ }
+ }
+}
+
+const savedSilent = Vue.config.silent
+const savedWarnHandler = Vue.config.warnHandler
+
+const CatchWarn = {
+ template: `{{catchedWarn ? 'catched missing prop warn' : 'no missing prop warn'}}
`,
+ data () {
+ return {
+ catchedWarn: 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.catchedWarn = missPropWarn
+ })
+ },
+ beforeRouteLeave (to, from, next) {
+ // restore vue config
+ Vue.config.silent = savedSilent
+ Vue.config.warnHandler = savedWarnHandler
+ next()
+ }
+}
+
const router = new VueRouter({
mode: 'history',
base: __dirname,
@@ -58,6 +100,23 @@ const router = new VueRouter({
path: '/with-guard2',
name: 'with-guard2',
component: WithGuard
+ },
+ {
+ path: '/config-required-props',
+ component: Parent,
+ children: [
+ {
+ path: 'child',
+ component: RequiredProps,
+ props: {
+ msg: 'ok'
+ }
+ }
+ ]
+ },
+ {
+ path: '/catch-warn',
+ component: CatchWarn
}
]
})
@@ -72,6 +131,8 @@ new Vue({
/home
/with-guard1
/with-guard2
+ /config-required-props/child
+ /catch-warn
diff --git a/src/components/view.js b/src/components/view.js
index 3f440d5b4b..b96b29efd4 100644
--- a/src/components/view.js
+++ b/src/components/view.js
@@ -41,17 +41,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) {
+ passProps(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
@@ -83,25 +98,38 @@ 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]
+ // pass props
+ if (configProps) {
+ // add route and config props to cache data
+ extend(cache[name], {
+ route,
+ configProps
+ })
+ passProps(component, data, route, configProps)
}
return h(component, data, children)
}
}
+function passProps (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 5e36712382..eedafc09e6 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', 5)
+ .assert.count('li a', 7)
.click('li:nth-child(1) a')
.assert.containsText('.view', 'index child1')
@@ -35,6 +35,15 @@ module.exports = {
.click('li:nth-child(4) a')
.assert.containsText('.view', 'with-guard1: 3')
+ // missing props in nested routes with keep alive
+ // https://github.com/vuejs/vue-router/issues/2301
+ .click('li:nth-child(6) a')
+ .assert.containsText('.view', 'props from route config is: ok')
+ .click('li:nth-child(7) a')
+ .assert.containsText('.view', 'no missing prop warn')
+ .click('li:nth-child(6) a')
+ .assert.containsText('.view', 'props from route config is: ok')
+
.end()
}
}