web有严重的内存泄漏,因此要进行性能排查并进行优化

@TOC

1、如何定位到有内存泄漏

操作步骤:

1.1 打开web页面的开发者选项,打开控制台 1.2 打开性能监视器,或者是Memory(只要能看懂啊js堆信息就行) 1.3 主要关注JS堆大小的数值 1.4 重复多次打开要测试的页面,看JS堆大小是否持续增加,持续增加点击回收垃圾按钮JS堆未变小,则是内存泄漏

2、导致js内存泄露的常见原因(开发的时候就要注意)

这篇就主要讲那些不合适的代码组织方式,可能会导致内存泄漏(vue框架)

2.1 eventBus未销毁

注:vue3已不支持这种模式,不利于代码维护。组件内部通信慎用。

// 错误demo
// 只$on但不在组件销毁中销毁,导致eventBus 模块对象持有组件上下文,组件内存不释放,内存泄漏
export default {
  created() {
    eventBus.$on('mylisntenr', ()=>{
      console.log('do something');
    });
  }
};
// 错误demo
// 只$off未取消对应绑定的方法,而是$off全部mylisntenr事件对例,会影响其调用
export default {
  created() {
    eventBus.$on('mylisntenr',()=>{
     console.log('do something');
   });
  },
  beforeDestroy() {
    eventBus.$off('mylisntenr');
  }
};

// 正确demo
// 组件创建时注册监听,组件销毁时反注册监听,释放组件上下文。【注】:mylisntenr要同一个函数,故要用变量保存
export default {
  created() {
    eventBus.$on('mylisntenr', this.mylisntenr);
  },
  beforeDestroy() {
    eventBus.$off('mylisntenr', this.mylisntenr);
  },
  methods: {
    mylisntenr() {
      console.log('do something');
    }
  }
};

2.2 setInterval(定时器)没有销毁

beforeDestroy() {
    if (this.heartInterval) {
      clearInterval(this.heartInterval);
      this.heartInterval = null;
    }
  },

2.3 addEventListener

dom清空或删除时,事件未清除导致的内存泄漏

beforeDestroy() {
    console.log('destroyed');
    document.removeEventListener('click', this.blurPop);
  },

2.4 匿名函数(bind方法为例)导致无法解绑

bind方法会创建一个新函数

javascrpt绑定事件---匿名函数无法解除绑定(谬论,匿名函数当然可以解绑

demo:

this.handlder.on('click', this.clickSayHi.bind(this))
// 上下文就不写了,大致就是handlder有个on回去绑定事件,那肯定就会有个off去进行解绑事件,这么一搞,匿名函数就不好解绑了

结论:尽量不要搞个匿名函数出来,实在是不好捕捉到这个函数,从而无法进行精确控制

匿名函数如何捕捉进行处理?

var dom=document.getElementById("test");
   var sum=0; 
   dom.addEventListener("click",function(e){ 
   clickt++; 
   alert('你click了我' + sum +'下了。最多click 2下哦'); 
   if(sum>=2){
    console.log("11",e);
    this.removeEventListener(e.type,arguments.callee,false); } 
   });

2.5 第三方组件库,vue组件销毁的时候要调用三方库的销毁方法

Vue只对自己的生命周期做管理,三方组件如jquery插件等尤其是dom操作的,Vue无法管理,三方组件的销毁要自行处理

chart.dispose();需要放到chart.clear();前面,否则并没有真正清除占用的内存

//demo: echarts 图表销毁
 beforeDestroy() {
    if (this.echart) {
      this.echart.dispose();
      this.echart.clear();
    }
  },

2.6 包洋葱(一层,一层,剥开我的心)

//若Obj是共享的全局变量,则若每次进入会执行该逻辑就会导致方法 “包洋葱”,要注意
let oldMethod = Obj.myMethod;

Obj.myMethod = function(){

  oldMethod.bind(this);

  //自己的逻辑

}

2.7 正确的for循环

for(let i = 0;i < this.count; i++){ i++) {
// do something
}
// 下面这个性能会更好(count就获取一次值)
for(let i = 0, count = 1000;i < count; i++){ i++) {
// do something
}

2.8 vue2滥用钩子函数

同时存在多个this.$once('hook:beforeDestroy')会导致内存泄漏,hook:beforeDestroy,如果单个组件内同时存在多个就会内存泄漏

2.9 变量瞎搞

// 正确的demo: 方法执行完变量也就销毁了
function sayHi() {
  let name = "kate"
  console.log(name)
}
// 错误的demo: 方法执行完变量并不会销毁
function sayHi() {
  name = "kate" // 成了全局变量
  console.log(name)
}

2.9 浏览器插件

某些浏览器的插件也会有严重的内存泄漏,建议开无痕或者关闭插件(插件关闭,重启浏览器生效) 例如:谷歌翻译插件

2.10 闭包(自不去说他)

let Kate = (funcion sayHI() {
  let name = "kate" // // 被闭包所引用,不会被回收
  rturn function() {
    console.log(name) 
  }
}) ()

2.11 console

如果是在生产环境进行性能问题的排查,console在控制台打印出来,在内存中肯定是不会被销毁掉的,也会有一定的性能消耗