vue+vuerouter+vuex+keepalive动态缓存页面
需求:
vue+vuerouter+vuex
通过keepalive
控制页面缓存,有时候我们从列表页A
进入详情页
的时候,而详情页里面也有一个列表页B
,当我们从详情页进入列表页B
时,希望详情页缓存下来,当从列表页B
返回到详情页
时,我们需要详情页,不需要重新请求渲染页面,并且当前页面的滚动位置回到原来的位置,再继续返回到列表页A时,我们需要详情页不被缓存
目录结构
1project
2│
3└───src
4│ │
5| └───mixins
6| | scroll-position.js
7| |
8│ └───store
9| | | index.js
10| | |
11│ | └───common
12│ | index.js
13| |
14│ └───common
15│ | tools.js
16| |
17| └───router
18| | index.js
19| |
20| └───views
21| | list-a.vue
22| | detail.vue
23| | list-b.vue
24| |
25| └───App.vue
26
1.编写保存滚动位置方法
在目录下新建一个mixins
文件夹,在目录下新建一个scroll-position.js
,用户保存离开页面的滚动位置
1const scrollPosition = {
2 data() {
3 return {
4 }
5 },
6 activated() {
7 if (this.$route.meta.keepAlive) {
8 let pageName = this.$route.name;
9 let pagePositions = this.$store.state.CommonModule.pagePositions;
10 let res = pagePositions.find(v => v.name === pageName);
11 if (res && res.position > 0) {
12 this.$nextTick(function () {
13 document.documentElement.scrollTop = document.body.scrollTop = res.position || 0;
14 });
15 }
16 }
17 },
18 computed: {}
19};
20
21export default scrollPosition;
2.编写状态管理
在目录下新建一个store
文件夹,在store
文件夹下面新建一个index.js
,在store
文件夹下面新建一个common
文件夹,在common
文件夹下面新建一个index.js
/common/tools.js
1/**
2 * 获取数据类型,返回结果为 Number、String、Object、Array等
3 * @param value
4 * @returns {string}
5 */
6export const getRawType = (value) => {
7 return Object.prototype.toString.call(value).slice(8, -1)
8};
/store/common/tools.js
1import {getRawType} from '../../common/tools';
2
3const state = {
4 pagePositions: [],
5 cachedPages: []
6};
7
8const mutations = {
9 SAVE_POSITION(state, data) {
10 let pagePositions = state.pagePositions;
11 let key = -1;
12 pagePositions.find((v, k) => {
13 if (v.name === data.name) {
14 key = k;
15 return v
16 }
17 });
18 key >= 0 ? pagePositions[key] = data : pagePositions.push(data);
19 state.pagePositions = pagePositions;
20 },
21 SET_CACHED_PAGE(state, pageName) {
22 if (getRawType(pageName) === 'Array') {
23 state.cachedPages = pageName
24 } else {
25 let cached_pages = state.cachedPages;
26 let res = cached_pages.filter(cachedPage => cachedPage === `page-${pageName}`);
27 if (res.length <= 0) {
28 cached_pages.push(`page-${pageName}`);
29 state.cachedPages = cached_pages;
30 }
31 }
32 },
33 REMOVE_CACHED_PAGE(state, pageName) {
34 let cached_pages = state.cachedPages;
35 let index = cached_pages.findIndex(cachedPage => cachedPage === `page-${pageName}`)
36 if (index >= 0) {
37 cached_pages.splice(index, 1);
38 state.cachedPages = cached_pages
39 }
40 }
41};
42
43const getters = {
44};
45
46const actions = {
47 savePosition({commit}, data) {
48 commit('SAVE_POSITION', data)
49 },
50 setCachedPage({commit}, pageName) {
51 commit('SET_CACHED_PAGE', pageName)
52 },
53 removeCachedPage({commit}, pageName) {
54 commit('REMOVE_CACHED_PAGE', pageName)
55 }
56};
57
58export default {
59 namespace: true, //这里使用了vuex的命名空间,方便管理
60 state,
61 mutations,
62 getters,
63 actions
64}
/store/index.js
1import Vue from 'vue'
2import Vuex from 'vuex'
3import CommonModule from './common'
4
5Vue.use(Vuex);
6
7export default new Vuex.Store({
8 strict: process.env.NODE_ENV !== 'production', //严格模式
9 modules: {
10 CommonModule
11 }
12})
3.对特定页面编写缓存方法
app.vue
利用keeplive
的include
属性,详情可看https://cn.vuejs.org/v2/api/#keep-alive
1<template>
2 <div id="app">
3 <div class="app-container">
4 <keep-alive :include="cachedPages">
5 <router-view/>
6 </keep-alive>
7 </div>
8 </div>
9</template>
10
11<script type="text/ecmascript-6">
12 import {mapState} from 'vuex';
13
14 export default {
15 data() {
16 return {}
17 },
18 components: {
19 },
20 created() {
21 },
22 computed: {
23 ...mapState({
24 cachedPages: state => state.CommonModule.cachedPages
25 })
26 }
27 }
28</script>
29<style lang="less">
30</style>
/views/lista.vue
在详情页面
加入beforeRouteLeave
生命周期,
缓存的页面再次被激活的时候会触发activated
生命周期,页面被缓存离开的时候会触发deactivated
生命周期
/views/lista.vue
1<template>
2 <div>
3 i am list a
4 </div>
5</template>
6
7<script type="text/ecmascript-6">
8 import scrollPosition from "../mixins/scroll-position";
9
10 export default {
11 name: 'page-list-a',
12 mixins: [scrollPosition],
13 data() {
14 return {
15 }
16 },
17 beforeRouteLeave(to, from, next) {
18 if (['detail'].some(name => name === to.name)) {
19 from.meta.keepAlive = true;
20 this.$store.dispatch('setCachedPage', from.name).then(() => {
21 next()
22 });
23 } else {
24 from.meta.keepAlive = false;
25 this.$store.dispatch('removeCachedPage', from.name).then(() => {
26 next();
27 });
28 }
29 }
30 };
31</script>
32<style lang="less" scoped>
33</style>
/views/detail.vue
1<template>
2 <div>
3 i am detail
4 </div>
5</template>
6
7<script type="text/ecmascript-6">
8 export default {
9 name: 'page-detail',
10 data() {
11 return {
12 }
13 },
14 beforeRouteLeave(to, from, next) {
15 if (['list-a'].some(name => name === to.name)) {
16 from.meta.keepAlive = true;
17 this.$store.dispatch('setCachedPage', from.name).then(() => {
18 next()
19 });
20 } else {
21 from.meta.keepAlive = false;
22 this.$store.dispatch('removeCachedPage', from.name).then(() => {
23 next();
24 });
25 }
26 }
27 };
28</script>
29<style lang="less" scoped>
30</style>
list-b.vue
1<template>
2 <div>
3 i am list b
4 </div>
5</template>
6
7<script type="text/ecmascript-6">
8
9 export default {
10 name: 'page-list-b',
11 data() {
12 }
13</script>
14<style lang="less" scoped>
15</style>
/router/index.js
1import Vue from 'vue'
2import VueRouter from 'vue-router'
3
4// 重写新版VueRouter的push和replace方法的异常捕捉
5const originalPush = VueRouter.prototype.push;
6VueRouter.prototype.push = function push(location, onResolve, onReject) {
7 if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject);
8 return originalPush.call(this, location).catch(err => err)
9};
10Vue.use(VueRouter);
11
12const routes = [
13 {
14 path: '/list-a',
15 name: 'list-a',
16 meta: {
17 title: '列表页A'
18 },
19 component: () => import ('../views/list-a')
20 },
21 {
22 path: '/detail',
23 name: 'detail',
24 meta: {
25 title: '详情页',
26 keepAlive: true
27 },
28 component: () => import ('../views/detail')
29 },
30 {
31 path: '/list-b',
32 name: 'list-b',
33 meta: {
34 title: '列表页B'
35 },
36 component: () => import ('../views/list-b')
37 }
38];
39
40const router = new VueRouter({
41 mode: 'history',
42 routes
43});
44
45export default router