Сбор мусора ненужных прослушивателей событий в javascript
Я создаю одностраничный webapp. Это означает, что с течением времени я получаю новые элементы DOM, удаляю ненужные. Например, когда я беру новую форму, я просто заменяю содержимое конкретного div этой формой HTML, а также настраиваю слушателей, уникальных для этих элементов формы. Через некоторое время я заменяю содержимое этой формы новым экземпляром формы (с разными идентификаторами).
Я снова установил слушателей событий для этой новой формы. Теперь предыдущая форма больше не является частью DOM, поэтому элементы DOM должны автоматически собираться с мусором. Я также ожидаю, что функции слушателя, указывающие на элементы, удаленные из DOM, исчезнут.
Однако следующий профиль, собранный из Chrome, показывает, что количество моих слушателей растет с течением времени. Можете ли вы сказать мне, почему это так? Я попытался нажать кнопку "Собрать мусор". Но это профиль, который я получаю. Что-то не так с тем, как я строю свое приложение? Есть ли проблема, и если да, то как мне ее исправить?
![Chrome snapshot of my webapp for a few minutes]()
В случае, если это важно, я использую язык программирования JSP с jquery, jquery-ui и некоторыми другими плагинами.
Вот как выглядят динамические фрагменты, которые я добавляю/удаляю на своей странице.
<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 вызывается, которая эффективно обновляет содержимое (фрагмент HTML) с использованием запроса ajax и заменяет весь div в этом jsp с выбранным содержимым.
Функция 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.");
}
});
}
Ответы
Ответ 1
В то время как jQuery очень хорошо удаляет прослушиватели событий для элементов DOM, которые удаляются с помощью его методов (в том числе .html() - просто читайте API: http://api.jquery.com/html/) - он не удаляет прослушиватели событий для элементов 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, слушатель не будет удален из памяти. Это связано с тем, что элемент все еще существует в памяти в отдельном дереве DOM, доступном через глобальную переменную "form" (это связано с тем, что я не создавал использование "var" для создания начального отдельного дерева DOM в рамках выполняемой функции.... есть некоторые нюансы, и jQuery не может исправить плохой код, он может только сделать это лучше всего.
2 другие вещи:
Выполнение всего внутри callbacks или прослушивателей событий (например, это делается нажатием кнопки) превращается в настоящий плохой код спагетти очень быстро и становится неуправляемым довольно быстро. Попробуйте и отделите логику приложения от взаимодействия с пользовательским интерфейсом. Например, не используйте обратные вызовы, чтобы щелкнуть события, чтобы выполнить кучу логики, использовать обратные вызовы для кликов событий для вызова функций, выполняющих кучу логики.
Во-вторых, и несколько менее важно (я приветствую отзывы об этой перспективе с помощью комментариев). Я считаю, что 30 МБ памяти будет довольно высокой базой для веб-приложения. У меня есть довольно интенсивное веб-приложение для Google Maps, которое достигает 30 МБ после часа или около того интенсивного использования, и вы действительно можете заметить, что начинаете замечать его вялость, когда вы это делаете. Господь знает, как это будет выглядеть, если он когда-нибудь достигнет 60 МБ. Я думаю, что IE < 9 станет практически непригодным на данный момент, хотя, как я уже сказал, я приветствую обратную связь с другими людьми по этой идее.
Ответ 2
Интересно, просто ли вы не отключаете/удаляете ранее связанные прослушиватели событий при замене фрагментов?
Я кратко рассмотрел конкретные разделы кода, с которым вы связались в своем обновленном вопросе, но не видел привязки прослушивателя событий, отличного от того, что вы делаете в документе, поэтому я предполагаю, что вы выполняете дополнительную привязку при замене фрагментов документа. Я не эксперт jQuery, но, как правило, привязка или назначение дополнительных прослушивателей событий не заменяет ранее назначенных/назначенных прослушивателей событий.
Я хочу сказать, что вы должны посмотреть, выполняете ли вы привязку через "click()" (или с помощью какого-либо другого подхода) к существующим элементам, не отключая сначала существующий прослушиватель событий.
Вы можете взглянуть на ответ moff на этот вопрос, в котором приведен пример щелчка, в частности.
Ответ 3
Я не могу добавить комментарий из-за репутации, но ответить на то, что говорит Адам...
Чтобы обобщить случай, который представляет Адам, это потенциально не имеет никакого отношения к jQuery, проблема может быть в пределах обычного Javascript. Однако вы не представляете достаточно кода для тех, кто действительно подходит к сути проблемы. Ваше использование облачной инкапсуляции может быть совершенно прекрасным, и проблема может быть и в другом месте.
Я бы посоветовал вам искать инструменты для поиска причины утечки памяти (например, визуализация/перемещение всего дерева объектов/области/ссылки/функции и т.д.).
Одной из вещей, которые нужно учитывать при использовании jQuery, являются плагины и глобальные вставки в DOM! Я видел много JS-библиотек, а не только плагины jQuery, которые не предоставляют эсминцы и методы очистки. Худшими нарушителями часто являются вещи с всплывающими окнами и всплывающими окнами, такими как сборщики дат, диалоги и т.д., Которые имеют неприятную привычку добавлять дочерние слои и т.д. В тело, не удаляя их после.
Что-то нужно иметь в виду, если многие люди просто доберутся до того, чтобы сделать вещи конструированными, но не справляются с деструкцией, особенно в JS, потому что они ожидают, что даже в этот день и в возрасте вы будете обслуживать обычные веб-страницы. Вы также должны проверить плагины для методов уничтожения, потому что не все будут подключаться к событию удаления. События также используются беспорядочно в jQuery другими, поэтому обработчик изгоев может останавливать выполнение следующих событий очистки.
В заключение, jQuery - хорошая надежная библиотека, но ее предупреждают, только потому, что кто-то зависит от нее, не означает, что она наследует качество jQuery.
Из любопытства... вы проверили слушателей на document.ready? Возможно, вам нужно вручную скомпоновать их.