Sample code of how to implement page caching in vue mobile project

  • 2021-11-01 23:10:18
  • OfStack

Background

On the mobile side, caching between page jumps is a necessary requirement.

For example: Home page = > List page = > Details page.

From the first page to the list page, the list page needs to be refreshed, while from the details page to the list page, the list page needs to keep the page cache.

For the home page, we will keep it in a cached state.

For the details page, no matter which entrance it enters from, it will be refreshed again.

Realization thought

When it comes to page caching, in vue, we have to mention keep-alive component, and keep-alive provides routing caching function. This paper mainly realizes page jump caching in applications based on it and vuex.

vuex maintains an array cachePages to save the pages that need to be cached at present.
includes for keep-alive is set to cachePages.
Route meta adds custom fields needCachePages or keepAlive, needCachePages is an array, which means that if the page to be entered by the route is in the array, the route will be cached, and keepAlive means that no matter which page is entered, it will be cached, such as the first page of app.
In the routing guard beforeEach, if the route page to jump is in the needCachePages of the current route, the current route is added to cachePages, otherwise, it is deleted.

Concrete realization

vuex Implementation Content


// src/store/modules/app.js

export default {
 state: {
  //  Page cache array 
  cachePages: []
 },
 
 mutations: {
  //  Add a cache page 
  ADD_CACHE_PAGE(state, page) {
   if (!state.cachePages.includes(page)) {
    state.cachePages.push(page)
   }
  },
  
  //  Delete cached 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'

//  Export  store  Object 
export default new Vuex.Store({
 getters,
 modules: {
  user,
  app
 }
})

In App. vue, include of keep-alive sets cachePages


<keep-alive :include="cachePages">
 <router-view :key="$route.fullPath"></router-view>
</keep-alive>

computed: {
 ...mapGetters([
  'cachePages'
 ])
}

Routing configuration


{
  path: '/home',
  name: 'Home',
  component: () => import('@/views/tabbar/Home'),
  meta: {
   title: ' Home page ',
   keepAlive: true
  }
},
{
  path: '/list',
  name: 'List',
  component: () => import('@/views/List'),
  meta: {
   title: ' List page ',
   needCachePages: ['ListDetail']
  }
},
{
  path: '/list-detail',
  name: 'ListDetail',
  component: () => import('@/views/Detail'),
  meta: {
   title: ' Details page '
  }
}

Routing guard


import Vue from 'vue'
import Router from 'vue-router'
import store from '@/store'
Vue.use(Router)

//  Import modules All routes in the folder 
const files = require.context('./modules', false, /\.js$/)
let modules = []
files.keys().forEach(key => {
 modules = modules.concat(files(key).default)
})

//  Route 
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)
})

//  Global routing guard 
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)
 }
 //  The first cache failure of the page occurs, and the guess is vuex To keep-alive Reasons for Cache Delays 
 // Delay is used here 100 Millisecond solution 
 setTimeout(() => {
  next()
 }, 100)
})

export default router

Restore the page scroll bar position

At this time, although the page is cached, the scroll bar will return to the top every time.

For cached pages, two hooks, activated and deactivated, are triggered, which can be used to restore the scroll bar position.

Record the scroll bar position when the page leaves, that is, when deactivated triggers.

Restore the scroll bar position when returning to the page, that is, when activated triggers.


//  Create 1 A 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()
  }
 }
}

It is found here that when using deactivated, the height of the scroll bar of the obtained node will be 0 because the page is hidden too fast, so beforeRouteLeave is used.

Use in pages that need to be cached


<script>
import { savePosition } from '@/mixins'

export default {
 mixins: [new savePosition()]
}
</script>

If the page has a custom scroll container, you can pass in the scroll container id at this time


<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>

Attention

My friends often come to ask me a question, why do I configure but have no cache effect?

At this time, you need to pay attention to one problem. The key of keep-alive is to keep name in the route and name in the. vue file.

If your cache does not work, first check for 1 in the next two name and needCachePages.

Thoughts and deficiencies

This scheme is my practice more than one year ago, but now I think about it, there are still some shortcomings, such as configuring needCachePages in the route every time.

In fact, in the mobile terminal, when returning to the previous page, the previous page always keeps the cached state, just like when developing small programs. When we call navigateTo and then return, the page is always cached and does not need any artificial configuration.

Therefore, the current idea is to provide a global jump api in vue, and cache the current page as long as the api is called. If the refresh operation is needed, you can execute your logic in activated like onShow1 in the applet.


Related articles: