背景
在移动端中,页面跳转之间的缓存是必备的一个需求。
例如:首页=>列表页=>详情页。
从首页进入列表页,列表页需要刷新,而从详情页返回列表页,列表页则需要保持页面缓存。
对于首页,一般我们都会让其一直保持缓存的状态。
对于详情页,不管从哪个入口进入,都会让其重新刷新。
实现思路
说到页面缓存,在vue中那就不得不提keep-alive组件了,keep-alive提供了路由缓存功能,本文主要基于它和vuex来实现应用里的页面跳转缓存。
vuex里维护一个数组cachePages,用以保存当前需要缓存的页面。
keep-alive 的 includes 设置为cachePages。
路由meta添加自定义字段 needCachePages或keepAlive,needCachePages 为一个数组,表示该路由要进入的页面如果在数组内,则缓存该路由,keepAlive则表示无论进入哪个页面都保持缓存,如app首页这种。
在路由守卫beforeEach里判断,如果要跳转的路由页面在当前路由的needCachePages里,则当前路由添加进cachePages里,反之删除。
具体实现
vuex实现内容
// src/store/modules/app.js
export default {
state: {
// 页面缓存数组
cachePages: []
},
mutations: {
// 添加缓存页面
ADD_CACHE_PAGE(state, page) {
if (!state.cachePages.includes(page)) {
state.cachePages.push(page)
}
},
// 删除缓存页面
REMOVE_CACHE_PAGE(state, page) {
if (state.cachePages.includes(page)) {
state.cachePages.splice(state.cachePages.indexOf(page), 1)
}
}
}
}
// src/store/getters.js
const getters = {
cachePages: state => state.app.cachePages
}
export default getters
// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import user from './modules/user'
import app from './modules/app'
import getters from './getters'
// 导出 store 对象
export default new Vuex.Store({
getters,
modules: {
user,
app
}
})
App.vue里,keep-alive的include设置cachePages
<keep-alive :include="cachePages">
<router-view :key="$route.fullPath"></router-view>
</keep-alive>
computed: {
...mapGetters([
'cachePages'
])
}
路由配置
{
path: '/home',
name: 'Home',
component: () => import('@/views/tabbar/Home'),
meta: {
title: '首页',
keepAlive: true
}
},
{
path: '/list',
name: 'List',
component: () => import('@/views/List'),
meta: {
title: '列表页',
needCachePages: ['ListDetail']
}
},
{
path: '/list-detail',
name: 'ListDetail',
component: () => import('@/views/Detail'),
meta: {
title: '详情页'
}
}
路由守卫
import Vue from 'vue'
import Router from 'vue-router'
import store from '@/store'
Vue.use(Router)
// 导入modules文件夹里的所有路由
const files = require.context('./modules', false, /\.js$/)
let modules = []
files.keys().forEach(key => {
modules = modules.concat(files(key).default)
})
// 路由
const routes = [
{
path: '/',
redirect: '/home',
},
...modules
]
const router = new Router({
mode: 'hash',
routes: routes
})
function isKeepAlive(route) {
if (route.meta && route.meta.keepAlive) {
store.commit('ADD_CACHE_PAGE', route.name)
}
if (route.children) {
route.children.forEach(child => {
isKeepAlive(child)
})
}
}
routes.forEach(item => {
isKeepAlive(item)
})
// 全局路由守卫
router.beforeEach((to, from, next) => {
if (from.meta.needCachePages && from.meta.needCachePages.includes(to.name)) {
store.commit('ADD_CACHE_PAGE', from.name)
} else if (from.meta.needCachePages) {
store.commit('REMOVE_CACHE_PAGE', from.name)
}
// 出现页面首次缓存失效的情况,猜测是vuex到keep-alive缓存有延迟的原因
//这里使用延迟100毫秒解决
setTimeout(() => {
next()
}, 100)
})
export default router
还原页面滚动条位置
此时虽然页面实现缓存了,但滚动条每次都会重新回到顶部。
对于缓存的页面,会触发activated和deactivated这两个钩子,可以利用这两个钩子来实现还原滚动条位置。
在页面离开时,也就是deactivated触发时记录滚动条位置。
在重新回到页面时,也就是activated触发时还原滚动条位置。
// 创建一个mixin
// src/mixins/index.js
export const savePosition = (scrollId = 'app') => {
return {
data() {
return {
myScrollTop: 0
}
},
activated() {
const target = document.getElementById(scrollId)
target && target.scrollTop = this.myScrollTop
},
beforeRouteLeave(to, from, next) {
const target = document.getElementById(scrollId)
this.myScrollTop = target.scrollTop || 0
next()
}
}
}
这里发现使用deactivated时会因为页面隐藏过快会导致获取的节点滚动条高度为0,所以用beforeRouteLeave。
在需要缓存的页面中使用
<script>
import { savePosition } from '@/mixins'
export default {
mixins: [new savePosition()]
}
</script>
如果页面自定义了滚动容器,此时可以传入滚动容器id
<template>
<div id="scroll-container" style="height: 100vh; overflow-y: scroll;">
</div>
</template>
<script>
import { savePosition } from '@/mixins'
export default {
mixins: [new savePosition('scroll-container')]
}
</script>
注意
我的小伙伴经常会来问我一个问题,为什么我配置了却没有缓存的效果?
这个时候你就需要注意一个问题了,keep-alive的一个关键是路由里的name要和.vue文件里的name保持一致。
如果你的缓存没有生效,请首先检查一下两个name和needCachePages里是否一致。
思考与不足
此方案是我一年多前的做法,现在想来其实还是存在一些不足的,比如每次都需要去配置路由里的needCachePages。
而实际上在移动端中,往往都是在返回上一页时,上一页始终保持缓存的状态,就如开发小程序时一样,当我们调用navigateTo后再返回,页面始终是缓存的并不需要任何人为的配置。
所以现在的想法是,在vue中提供一个全局的跳转api,只要调用该api就把当前页面缓存,如果需要刷新操作,可以像小程序里的onShow一样在activated里执行你的逻辑。
到此这篇关于vue移动端项目中如何实现页面缓存的示例代码的文章就介绍到这了,更多相关vue 页面缓存内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论