background

Caching between page hops is an essential requirement on mobile.

For example, home page => List page => Details page.

To enter the list page from the home page, the list page needs to be refreshed, and to return to the list page from the details page, the list page needs to be cached.

For the home page, we usually keep it in cache at all times.

The details page is refreshed regardless of which entry it enters from.

Implementation approach

When it comes to page caching, keep-Alive component has to be mentioned in VUE. Keep-alive provides the route caching function. This article is mainly based on it and VUex to realize the page skipping caching in the application.

  1. Vuex maintains an array of cachePages to hold pages currently cached.
  2. The includes of keep-alive is set to cachePages.
  3. Route Meta Add a custom needCachePages or keepAlive field. NeedCachePages is an array, indicating that the route is cached if the page to be entered by the route is in the array. KeepAlive indicates that the route is cached no matter which page is entered, such as the home page of the app.
  4. Determine in the routing guard beforeEach that the current route is added to the cachePages if the route page to be jumped is in needCachePages of the current route, and deleted if not.

The specific implementation

  1. Vuex implements content
// src/store/modules/app.js

export default {
  state: {
    // Page cache array
    cachePages: []},mutations: {
    // Add cached pages
    ADD_CACHE_PAGE(state, page) {
      if(! state.cachePages.includes(page)) { state.cachePages.push(page) } },// Delete the cached page
    REMOVE_CACHE_PAGE(state, page) {
      if (state.cachePages.includes(page)) {
        state.cachePages.splice(state.cachePages.indexOf(page), 1)}}}}Copy the code
// src/store/getters.js

const getters = {
  cachePages: state => state.app.cachePages
}
export default getters

Copy the code
// 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 default new vuex. store ({getters, modules: { user, app } })Copy the code
  1. In app. vue, keep-alive include is set to cachePages
<keep-alive :include="cachePages">
  <router-view :key="$route.fullPath"></router-view>
</keep-alive>computed: { ... mapGetters([ 'cachePages' ]) }Copy the code
  1. The routing configuration
{
    path: '/home'.name: 'Home'.component: () = > import('@/views/tabbar/Home'),
    meta: {
      title: 'home'.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'}}Copy the code
  1. Routing guard
Import Vue from 'Vue' import Router from 'vue-router' import store from '@/store' vue. use(Router) // Import all routes in the modules 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 router guard. BeforeEach ((to, from, next) => { if (from.meta.needCachePages && from.meta.needCachePages.includes(to.name)) { store.commit('ADD_CACHE_PAGE', NeedCachePages) {store.com MIT ('REMOVE_CACHE_PAGE', from. Name)} SetTimeout (() => {next()}, 100)}) export default routerCopy the code

Restores the page scroll bar position

The page is cached, but the scroll bar goes back to the top every time.

For cached pages, the Activated and deactivated hooks are triggered and can be used to restore the scrollbar position.

Record the scroll bar position when the page leaves, which is when DeActivated triggers.

Restore the scroll bar position when you return to the page, which is triggered by Activated.

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

Copy the code

If deActivated is used, the scrollbar height is 0 because the page is hidden too fast. Therefore, beforeRouteLeave is used.

Used in pages that need to be cached

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

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

If the page has a custom scroll container, you can pass in the scroll container 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>Copy the code

Pay attention to

My friends often ask me a question, why I configure but there is no caching effect?

One of the keys to keep-alive is that the name in the route matches the name in the.vue file.

If your cache is not in effect, first check that the two names are the same in needCachePages.

Thinking and inadequacy

I did this over a year ago, but it still has some drawbacks, such as needing to configure needCachePages in the route every time.

In fact, on the mobile end, when you go back to the previous page, the previous page is always cached, just like in the development of small programs, when you call navigateTo and come back, the page is always cached and you don’t need any manual configuration.

So the idea is to provide a global jump API in Vue that caches the current page whenever it’s called, and if you need to refresh, you can do your logic in Activated just like onShow in the applet.