Keepalive in vue2 manually cleans up the memory, and there is a problem that the sub-routing memory cannot be reclaimed

cause

Recently, customers often report the problem of system crashes, especially in the afternoon. After self-testing, I found that the system memory is not recovered after the tab is closed. I have already dealt with it. After the tab is closed, manually clear the keep-alive There should be no memory leaks in the internal cache. It seems that there are other caches that have not been cleaned up.

positioning problem

1. Restoring the scene

The company's project is a single-page application, and all operations are performed in a browser tab. The entire page is laid out through Layou + sub-routing. The routing level reaches level 4, and the business is complex and cumbersome. Need to rebuild a pure project restoration scene

2. Write a demo

Create a project using vue-cli, [email protected],[email protected]

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false

new Vue({render: h => h(App),router
}).$mount('#app') 

app.vue

<template><router-view></router-view>
</template>

<script> export default {name: "App",
}; </script> 

view/Page1、view/Page2、view/A1、view/A2

<template><div>Page1<router-view></router-view></div>
</template>

<script> export default {name: "Page1",
}; </script>

<template><div>Page2<router-view></router-view></div>
</template>

<script> export default {name: "Page2",
}; </script>

<template><div>组件view A1</div>
</template>
<script> export default {name: "A1",data() {return {a: new Array(20000000).fill(1), //大概80mb};},
}; </script>

<template><div>组件view A2</div>
</template>
<script> export default {name: "A2",data() {return {a: new Array(20000000).fill(1), //大概80mb};},
}; </script> 

view/Layout.vue

<template><div><h1>Layout</h1><div class="box"><p>二级路由</p><router-link :to="{ name: 'A' }">A</router-link><br /><router-link :to="{ name: 'B' }">B</router-link></div><div class="box"><p>三级路由</p><router-link :to="{ name: 'AA' }">Page1</router-link><br /><router-link :to="{ name: 'BB' }">Page2</router-link></div><div class="box"><button @click="includeRemove()">清理keepalive缓存</button><br /><router-link to="/home">Home</router-link><br /></div><h1>keep-alive</h1>缓存页面:{
   
   { include }}<keep-alive :include="include"><router-view ref="alive"></router-view></keep-alive></div>
</template>

<script> export default {name: "Layout",data() {return {include: [],};},watch: {'$route'(val) {const name = val.meta.nameif (name && !this.include.includes(name)) {this.include.push(name);}}},methods: {includeRemove() {this.include = [];},},mounted() {},
}; </script>
<style> .box {margin-bottom: 20px;
} </style> 

router/index.js

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

const router = new Router({mode: "hash",routes: [{path: "/",redirect: "/home",component: () => import("../view/Layout.vue"),children: [{path: "home",component: () => import("../view/Home.vue"),},],},{path: "/a",component: () => import("../view/Layout.vue"),children: [{path: "a",name: "A",meta: {name: 'A1'},component: () => import("../view/A1.vue"),},],},{path: "/b",component: () => import("../view/Layout.vue"),children: [{path: "b",name: "B",meta: {name: 'A2'},component: () => import("../view/A2.vue"),},],},{path: "/a",component: () => import("../view/Layout.vue"),children: [{path: "page1",name: "Page1",component: () => import("../view/Page1.vue"),children: [{path: "a",name: "AA",meta: {name: 'Page1'},component: () => import("../view/A1.vue"),},],},],},{path: "/b",component: () => import("../view/Layout.vue"),children: [{path: "page2",name: "Page2",component: () => import("../view/Page2.vue"),children: [{path: "b",name: "BB",meta: {name: 'Page2'},component: () => import("../view/A2.vue"),},],},],},],
});

export defaultrouter 

running result

3. Reproduce the problem

It can be seen that in the initial case, the memory usage is about 7.7MB

1. After clicking A and B, the memory occupies 168MB

2. Click Home to ensure that the routing does not occupy components A and B when clearing the cache, and click again to clear the keepalive cache

After manual GC, it was found that the memory usage had changed to 7.9MB, indicating that components A and B were successfully released. This simulated the situation that the company had only secondary routing in the early stage of the project. At that time, the problem of system crash did not exist, which is just confirmed here .

3. Click Page1, Page2, the memory usage is 168MB

4. Click Home, click again to clear the keepalive cache

A problem occurred at this time, the memory was not released successfully, the problem was found

4. Analysis

First record the memory usage in the initial case

Open Page1 and Page2, switch to the Home page, clear the keepalive cache, and record the current memory snapshot

It can be seen from the figure that the A1 component still exists, and is referenced by vue-router, and nameMap saves all routing information, so the problem is found

Routing information in the initial state

Open Page1 and Page2, switch to the Home page, and clear the routing information after the keepalive cache

Test another situation, do not switch to Home before cleaning the keepalive cache, in this case, the memory can be released successfully.

5 Conclusion

1. If the tab is closed on the current route, and then the keepalive cache is cleared, the memory can be recycled normally.
2. If the inactive route is closed on other routes, the second-level routing components can be recycled normally, and the memory recovery of the lower-level routes is abnormal , Guessing that the information in the inactive route matched has been changed. After all, it is a singleton mode, which makes sense. Why is it normal for the active route to remove the cache?

3. Fix the problem

1. Idea

1. Obtain and close the current tab routing parent-child relationship
2. Through all routing information, traverse and delete the instances.defaultvalue of related routing

2. Specific code implementation

includeRemove() {this.include = [];// 为啥vue-router不开放直接获取nameMap的接口 淦const routes = this.$router.getRoutes()const nameMap = new Map()for (let index = 0; index < routes.length; index++) {const r = routes[index];nameMap.set(r.name, r)}// 假设我这边获取到了当前移除的tab页签,具体代码根据具体项目实现const rList = ['AA', 'BB']for (let index = 0; index < rList.length; index++) {const name = rList[index];const r = nameMap.get(name)if (r) r.instances.default = undefined}
} 

After the code modification, go through the process again, the memory usage is as follows, and the problem is successfully solved

At last

Recently, I also sorted out a JavaScript and ES note, a total of 25 important knowledge points, and explained and analyzed each knowledge point. It can help you quickly master the relevant knowledge of JavaScript and ES, and improve work efficiency.



Friends in need, you can click the card below to receive and share for free

Guess you like

Origin blog.csdn.net/web22050702/article/details/128713589