From b2429abde2fec36eb28b8d8efe89a4a1446f0457 Mon Sep 17 00:00:00 2001 From: zrh122 <1229550935@qq.com> Date: Tue, 21 May 2019 00:08:56 +0800 Subject: [PATCH 1/4] fix(router-view): add passing props to inactive component --- examples/keepalive-view/app.js | 61 +++++++++++++++++++++++++++++++ src/components/view.js | 62 +++++++++++++++++++++++--------- test/e2e/specs/keepalive-view.js | 11 +++++- 3 files changed, 116 insertions(+), 18 deletions(-) diff --git a/examples/keepalive-view/app.js b/examples/keepalive-view/app.js index 37563aa32..935b66a9e 100644 --- a/examples/keepalive-view/app.js +++ b/examples/keepalive-view/app.js @@ -29,6 +29,48 @@ const Home = { template: '
home
' } const ViewWithKeepalive = { template: '' } +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, @@ -80,6 +122,23 @@ const router = new VueRouter({ ] } ] + }, + { + path: '/config-required-props', + component: Parent, + children: [ + { + path: 'child', + component: RequiredProps, + props: { + msg: 'ok' + } + } + ] + }, + { + path: '/catch-warn', + component: CatchWarn } ] }) @@ -96,6 +155,8 @@ new Vue({
  • /with-guard2
  • /one/two/child1
  • /one/two/child2
  • +
  • /config-required-props/child
  • +
  • /catch-warn
  • diff --git a/src/components/view.js b/src/components/view.js index 23d48b883..378443b7d 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) { + 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 @@ -81,25 +96,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 68a38ef48..4a8c56c03 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', 9) .click('li:nth-child(1) a') .assert.containsText('.view', 'index child1') @@ -44,6 +44,15 @@ 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', 'props from route config is: ok') + .click('li:nth-child(9) a') + .assert.containsText('.view', 'no missing prop warn') + .click('li:nth-child(8) a') + .assert.containsText('.view', 'props from route config is: ok') + .end() } } From ae0e02a14d0763eb8fe13a7dca5a5f5ccc20b339 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 15 Jan 2020 12:02:35 +0100 Subject: [PATCH 2/4] test(keep-alive): add more cases for props --- examples/keepalive-view/app.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/examples/keepalive-view/app.js b/examples/keepalive-view/app.js index 935b66a9e..341fed97e 100644 --- a/examples/keepalive-view/app.js +++ b/examples/keepalive-view/app.js @@ -27,12 +27,14 @@ const IndexChild2 = { template: '
    index child2
    ' } const Home = { template: '
    home
    ' } -const ViewWithKeepalive = { template: '' } +const ViewWithKeepalive = { + template: '' +} -const Parent = { template: '' } +const Parent = { template: '
    msg: {{ msg }}
    ', props: ['msg'] } const RequiredProps = { - template: '
    props from route config is: {{msg}}
    ', + template: '
    props from route config is: {{ msg }}
    ', props: { msg: { type: String, @@ -41,14 +43,15 @@ const RequiredProps = { } } -const savedSilent = Vue.config.silent -const savedWarnHandler = Vue.config.warnHandler +// keep original values to restore them later +const originalSilent = Vue.config.silent +const originalWarnHandler = Vue.config.warnHandler const CatchWarn = { - template: `
    {{catchedWarn ? 'catched missing prop warn' : 'no missing prop warn'}}
    `, + template: `
    {{ didWarn ? 'caught missing prop warn' : 'no missing prop warn' }}
    `, data () { return { - catchedWarn: false + didWarn: false } }, beforeRouteEnter (to, from, next) { @@ -60,13 +63,13 @@ const CatchWarn = { } } next(vm => { - vm.catchedWarn = missPropWarn + vm.didWarn = missPropWarn }) }, beforeRouteLeave (to, from, next) { // restore vue config - Vue.config.silent = savedSilent - Vue.config.warnHandler = savedWarnHandler + Vue.config.silent = originalSilent + Vue.config.warnHandler = originalWarnHandler next() } } @@ -126,12 +129,13 @@ const router = new VueRouter({ { path: '/config-required-props', component: Parent, + props: { msg: 'from parent' }, children: [ { path: 'child', component: RequiredProps, props: { - msg: 'ok' + msg: 'from child' } } ] @@ -155,6 +159,7 @@ new Vue({
  • /with-guard2
  • /one/two/child1
  • /one/two/child2
  • +
  • /config-required-props
  • /config-required-props/child
  • /catch-warn
  • From 880e6ac229a862426440a82e29478c77162fb281 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 15 Jan 2020 12:08:43 +0100 Subject: [PATCH 3/4] test(keep-alive): adapt tests --- test/e2e/specs/keepalive-view.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/e2e/specs/keepalive-view.js b/test/e2e/specs/keepalive-view.js index 4a8c56c03..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', 9) + .assert.count('li a', 10) .click('li:nth-child(1) a') .assert.containsText('.view', 'index child1') @@ -47,11 +47,13 @@ module.exports = { // 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', 'props from route config is: ok') + .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(8) a') - .assert.containsText('.view', 'props from route config is: ok') + .click('li:nth-child(9) a') + .assert.containsText('.view', 'msg: from parent\nprops from route config is: from child') .end() } From f1f10343cfae6222dddc771129eb91d0b0880018 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Wed, 15 Jan 2020 12:20:27 +0100 Subject: [PATCH 4/4] refactor(view): rename function --- src/components/view.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/view.js b/src/components/view.js index 378443b7d..e24d6fa4e 100644 --- a/src/components/view.js +++ b/src/components/view.js @@ -45,7 +45,7 @@ export default { // #2301 // pass props if (cachedData.configProps) { - passProps(cachedComponent, data, cachedData.route, cachedData.configProps) + fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps) } return h(cachedComponent, data, children) } else { @@ -97,21 +97,20 @@ export default { } const configProps = matched.props && matched.props[name] - // pass props + // save route and configProps in cachce if (configProps) { - // add route and config props to cache data extend(cache[name], { route, configProps }) - passProps(component, data, route, configProps) + fillPropsinData(component, data, route, configProps) } return h(component, data, children) } } -function passProps (component, data, route, configProps) { +function fillPropsinData (component, data, route, configProps) { // resolve props let propsToPass = data.props = resolveProps(route, configProps) if (propsToPass) {