This post is also posted on my blog at 😄
Writing in the front
In the actual use scenarios of a Web App, there are some interactive requirements to record the browsing status of users. The most common is that after the list page enters the detail page, the user wants to return to the state before entering the detail page to continue operation. But there are some scenarios where users want to be able to get the latest data, such as when switching between peer list pages.
Thus, for both of the above usage scenarios, you need to implement on-demand page caching. As the routing logic of SPA application is also implemented in the front end, the routing logic can be set in the front end to achieve the desired effect.
The use of the technical
- Vue.js as the main framework
- Vue-router functions as the front-end route manager
- Vuex as a state management tool
The overall train of thought
Keep-alive determines whether the current component is reading the cached node, very late in the life cycle, after afterEach, and basically before the component instance is created. (So it is possible to handle whether or not the current component reads from the cache until then, I choose to handle it in the global front-guard.)
The node that determines whether the current component is cached precedes the component’s beforeRouteLeave hook.
Based on the above logic, the logic of this solution is to judge the current open page, dynamically generate the configuration of the keepAlive component array, cache the cache that may need to be cached first, and then judge and read the page cache as needed during each route switch.
- Use kepp-alive for caching and use the include attribute to configure the pages to be cached.
- Because the page configuration that needs to be cached is dynamically generated, use VUEX to store that configuration.
- Two configurations are written into the route meta-information, one is whether the route needs to be cached or not, and the other is the specific route array that is cached only when the related route comes in.
- BeforeEach entering a route, the incoming route and all its parent routes are judged before entering a route. If a specific route array needs to be cached and matched, related routes are added to the cache configuration file. If no, delete the route. (This step enables the cache to be read if necessary and the data to be retrieved if not required.)
- With a global mixin, the current route is evaluated before entering the relevant component, and if it needs to be cached, the route is added to the cache configuration. (This step implements caching of currently open pages that need to be cached.)
The specific implementation
1. Use the include attribute to control the route cache
Note here that include matching first checks for the name option of the component itself, and if the name option is not available, matches its locally registered name (the key value of the parent component’s components option). The anonymous component could not be matched.
However, in the vue-router environment, there is no local registered name and only the name attribute can be completed for the component.
Therefore, be sure to add the name option to the component, otherwise anonymous components will all be cached.
<keep-alive :include="$store.state.cachedRouteNames">
<router-view />
</keep-alive>
Copy the code
2. Add the global route cache configuration
// store/index.js
const store = new vuex.Store({
state: {
// A list of cached routes
cachedRouteNames: [],
},
mutations: {
UPDATE_CACHEDROUTENAMES(state,{ action, route }) {
const methods = {
'add': (a)= > {
state.cachedRouteNames.push(route)
},
'delete': (a)= > {
state.cachedRouteNames.splice(state.cachedRouteNames.findIndex((e) = > { return e === route}),1)
}
}
methods[action]()
}
}
})
Copy the code
3. Configure the route meta information and configure the routes to be cached
KeepAlive indicates that routes need to be cached
CacheWhenFromRoutes is an array; it is not required; if falsy, it is cached at any time; If it is an empty array, it is not cached at any time
// router/index.js
{
path: '/productslist'.name: 'ProductsList'.component: ProductsList,
meta: {
keepAlive: true.cacheWhenFromRoutes: ['ProductDetail'] // The route name is configured here}},Copy the code
4. Configure global front-guard to read cache on demand
// routeControl.js
// An array of route names to cache
const cachedRouteNames = store.state.cachedRouteNames;
// Define add cache component name function, set component name
const addRoutes = (route) = > {
const routeName = route.components.default.name
if (routeName && cachedRouteNames.indexOf(routeName) === - 1) {
store.commit('UPDATE_CACHEDROUTENAMES', { action: 'add'.route: routeName })
}
}
// Define delete cache component name function, set component name
const deleteRoutes = (route) = > {
const routeName = route.components.default.name
if(routeName && cachedRouteNames.indexOf(routeName) ! = =- 1) {
store.commit('UPDATE_CACHEDROUTENAMES', { action: 'delete'.route: routeName })
}
}
router.beforeEach((to, from, next) = > {
// Start processing cached routes
// Whether the component reads the cache is processed before it reads the cache
to.matched.forEach((item, index) = > {
const routes = item.meta.cacheWhenFromRoutes;
/** * there are several cases * 1. CacheWhenFromRoutes are not configured; When you open the Web app for the first time, from.name is null, and the name of the page component should be added to the cache configuration file * 3. If cacheWhenFromRoutes is configured, from.name is not empty. If cacheWhenFromRoutes is matched, add the name of the page component to the cache profile, otherwise delete it. * * * /
if(item.meta.keepAlive && (! routes || (routes && (!from.name || routes.indexOf(from.name) ! = =- 1)))) {
addRoutes(item)
} else {
deleteRoutes(item)
}
})
// Process the cached route
new Promise(( resolve, reject ) = > {
/ /.. other codes
}).then( res= > {
if ( res ) {
next(res)
} else {
next()
}
})
})
// global mixin. The purpose of this step is to add the component that needs to be cached to the cache configuration after it has been parsed.
// The last step in the navigational guard is to call the callback passed to Next in the beforeRouteEnter guard, at which point the entire component has been parsed and the DOM has been updated.
Vue.mixin({
beforeRouteEnter(to, from, next) {
next(vm= > {
to.matched.forEach((item) = > {
const routeName = item.components.default.name
if (to.meta.keepAlive && routeName && cachedRouteNames.indexOf(routeName) === - 1) {
store.commit('UPDATE_CACHEDROUTENAMES', { action: 'add'.route: routeName })
}
})
})
},
})
Copy the code
Write in the last
Pit point
- This scheme involves two names. One is the name of the route to be used when setting up a specific route. The other is to dynamically generate the cache configuration file using the name of the page component.
- Be sure to add the name attribute to the component to make it easier to use the include attribute and debug trace. If the component lacks the name attribute, caching is used by default.
- In the process of dynamic cache configuration, to.matched must be traversed; otherwise, the cache of parent routes of nested routes cannot take effect, and the cache of child routes cannot take effect as well.
- Global mixing has a certain risk, careful use…
The above is a solution found in the practice process. I believe there is a more elegant and efficient solution. If you happen to practice the relevant methods, please correct, thank you.
More reference
Github.com/vuejs/vue/i…