ホームページ  >  記事  >  ウェブフロントエンド  >  vue 使用時のメモリリーク【推奨】_vue.js

vue 使用時のメモリリーク【推奨】_vue.js

无忌哥哥
无忌哥哥オリジナル
2018-07-12 14:04:311263ブラウズ

メモリ リークとは、新しいメモリを解放できない、またはガベージ コレクションができないことを意味します。この記事では主にvueの使用におけるメモリリークについて紹介しますので、困っている方は参考にしてください

メモリリークとは何ですか?メモリ リークとは、解放またはガベージ コレクションができない新しいメモリ部分を指します。オブジェクトを新規作成した後、オブジェクト ポインターが null に設定されるか、スコープから出て破棄されると、このメモリーは、誰も参照しない場合、JS 内で自動的にガベージ コレクションされます。ただし、オブジェクト ポインターが null に設定されておらず、コード内でオブジェクト ポインターを取得する方法がない場合、オブジェクト ポインターが指すメモリは解放されず、メモリ リークが発生します。コード内でこのオブジェクト ポインターを取得できないのはなぜですか? 以下に例を示します:

// module date.js
let date = null;
export default {
 init () {
  date = new Date();
 }
}
// main.js
import date from 'date.js';
date.init();

main.js が date を初期化した後、date 変数はページを閉じるまで常に存在します。これは、日付参照が別の場所にあるためです。 module である場合、モジュールは外界からは見えないクロージャであることが理解できます。したがって、この日付オブジェクトを常に存在させ、常に使用する必要がある場合は問題ありませんが、一度使用しただけでその後は使用しない場合、このオブジェクトが存在することになります。メモリが解放されずにメモリリークが発生しました。

もう 1 つのより微妙で一般的なメモリ リークは、クロージャを形成し、一部の変数が常に存在するようにするイベント バインディングです。次の例に示すように:

// 一个图片懒惰加载引擎示例
class ImageLazyLoader {
 constructor ($photoList) {
  $(window).on('scroll', () => {
   this.showImage($photoList);
  });
 }
 showImage ($photoList) {
  $photoList.each(img => {
   // 通过位置判断图片滑出来了就加载
   img.src = $(img).attr('src');
  });
 }
}
// 点击分页的时候就初始化一个图片懒惰加载的
$('.page').on('click', function () {
 new ImageLazyLoader($('img.photo'));
});

これは、ページをクリックするたびに、前のページのデータがクリアされ、現在のページの DOM に更新されます。 、遅延読み込みエンジンが再初期化されます。スクロール イベントをリッスンし、受信した画像リストの DOM を処理します。新しいページをクリックするたびに、新しいページが作成されます。主に次の 3 行のコードが原因でメモリ リークが発生します。 photoList 2 つの変数は解放されていません。これは ImageLazyLoader のインスタンスを指し、$photoList は DOM ノードを指します。前ページのデータがクリアされると、該当する DOM ノードは DOM ツリーから切り離されます。それらを指す $photoList がまだ存在するため、これらの DOM ノードはガベージ コレクションできずメモリ内に残り、メモリ リークが発生します。この変数もクロージャによってトラップされ解放されていないため、ImageLazyLoader のインスタンスでもメモリ リークが発生します。

これに対する解決策は比較的簡単で、次のコードに示すように、インスタンスを破棄するときにバインドされたイベントをオフにすることです:

$(window).on('scroll', () => {
 this.showImage($photoList);
});

毎回 ImageLazyLoader をインスタンス化する前に前のインスタンスをクリアし、バインドを解除しますJS にはコンストラクターはありますが、デストラクターがないため、自分でクリアを記述し、外部のクリアを手動で調整する必要があります。同時に、イベント実行中の適切なタイミングでイベントが自動的にアンバインドされます。これは、すべてのピクチャが表示されていれば、スクロール イベントを監視して直接アンバインドする必要がないと判断するためです。これにより、メモリ リークの問題が解決され、自動ガベージ コレクションがトリガーされます。

イベントのバインドを解除した後にクロージャ参照がないのはなぜですか? JS エンジンはクロージャがもう役に立たないことを検出するため、クロージャを破棄し、クロージャによって参照される外部変数は当然空白のままになります。

さて、ここで基本的な知識を説明しました。次に、Chrome devtools のメモリ検出ツールを使用して、ページ上のメモリ リークを発見しやすくします。ブラウザにインストールされている一部のプラグインの影響を回避するには、Chome のシークレット モード ページを使用して、すべてのプラグインを無効にします。

次に、以下に示すように、devtools を開き、[メモリ] タブに切り替えて、[ヒープ スナップショット] を選択します:

ヒープ スナップショットとは何ですか?翻訳すると、これは現在のメモリ ヒープの写真を撮るヒープ スナップショットです。動的に適用されるメモリはヒープ内にあり、ローカル変数はメモリ スタック内にあり、オペレーティング システムによって割り当ておよび管理されるため、メモリ リークは発生しません。したがって、ヒープの状況だけを気にしてください。

その後、次のような DOM の追加、削除、変更の操作を実行します:
(1) ボックスをポップアップし、ポップアップ ボックスを閉じます

(2) 1 つのページをクリックして別のページにジャンプしますルートに移動し、戻るをクリックして戻ります

(3) ページングをクリックして動的な DOM 変更をトリガーします

つまり、最初に DOM を追加し、次にこれらの DOM を削除し、削除された DOM にそれらを参照するオブジェクトがまだあるかどうかを確認します。

ここでは 2 番目の方法を使用して、シングルページ アプリケーションのルーティング ページにメモリ リークがあるかどうかを検出しています。まずホームページを開いて別のページをクリックし、次に戻るをクリックしてガベージ コレクション ボタンをクリックします:

不要な干渉を避けるためにガベージ コレクションをトリガーします。

次に、写真ボタンをクリックします:

 

它就会把当前页面的内存堆扫描一遍显示出来,如下图所示:

 

然后在上面中间的Class Filter的搜索框里搜一下detached:

 

它就会显示所有已经分离了DOM树的DOM结点,重点关注distance值不为空的,这个distance表示距离DOM根结点的距离。上图展示的这些p具体是啥呢?我们把鼠标放上去不动等个2s,它就会显示这个p的DOM信息:

 

通过className等信息可以知道它就是那个要检查的页面的DOM节点,在下面的Object的窗口里面依次展开它的父结点,可以看到它最外面的父结点是一个VueComponent实例:

 

下面黄色字体native_bind表示有个事件指向了它,黄色表示引用仍然生效,把鼠标放到native_bind上面停留2秒:

它会提示你是在homework-web.vue这个文件有一个getScale函数绑定在了window上面,查看一下这个文件确实是有一个绑定:

mounted () {
 window.addEventListener('resize', this.getScale);
}

所以虽然Vue组件把DOM删除了,但是还有个引用存在,导致组件实例没有被释放,组件里面又有一个$el指向DOM,所以DOM也没有被释放。

要在beforeDestroyed里面解绑的

beforeDestroyed () {
 window.removeEventListener('resize', this.getScale);
}

所以综合上面的分析,造成内存泄露的可能会有以下几种情况:

(1)监听在window/body等事件没有解绑

(2)绑在EventBus的事件没有解绑

(3)Vuex的$store watch了之后没有unwatch

(4)模块形成的闭包内部变量使用完后没有置成null

(5)使用第三方库创建,没有调用正确的销毁函数

并且可以借助Chrome的内存分析工具进行快速排查,本文主要是用到了内存堆快照的基本功能,读者可以尝试分析自己的页面是否存在内存泄漏,方法是做一些操作如弹个框然后关了,拍一张堆快照,搜索detached,按distance排序,把非空的节点展开父级,找到标黄的字样说明,那些就是存在没有释放的引用。也就是说这个方法主要是分析仍然存在引用的游离DOM节点。因为页面的内存泄露通常是和DOM相关的,普通的JS变量由于有垃圾回收所以一般不会有问题,除非使用闭包把变量困住了用完了又没有置空。

DOM相关的内存泄露通常也是因为闭包和事件绑定引起的。绑了(全局)事件之后,在不需要的时候需要把它解绑。当然直接绑在p上面的可以直接把p删了,绑在它上面的事件就自然解绑了。

以上がvue 使用時のメモリリーク【推奨】_vue.jsの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。