储存keep-alive内组件的滚动位置

keep-alive一般被用来缓存组件实例.当从一个组件切换到另一个时, 这个组件不会被销毁, 而会被保存起来供下次调用时使用.
遗憾的是, 如果被keep-alive包裹的组件中有滚动元素,keep-alive不会储存滚动位置.
需要在滚动时储存位置, 然后在组件被激活时还原.

keep-alive 组件有两个特殊的生命周期钩子: activated和deactivated.
activated在keep-alive组件激活时调用.
deactivated在keep-alive组件被停用时调用.
API — Vue.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
mounted() {
// 获取组件根元素Dom
const page = this.$el;
// 绑定事件,滚动时,储存位置到this.scrollTop
page.addEventListener("scroll", () => {
this.scrollTop = page.scrollTop;
});
},
// activated生命钩子在keep-alive被激活时调用
activated() {
// 如果曾滚动过,则还原位置
if (this.scrollTop) {
const page = this.$el;
page.scrollTop = this.scrollTop;
}
},
// deactivated生命钩子在keep-alive被停用时调用
// 如果onScroll绑定在window之类的元素上,记得用removeEventListener删除事件
deactivated() {}
};

另外,可以将这段代码储存为mixin, 需要时调用即可.

See the Pen [Vue] 记录keep-alive内组件的滚动位置 by aaronbird (@aaronbird) on CodePen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.9/vue.min.js">
<div class="app">
<div class="main">
<keep-alive>
<!-- 动态组件 -->
<component :is="componentName">
</keep-alive>
</div>
<!-- 导航 -->
<div class="navs">
<div class="nav" @click="componentName = tag" v-for="tag of tags">
{{tag}}
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
body {
margin: 0;
font-size: 20px;
}

@mixin pin() {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: auto;
}

.app {
height: 100vh;
display: flex;
flex-direction: column;
.main {
position: relative;
flex: 1 1 auto;
overflow: hidden;
text-align: center;
.home {
color: red;
background: blue;
@include pin();
}
.shop {
color: blue;
background: red;
@include pin();
}
}
.navs {
display: flex;
flex: 0 0 50px;
background: #ffa;
align-items: center;
.nav {
flex: 1 1 auto;
height: 100%;
line-height: 50px;
text-align: center;
border-right: 1px solid rgba(0, 0, 0, 0.2);
&:last-child {
border-right: none;
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 定义mixin, 用于记录scroll位置
const keepScrollTop = {
mounted() {
// 获取组件根元素Dom
const page = this.$el;
// 绑定事件,滚动时,储存位置到this.scrollTop
page.addEventListener("scroll", () => {
this.scrollTop = page.scrollTop;
});
},
// activated生命钩子在keep-alive被激活时调用
activated() {
// 如果曾滚动过,则还原位置
if (this.scrollTop) {
const page = this.$el;
page.scrollTop = this.scrollTop;
}
},
// deactivated生命钩子在keep-alive被停用时调用
// 如果onScroll绑定在window之类的元素上,记得用removeEventListener删除事件
deactivated() {}
};
// 定义组件home
Vue.component("home", {
mixins: [keepScrollTop],
template: `
<div class="home">
<div v-for="i of 50">{{i}}</div>
</div>
`
});
// 定义组件shop
Vue.component("shop", {
mixins: [keepScrollTop],
template: `
<div class="shop">
<div v-for="i of 50">{{i}}</div>
</div>
`
});
// 创建Vue实例
const app = new Vue({
el: ".app",
data: {
tags: ["home", "shop"],
componentName: "home"
}
});