Skip to content

Commit 0fb1343

Browse files
zrh122posva
andcommitted
fix(view): add passing props to inactive component (#2773)
Closes #2301 * fix(router-view): add passing props to inactive component * test(keep-alive): add more cases for props * test(keep-alive): adapt tests * refactor(view): rename function Co-authored-by: Eduardo San Martin Morote <[email protected]>
1 parent 0c2b1aa commit 0fb1343

File tree

3 files changed

+123
-19
lines changed

3 files changed

+123
-19
lines changed

examples/keepalive-view/app.js

+67-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,52 @@ const IndexChild2 = { template: '<div>index child2</div>' }
2727

2828
const Home = { template: '<div>home</div>' }
2929

30-
const ViewWithKeepalive = { template: '<keep-alive><router-view></router-view></keep-alive>' }
30+
const ViewWithKeepalive = {
31+
template: '<keep-alive><router-view></router-view></keep-alive>'
32+
}
33+
34+
const Parent = { template: '<div>msg: {{ msg }}<router-view /></div>', props: ['msg'] }
35+
36+
const RequiredProps = {
37+
template: '<div>props from route config is: {{ msg }}</div>',
38+
props: {
39+
msg: {
40+
type: String,
41+
required: true
42+
}
43+
}
44+
}
45+
46+
// keep original values to restore them later
47+
const originalSilent = Vue.config.silent
48+
const originalWarnHandler = Vue.config.warnHandler
49+
50+
const CatchWarn = {
51+
template: `<div>{{ didWarn ? 'caught missing prop warn' : 'no missing prop warn' }}</div>`,
52+
data () {
53+
return {
54+
didWarn: false
55+
}
56+
},
57+
beforeRouteEnter (to, from, next) {
58+
let missPropWarn = false
59+
Vue.config.silent = false
60+
Vue.config.warnHandler = function (msg, vm, trace) {
61+
if (/Missing required prop/i.test(msg)) {
62+
missPropWarn = true
63+
}
64+
}
65+
next(vm => {
66+
vm.didWarn = missPropWarn
67+
})
68+
},
69+
beforeRouteLeave (to, from, next) {
70+
// restore vue config
71+
Vue.config.silent = originalSilent
72+
Vue.config.warnHandler = originalWarnHandler
73+
next()
74+
}
75+
}
3176

3277
const router = new VueRouter({
3378
mode: 'history',
@@ -80,6 +125,24 @@ const router = new VueRouter({
80125
]
81126
}
82127
]
128+
},
129+
{
130+
path: '/config-required-props',
131+
component: Parent,
132+
props: { msg: 'from parent' },
133+
children: [
134+
{
135+
path: 'child',
136+
component: RequiredProps,
137+
props: {
138+
msg: 'from child'
139+
}
140+
}
141+
]
142+
},
143+
{
144+
path: '/catch-warn',
145+
component: CatchWarn
83146
}
84147
]
85148
})
@@ -96,6 +159,9 @@ new Vue({
96159
<li><router-link to="/with-guard2">/with-guard2</router-link></li>
97160
<li><router-link to="/one/two/child1">/one/two/child1</router-link></li>
98161
<li><router-link to="/one/two/child2">/one/two/child2</router-link></li>
162+
<li><router-link to="/config-required-props">/config-required-props</router-link></li>
163+
<li><router-link to="/config-required-props/child">/config-required-props/child</router-link></li>
164+
<li><router-link to="/catch-warn">/catch-warn</router-link></li>
99165
</ul>
100166
<keep-alive>
101167
<router-view class="view"></router-view>

src/components/view.js

+44-17
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,32 @@ export default {
3939

4040
// render previous view if the tree is inactive and kept-alive
4141
if (inactive) {
42-
return h(cache[name], data, children)
42+
const cachedData = cache[name]
43+
const cachedComponent = cachedData && cachedData.component
44+
if (cachedComponent) {
45+
// #2301
46+
// pass props
47+
if (cachedData.configProps) {
48+
fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps)
49+
}
50+
return h(cachedComponent, data, children)
51+
} else {
52+
// render previous empty view
53+
return h()
54+
}
4355
}
4456

4557
const matched = route.matched[depth]
46-
// render empty node if no matched route
47-
if (!matched) {
58+
const component = matched && matched.components[name]
59+
60+
// render empty node if no matched route or no config component
61+
if (!matched || !component) {
4862
cache[name] = null
4963
return h()
5064
}
5165

52-
const component = cache[name] = matched.components[name]
66+
// cache component
67+
cache[name] = { component }
5368

5469
// attach instance registration hook
5570
// this will be called in the instance's injected lifecycle hooks
@@ -81,25 +96,37 @@ export default {
8196
}
8297
}
8398

84-
// resolve props
85-
let propsToPass = data.props = resolveProps(route, matched.props && matched.props[name])
86-
if (propsToPass) {
87-
// clone to prevent mutation
88-
propsToPass = data.props = extend({}, propsToPass)
89-
// pass non-declared props as attrs
90-
const attrs = data.attrs = data.attrs || {}
91-
for (const key in propsToPass) {
92-
if (!component.props || !(key in component.props)) {
93-
attrs[key] = propsToPass[key]
94-
delete propsToPass[key]
95-
}
96-
}
99+
const configProps = matched.props && matched.props[name]
100+
// save route and configProps in cachce
101+
if (configProps) {
102+
extend(cache[name], {
103+
route,
104+
configProps
105+
})
106+
fillPropsinData(component, data, route, configProps)
97107
}
98108

99109
return h(component, data, children)
100110
}
101111
}
102112

113+
function fillPropsinData (component, data, route, configProps) {
114+
// resolve props
115+
let propsToPass = data.props = resolveProps(route, configProps)
116+
if (propsToPass) {
117+
// clone to prevent mutation
118+
propsToPass = data.props = extend({}, propsToPass)
119+
// pass non-declared props as attrs
120+
const attrs = data.attrs = data.attrs || {}
121+
for (const key in propsToPass) {
122+
if (!component.props || !(key in component.props)) {
123+
attrs[key] = propsToPass[key]
124+
delete propsToPass[key]
125+
}
126+
}
127+
}
128+
}
129+
103130
function resolveProps (route, config) {
104131
switch (typeof config) {
105132
case 'undefined':

test/e2e/specs/keepalive-view.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module.exports = {
99
browser
1010
.url('http://localhost:8080/keepalive-view/')
1111
.waitForElementVisible('#app', 1000)
12-
.assert.count('li a', 7)
12+
.assert.count('li a', 10)
1313

1414
.click('li:nth-child(1) a')
1515
.assert.containsText('.view', 'index child1')
@@ -44,6 +44,17 @@ module.exports = {
4444
.click('li:nth-child(7) a')
4545
.assert.containsText('.view', 'index child2')
4646

47+
// missing props in nested routes with keep alive
48+
// https://github.com/vuejs/vue-router/issues/2301
49+
.click('li:nth-child(8) a')
50+
.assert.containsText('.view', 'msg: from parent')
51+
.click('li:nth-child(9) a')
52+
.assert.containsText('.view', 'msg: from parent\nprops from route config is: from child')
53+
.click('li:nth-child(10) a')
54+
.assert.containsText('.view', 'no missing prop warn')
55+
.click('li:nth-child(9) a')
56+
.assert.containsText('.view', 'msg: from parent\nprops from route config is: from child')
57+
4758
.end()
4859
}
4960
}

0 commit comments

Comments
 (0)