美文网首页@IT·互联网LOVETOO-前端编程
前端vue项目内存泄漏排查总结

前端vue项目内存泄漏排查总结

作者: 车文烨 | 来源:发表于2022-06-10 16:39 被阅读0次

    前端vue项目内存泄漏排查总结

    未完待续。。。

    背景

    1. 项目由vue-element-admin改造,二次封装了element-ui,组件套的有点深;
    2. 系统上线一段时间后,业务频繁反馈页面崩溃;
    3. 项目使用了页面缓存,移除的话没问题,但因业务需要不能移除:
      <transition name="fade-transform" mode="out-in">
        <keep-alive :include="cachedViews">
          <!-- 匹配三级路由,并缓存 -->
          <router-view :key="key" />
        </keep-alive>
      </transition>
      

    排查

    1. 使用谷歌开发工具面板performancememory排查,打快照,发现详情页内存一直累积,无法释放;
    2. 列表页和详情页引用了公共组件(注意import引入的资源在内存中实质上是同一个对象);
    3. memory排查分析分析累积的都是些vue组件;
    4. 注意组件的name与路由的name保持一致且唯一,不然用keep-alive无法正常缓存(通过componentOptions.Ctor.options.name || componentOptions.tag决定include包含的组件缓存);

    解决方案

    1. 缓存的页面走keep-alive,不缓存的把router-view移到外面,即:
      <transition name="fade-transform" mode="out-in">
        <div>
          <keep-alive :include="cachedViews" :max="10">
            <!-- 匹配三级路由,并缓存 -->
            <router-view v-if="!$route.meta.noCache" :key="key" />
          </keep-alive>
          <router-view v-if="$route.meta.noCache" :key="key" />
        </div>
      </transition>
    
    1. 比较粗暴的方法:主动销毁组件,详情页离开(销毁)之前,递归把组件的componentInstanceelm.innerHTML移除,即把组件的联系断开,以便浏览器回收:
    /**
     * @name 深度销毁组件
     * @description 用于没有使用 keep-alive 缓存的页面(详情页)
     */
    
    export default {
      beforeDestroy() {
        async function destroyDeep(vnode) {
          let vnodes
          if (vnode.children || vnode.componentInstance?._vnode?.children) {
            vnodes = vnode.children || vnode.componentInstance._vnode.children
            for (const vn of vnodes) {
              destroyDeep(vn)
            }
          }
    
          vnode.componentInstance?.$destroy()
          setTimeout(() => {
            vnode.componentInstance = undefined
            vnode.elm.innerHTML = ''
            vnode.elm.__vue__ = undefined
          }, 0)
        }
    
        destroyDeep(this._vnode)
      }
    }
    
    1. vuevue-template-compiler都升级到2.6.13版本;

    但有个情况依然有问题,就是两个不同列表(缓存)的详情页(不缓存)之间切换,详情页引用同一个详情组件的时候内存一样不释放。

    issues commit
    issues 源码改动

    keep-alive组件部分源码:

    vue@2.6.10

      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance;
        ...
      } else {
        cache[key] = vnode;
        ...
      }
    

    vue@2.6.13新增的cacheVNode方法

      if (vnodeToCache) {
        var tag = vnodeToCache.tag;
        var componentInstance = vnodeToCache.componentInstance;
        var componentOptions = vnodeToCache.componentOptions;
        cache[keyToCache] = {
          name: getComponentName(componentOptions),
          tag: tag,
          componentInstance: componentInstance,
        };
        ...
        this.vnodeToCache = null;
      }
    

    为了减少篇幅,只把部分代码贴上;vue@2.6.13mountedupdated都执行了this.cacheVNode()

    这里this.vnodeToCache = null,把vnode的引用断开了!而旧版本的cache[key] = vnode是没有做这个操作。

    其实可以看出,这里的缓存只用到三个参数:组件的nametag、实例componentInstance,如果缓存整个vnode,没有断开引用,会导致缓存无法释放。

    相关文章

      网友评论

        本文标题:前端vue项目内存泄漏排查总结

        本文链接:https://www.haomeiwen.com/subject/yzsbmrtx.html