公司后臺(tái)管理系統(tǒng),產(chǎn)品經(jīng)理希望做一個(gè)tab欄,可以切換不同的頁(yè)面,類似于很早之前通過iframe的方式進(jìn)行各個(gè)page之間的切換。這邊直接使用的keep-alive
layout組件如下
// @/layouts/BasicLayout.vue
<template>
<keep-alive>
<router-view />
</keep-alive>
</template>
<script>
export default {
name: 'BasicLayout'
}
</script>
如下路由配置
// @/router/index.js
const routes = [{
name: 'index',
path: '/keep-alive',
component: BasicLayout,
children: [
{
path: 'one',
name: 'demoOne',
component: DemoOne
},
{
path: 'two/:id',
name: 'demoTwo',
component: DemoTwo
},
{
path: 'three/:id',
name: 'DemoThree',
component: DemoThree
}
]
}]
然后是demo-one
// demo-one.vue
<template>
<div class="demo">
<div>This is demo one</div>
</div>
</template>
然后是demo-two
// demo-two.vue
<template>
<div class="demo">
<h4>DEMO-TWO</h4>
<div>這是ID:{{id}}</div>
<div>
<input type="text" :placeholder="placeholder">
</div>
</div>
</template>
<script>
export default {
data () {
const { id } = this.$route.params
return {
id,
placeholder: `請(qǐng)輸入ID:${id}對(duì)應(yīng)的名字`
}
}
}
</script>
但是如果遇到需要使用動(dòng)態(tài)路徑參數(shù)的話,就會(huì)出現(xiàn)問題
- demo-two中的id不會(huì)隨著路徑中的id的改變而改變
-
在demo-two/1頁(yè)面中的input框輸入值后,在demo-two/2,demo-two/3,demo-two/4,demo-two/5中同樣會(huì)顯示這個(gè)值。
案例圖片
解決方案1:使用路由組件傳參
// @/router/index
// ...
{
path: 'two/:id',
name: 'demoTwo',
component: DemoTwo,
props: true
},
// ...
// @/pages/demo-two.vue
export default {
props: {
id: {
type: String,
required: true
}
},
computed: {
placeholder () {
return `請(qǐng)輸入ID:${this.id}對(duì)應(yīng)的名字`
}
}
}
修改過后問題一得到了解決,但是問題二依然存在

修改過后問題一得到了解決,但是問題二依然存在
查看keep-alive源代碼
// /Users/zhaoxuetong/github/vue/src/core/components/keep-alive.js
const key: ?string = vnode.key == null
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
} else {
cache[key] = vnode
keys.push(key)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}
keep-alive緩存子組件的使用的健名(key)的邏輯如下:
如果vnode上有key,則使用key,不過不存在key則使用componentOptions.Ctor.cid + (componentOptions.tag ? ::${componentOptions.tag} : '')。由于
- 沒有在組件上使用key。
- router-view這一層,componentOptions.tag也沒有值
- 所以最終獲得的key就是vnode.componentOptions.Ctor.cid
由同一個(gè)組件所渲染的實(shí)例的cid都是一樣的。
所以其實(shí)根據(jù)demo-two渲染出來(lái)的5個(gè)組件的cid都是一樣的,那么keep-alive其實(shí)緩存的就是同一個(gè)vnode. componentInstance。所以看到只要在一個(gè)路徑下輸入了input的內(nèi)容,其余四個(gè)都會(huì)同時(shí)改變。
那么我們?cè)趘ue-route上加上key,那么就可是實(shí)現(xiàn)我們想要的效果。
// @/layouts/BasicLayout.vue
<template>
<keep-alive>
<router-view :key="$route.fullPath" />
</keep-alive>
</template>
<script>
export default {
name: 'BasicLayout'
}
</script>

增加key之后的效果圖.gif

打印出來(lái)的keep-alive的cache的緩存
