Нарушение Долгосрочная задача JavaScript заняла xx ms
Недавно я получил такое предупреждение, и это мой первый раз:
[Violation] Long running JavaScript task took 234ms [Violation] Forced reflow while executing JavaScript took 45ms
Я работаю над групповым проектом и понятия не имею, откуда это исходит. Этого никогда не было раньше. Внезапно это появилось, когда кто-то еще вмешался в проект. Как мне найти, какой файл/функция вызывает это предупреждение? Я искал ответ, но в основном о том, как его решить. Я не могу решить это, если я даже не могу найти источник проблемы.
В этом случае предупреждение появляется только в Chrome. Я пытался использовать Edge, но я не получил подобных предупреждений, и я еще не тестировал его в Firefox.
Я даже получаю сообщение об ошибке из jquery.min.js :
[Violation] Handler took 231ms of runtime (50ms allowed) jquery.min.js:2
Обновление: Chrome 58+ скрыл эти и другие сообщения отладки по умолчанию. Чтобы отобразить их, щелкните стрелку рядом с “Информация” и выберите “Подробно”.
Chrome 57 по умолчанию включил “скрыть нарушения”. Чтобы включить их снова, необходимо включить фильтры и снять флажок “скрывать нарушения”.
вдруг появляется, когда кто-то еще участвует в проекте
Я думаю, что скорее всего вы обновили Chrome 56. Это предупреждение – замечательная новая функция, на мой взгляд, отключайте ее, только если вы в отчаянии и ваш оценщик уберет у вас оценки. Основные проблемы есть в других браузерах, но браузеры просто не сообщают вам о проблеме. Билет Chromium здесь, но на самом деле нет никакой интересной дискуссии по нему.
Эти сообщения являются предупреждениями, а не ошибками, потому что они не вызовут серьезных проблем. Это может привести к тому, что кадры могут быть сброшены или иным образом вызвать менее гладкое впечатление
Тем не менее, их стоит изучить и исправить, чтобы улучшить качество вашего приложения. Это можно сделать, обращая внимание на обстоятельства, при которых появляются сообщения, и проводя тестирование производительности, чтобы определить причину возникновения проблемы. Самый простой способ начать тестирование производительности – вставить такой код:
function someMethodIThinkMightBeSlow() < const startTime = performance.now(); // Do the normal stuff for this function const duration = performance.now() - startTime; console.log('someMethodIThinkMightBeSlow took $ms'); >
Если вы хотите стать более продвинутым, вы также можете использовать Chrome Profiler или использовать такую библиотеку для сравнения, как эта.
Как только вы нашли какой-то код, который занимает много времени (50 мс – порог Chrome), у вас есть несколько вариантов:
- Вырежьте часть/всю эту задачу, которая может быть ненужной
- Выясните, как выполнить ту же задачу быстрее
- Разделите код на несколько асинхронных шагов
(1) и (2) может быть трудным или невозможным, но иногда это действительно легко и должно быть вашими первыми попытками. При необходимости всегда должно быть возможно сделать (3). Для этого вы будете использовать что-то вроде:
setTimeout(functionToRunVerySoonButNotNow);
// This one is not available natively in IE, but there are polyfills available. Promise.resolve().then(functionToRunVerySoonButNotNow);
Вы можете прочитать больше об асинхронной природе JavaScript здесь.
Это всего лишь предупреждения, как все упоминали. Однако, если вы заинтересованы в разрешении этих проблем (что следует сделать), вам необходимо сначала определить причину, по которой выдается предупреждение. Там нет ни одной причины, из-за которой вы можете получить предупреждение о принудительном оплавлении. Кто-то создал список для некоторых возможных вариантов. Вы можете следить за обсуждением для получения дополнительной информации.
Вот суть возможных причин:
Что заставляет макет/оплавление
Все нижеприведенные свойства или методы, когда их запрашивают/вызывают в JavaScript, заставят браузер синхронно вычислять стиль и макет *. Это также называется перекомпоновкой или компоновкой макета, и является обычным узким местом производительности.
Элемент
- elem.offsetLeft , elem.offsetTop , elem.offsetWidth , elem.offsetHeight , elem.offsetParent
- elem.clientLeft , elem.clientTop , elem.clientWidth , elem.clientHeight
- elem.getClientRects() , elem.getBoundingClientRect()
- elem.scrollBy() , elem.scrollTo()
- elem.scrollIntoView() , elem.scrollIntoViewIfNeeded()
- elem.scrollWidth , elem.scrollHeight
- elem.scrollLeft , elem.scrollTop также, устанавливая их
- elem.focus() может вызвать двойной принудительный макет (источник)
- elem.computedRole , elem.computedName
- elem.innerText (источник)
getComputedStyle
- Элемент находится в теневом дереве
- Есть медиа-запросы (связанные с окном просмотра). В частности, одно из следующего: (источник) * min-width , min-height , max-width , max-height , width , height * aspect-ratio , min-aspect-ratio , max-aspect-ratio
- device-pixel-ratio , resolution , orientation
- Запрашиваемое свойство является одним из следующих: (источник)
- height , width * top , right , bottom , left * margin [ -top , -right , -bottom , -left или сокращение], только если поле зафиксировано. * padding [ -top , -right , -bottom , -left или сокращение], только если заполнение фиксировано. * transform , transform-origin , perspective-origin * translate , rotate , scale * webkit-filter , backdrop-filter * motion-path , motion-offset , motion-rotation * x , y , rx , ry
окно
- window.scrollX , window.scrollY
- window.innerHeight , window.innerWidth
- window.getMatchedCSSRules() только заставляет стиль
формы
События мыши
документ
Спектр
SVG
contenteditable
Также здесь приведен исходный код Chromium из оригинального выпуска и обсуждение API производительности для предупреждений.
Изменить: Там также статья о том, как свести к минимуму перекомпоновку макета на PageSpeed Insight от Google. Это объясняет, что такое перекомпоновка браузера:
Reflow – это имя процесса веб-браузера для повторного расчета положений и геометрии элементов в документе с целью повторного рендеринга части или всего документа. Поскольку reflow является операцией блокировки пользователя в браузере, разработчикам полезно понять, как улучшить время перекомпоновки, а также понять влияние различных свойств документа (глубина DOM, эффективность правил CSS, различные типы изменений стиля) на перекомпоновку. время. Иногда для перекомпоновки одного элемента в документе может потребоваться перекомпоновка его родительских элементов, а также любых элементов, следующих за ним.
Кроме того, это объясняет, как минимизировать это:
- Уменьшите ненужную глубину DOM. Изменения на одном уровне в дереве DOM могут вызвать изменения на каждом уровне дерева – вплоть до корня и вплоть до потомков модифицированного узла. Это приводит к увеличению времени, затрачиваемого на выполнение оплавления.
- Минимизируйте правила CSS и удалите неиспользуемые правила CSS.
- Если вы делаете сложные изменения рендеринга, такие как анимация, делайте это из потока. Используйте абсолютное положение или фиксированное положение для достижения этой цели.
- Избегайте ненужных сложных CSS-селекторов – в частности, потомков-селекторов – которые требуют больше ресурсов процессора для сопоставления селекторов.
- Удалите половину своего кода (возможно, через комментирование).
- Есть ли проблема? Отлично, вы сузили возможности! Повторить.
- Не проблема? Хорошо, посмотрите на половину вы прокомментировали!
В моем случае я обнаружил, что это на самом деле было вызвано кодом из расширения. Для меня расширение было AdBlock.
Чтобы определить источник проблемы, запустите приложение и запишите его на вкладке “Производительность Chrome “.
Там вы можете проверить различные функции, которые потребовалось много времени для запуска. В моем случае, тот, который коррелировал с предупреждениями в консоли, был из файла, который был загружен расширением AdBlock, но это может быть что-то другое в вашем случае.
Проверьте эти файлы и попытайтесь определить, является ли это каким-либо кодом расширения или вашим. (Если это ваше, значит, вы нашли источник вашей проблемы.)
Посмотрите консоль Chrome на вкладке “Сеть” и найдите сценарии, которые занимают больше всего времени.
В моем случае появился набор Angular add на скриптах, которые я включил, но еще не использовал в приложении:
Это были только файлы JavaScript, которые занимали больше времени, чем время, указанное в задаче “Long Running Task”.
Все эти файлы запускаются на других моих сайтах без каких-либо ошибок, но я получаю эту ошибку “Long Running Task” в новом веб-приложении, которое едва ли имело какие-либо функции. Ошибка была немедленно устранена после удаления.
Моя лучшая догадка заключается в том, что эти дополнения Angular выглядели рекурсивно во все более глубокие разделы DOM для их начальных тегов – не обнаруживая их, они должны были пройти весь DOM перед выходом, что заняло больше времени, чем ожидало Chrome – таким образом предупреждение.
Это было добавлено в бета-версию Chrome 56, хотя она не включена в этот список изменений из блога Chromium: Chrome 56 Beta: предупреждение “Not Secure”, веб-Bluetooth и position: sticky CSS position: sticky
Вы можете скрыть это в панели фильтров консоли с помощью флажка Скрыть нарушения.
Если вы используете Chrome Canary (или бета-версию), просто установите флажок “Скрыть нарушения”.
Я нашел решение в исходном коде Apache Cordova.
Они реализуются следующим образом:var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve(); var nextTick = resolvedPromise ? function(fn) < resolvedPromise.then(fn); >: function(fn) < setTimeout(fn); >;
Простая реализация, но умный способ.
В Android 4.4 используйте Promise .
Для старых браузеров используйте setTimeout()После ввода этого трюкового кода все предупреждающие сообщения исчезли.
Я нашел корень этого сообщения в своем коде, который искал и скрывал или показывал узлы (в автономном режиме). Это был мой код:
search.addEventListener('keyup', function() < for (const node of nodes) if (node.innerText.toLowerCase().includes(this.value.toLowerCase())) node.classList.remove('hidden'); else node.classList.add('hidden'); >);
Вкладка производительности (профилировщик) показывает, что событие занимает около 60 мс:
search.addEventListener('keyup', function() < const nodesToHide = []; const nodesToShow = []; for (const node of nodes) if (node.innerText.toLowerCase().includes(this.value.toLowerCase())) nodesToShow.push(node); else nodesToHide.push(node); nodesToHide.forEach(node =>node.classList.add('hidden')); nodesToShow.forEach(node => node.classList.remove('hidden')); >);
Вкладка производительности (профилировщик) теперь показывает, что событие занимает около 1 мс:
И я чувствую, что поиск работает быстрее (229 узлов).
Для Chrome 57:
- Перейти к консоли
- Нажмите значок фильтра рядом с флажком “Сохранить журнал”
- Установите флажок “Скрыть нарушения”, чтобы скрыть предупреждение.
Для Chrome 58+:
- Перейти к консоли
- Установите Verbose в раскрывающемся списке уровня ведения журнала, чтобы увидеть нарушения, или снимите флажок, чтобы скрыть его.
Запустите профилировщик производительности в Google Chrome, как указано выше. Что может частично помочь вам, это изменить порядок в вашем коде javascript, в частности поставить проблемные части кода в самом его конце.
Это ошибка нарушения от Google Chrome, которая показывает, когда включен Verbose уровень ведения журнала.
Пример сообщения об ошибке:
Reflow – это имя процесса веб-браузера для повторного расчета положений и геометрии элементов в документе с целью повторного рендеринга части или всего документа. Поскольку reflow является операцией блокировки пользователя в браузере, разработчикам полезно понять, как улучшить время перекомпоновки, а также понять влияние различных свойств документа (глубина DOM, эффективность правил CSS, различные типы изменений стиля) на перекомпоновку. время. Иногда для перекомпоновки одного элемента в документе может потребоваться перекомпоновка его родительских элементов, а также любых элементов, следующих за ним.
Оригинальная статья: Минимизация перекомпоновки браузера Линдси Саймон, разработчиком UX, опубликована на developers.google.com.
И это ссылка, которую Google Chrome дает вам в профилировщике производительности, в профилях макетов (лиловые регионы), для получения дополнительной информации о предупреждении.
Принудительное переопределение часто происходит, когда у вас есть функция, вызванная несколько раз до конца выполнения.
Например, у вас может быть проблема на смартфоне, но не на классическом браузере.
Я предлагаю использовать setTimeout для решения проблемы.
Это не очень важно, но я повторяю, проблема возникает, когда вы вызываете функцию несколько раз, а не когда функция занимает более 50 мс. Я думаю, что вы ошибаетесь в своих ответах.
- Выключите вызовы “1 на 1” и перезагрузите код, чтобы увидеть, все ли выдает ошибку.
- Если второй скрипт вызывает ошибку, используйте setTimeOut основываясь на продолжительности нарушения.
Сообщение было показано в Google Chrome 74 и Opera 60. После смены было понятно, 0 многословно.
Подход к решениюУ вас должен быть повторяющийся селектор классов, который неоднократно ищется в JavaScript. Если что-то должно использовать идентификатор, используйте его как идентификатор, а не как класс. Это случилось со мной.