specialgirlgotoheaven / MyBlog

MyBlog
0 stars 0 forks source link

内存泄漏(难点) #152

Open specialgirlgotoheaven opened 5 years ago

specialgirlgotoheaven commented 5 years ago

1.优化IE下的内存泄漏问题 系统有各个业务模块,各业务模块的菜单权限由后端配置,各个模块间是相互独立的,打开某个模块采用iframe嵌入的方式,交互场景是打开一个模

块就生成一个tab页签(仿造chrome页签的方式),可以对该页签进行关闭和相互切换。 当前就有一个问题,不同的页签之间切换,IE下内存就会飙升,故判断存在内存泄漏。 (不同的iframe是用v-for循环出来的)不同的iframe之间的显示是采用v-show的方式展现的,VUE当中,v-show的方式是不销毁组件的,v-if会销毁

组件,若采用v-if会重新渲染组件,显然这样是不合理的,iframe与数据存在绑定关系,当我们移除当前iframe后,即失去了与数据的关联(用户无

法保留当前现场)。所以不能采用v-if去解决这个问题。

在发现内容泄露问题以及预研后定位原因后,最初的思路是将iframe移除后,在重新创建。但马上会发现一个问题。与传统项目不同,因为iframe是

通过v-for的方式进行渲染的。因此,iframe与数据存在绑定关系,当我们移除当前iframe后,即失去了与数据的关联。会对后面的功能有很大的影

响。

所以对iframe的渲染做了改进,对iframe进行了一层封装:iframe外层包上父级容器,循环数据绑定在父级容器上,对传入数据进行监听,当数据变

化时,先将iframe地址指向空,并将当前iframe内容置为空,最后重新加入iframe节点,内存得以释放。

// 此处为封装后的iframe使用方式 <tab-iframe v-for="menu in tabMenus" :key="menu.menuId" :menu="menu" v-show="activeTabMenu === menu.menuId">

// 内部封装容器,用以承载iframe插入

//监听传入的菜单数据变化,改变iframe的创建和销毁 watch: { menu: { handler () { // 这里需要先判断被打开的tab菜单不是首页菜单,因为首页菜单是不会被关闭的,且内容是通过路由的方式进行展示的。 // 同时我们也需要判断,当前的tab切换自身的时候,不能重新渲染,以避免造成不必要的性能消耗。 if (this.menu.menuId !== 'home' && this.menu._info.showUrl !== this.oldMenu._info.showUrl) { // 获取iframe容器 Dom对象 let iframeWrap = document.getElementById(iframeWrap${this.menu.menuId}); // 获取将要被移除的iframe对象 let iframe = document.getElementById(iframe${this.oldMenu.menuId}); // 通过contentWindow属性获得iframe中的HTML对象 let _iframe = iframe.contentWindow; // 这里做一层防报错处理 if (iframe) { // 现将iframe地址指向空,并将其document内容置空后清除 iframe.src = 'about:blank'; try { _iframe.document.write(''); _iframe.document.clear(); } catch (e) { console.log(e); } // iframe内容被清空后,将该对象移除以达到释放内存的目的 iframeWrap.removeChild(iframe); } // 移除iframe对象后,再重新创建对象,并对其属性进行赋值 let newIframe = document.createElement('iframe'); newIframe.style.width = '100%'; newIframe.style.height = '100%'; newIframe.setAttribute('id', iframe${this.menu.menuId}); newIframe.setAttribute('src', this.menu._info.showUrl); newIframe.setAttribute('frameborder', 0); iframeWrap.appendChild(newIframe); this.oldMenu = JSON.parse(JSON.stringify(this.menu)); } }, deep: true }, }

  1. 第三方插件内存泄漏问题

    // 路由跳转时会触发destroyed钩子函数,因此我们将销毁echarts实例放在该方法中调用。 destroyed () { this.myChart.dispose() }

3.当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。比如:vue

使用了定时器,需要在beforeDestroy 中做对应销毁处理。js也是一样的。

4.VUE中如果在mounted/created 钩子中使用了$on,需要在beforeDestroy 中做对应解绑($off)处理

5.给DOM对象添加的属性是一个对象的引用 var testObject = {}; document.getElementById('idname').property = testObject; // 如果DOM不被消除,则testObject会一直存在,造成内存泄漏 window.onunload=function(){ document.getElementById('idname').property = null; //释放内存 }