19

シングルページのウェブアプリを構築しています。これは、一定期間にわたって新しい DOM 要素を取得し、不要なものを削除することを意味します。たとえば、新しいフォームをフェッチするときは、特定の div のコンテンツをそのフォームの HTML に置き換え、このフォームの要素に固有のリスナーも設定します。しばらくしてから、このフォームの内容をフォームの新しいインスタンス (ID が異なる) に置き換えます。

この新しいフォームのイベント リスナーを再度セットアップします。以前のフォームはもはや DOM の一部ではないので、DOM 要素は自動的にガベージ コレクションされるはずです。また、DOM から削除された要素を指すリスナー関数が消えることも期待しています。

ただし、Chrome から収集した次のプロファイルは、リスナー数が時間の経過とともに増加していることを示しています。なぜそうなのか教えていただけますか?「ごみを集める」ボタンをクリックしてみました。しかし、これは私が得るプロファイルです。アプリケーションの作成方法に何か問題がありますか? 問題はありますか? もしそうなら、どのように修正すればよいですか?

数分間の私の webapp の Chrome スナップショット

問題がある場合は、jquery、jquery-ui、およびその他のプラグインで JSP テンプレート言語を使用しています。これは、ページで追加/削除する動的フラグメントがどのように見えるかです。

<script>
  $(document).ready(function() {
    $("#unique_id").find(".myFormButton").button().click(
      function() {
        $.ajax({url: "myurl.html",
          success: function(response) {
                console.log(response);
          }
        });
    });
  });
</script>

<div id="unique_id">
    <form>
      <input name="myvar" />
      <button class="myFormButton">Submit</button>
    </form>
</div>

アップデート

実際のコードを見たい場合は、ここに関連する部分があります。 このリンクは、クリア ボタンが押されると関数 clearFindForm が呼び出され、Ajax リクエストを使用してコンテンツ (HTML フラグメント) を効果的に再フェッチし、この jsp の div 全体をフェッチしたコンテンツに置き換えることを示しています。refetchContent 関数は次のように機能します。 より良い回答を得るのに役立つ場合に備えて、コードへのリンクを次に示します

function refetchContent(url, replaceTarget) {
  $.ajax({
    url: url,
    data: {},
    type: "GET",
    success: function (response) {
       replaceTarget.replaceWith(response);
    },
    error:   function (response) {
       showErrorMessage("Something went wrong. Please try again.");
    }
  });
}
4

3 に答える 3

5

jQuery は、そのメソッド (.html() を含む - API を読むだけ: http://api.jquery.com/html/ ) を介して削除される DOM 要素へのイベント リスナーを削除するのに非常に優れていますが、イベントは削除されません。分離された DOM ツリーでまだそれらへの参照を持っている可能性がある DOM 要素へのリスナー。

たとえば、次のようなことをするとします。

$.ajax({
    ....
})
    .done(function(response,status,jqXHR) {

        //create a detached DOM tree
        form = $(response)

        //add an event listener to the detached tree
        form.find('#someIDInTheResponse').on('submit',function() {

        });

        //add the form to the html
        $('#someID').html(form);
    });

//at some other point in the code
$('#someIDInTheResponse').remove();

上記の例では、要素を DOM から削除したにもかかわらず、リスナーはメモリから削除されないことに注意してください。これは、グローバル変数「form」を介してアクセス可能なデタッチド DOM ツリーのメモリ内に要素がまだ存在するためです (これは、「var」を使用して、done 関数のスコープ内に最初のデタッチド DOM ツリーを作成しなかったためです。 ...いくつかのニュアンスがあり、jQuery は悪いコードを修正できず、最善を尽くすことしかできません。

他に 2 点:

コールバックまたはイベント リスナー内ですべてを実行すると (ボタン クリックでこれを実行するなど)、本当に悪いスパゲッティ コードになり、すぐに管理不能になります。アプリケーション ロジックを UI インタラクションから分離してみてください。たとえば、コールバックを使用してイベントをクリックして一連のロジックを実行するのではなく、コールバックを使用してイベントをクリックし、一連のロジックを実行する関数を呼び出します。

2 つ目は、それほど重要ではありません (コメントによるこの観点に関するフィードバックを歓迎します) 30MB のメモリは、Web アプリのかなり高いベースラインであると考えています。1時間ほど集中的に使用した後、30MBに達するかなり集中的なGoogleマップWebアプリを持っています。実際に使用すると、それが遅いことに気づき始めることができます. 主は、60MB に達した場合にどのように動作するかを知っています。この時点で IE<9 は事実上使用できなくなると思いますが、前述したように、このアイデアに関する他の人のフィードバックを歓迎します。

于 2013-01-21T01:41:05.780 に答える
1

フラグメントを置き換えるときに、以前にバインドされたイベントリスナーのバインドを解除/削除していないのではないかと思いますか?

更新された質問でリンクしたコードの特定のセクションを簡単に確認しましたが、ドキュメントレディで実行している以外のイベントリスナーバインディングは表示されなかったため、置き換えるときに追加のバインディングを実行していると思いますドキュメントの断片。私はjQueryの専門家ではありませんが、一般に、追加のイベントリスナーをバインドまたは割り当てることで、以前にバインド/割り当てられたイベントリスナーが自動的に置き換えられることはありません。

私のポイントは、最初に既存のイベントリスナーのバインドを解除せずに、「click()」(または他のアプローチ)を介して既存の要素にバインドを行っているかどうかを確認する必要があるということです。

特にクリックの例を提供する、この質問に対するmoffの回答をご覧ください。

于 2013-01-30T14:12:55.243 に答える