Нарушение Долгосрочная задача 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

Ответ 1

Обновление: 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 ${duration}ms');
}

Если вы хотите стать более продвинутым, вы также можете использовать Chrome Profiler или использовать такую библиотеку для сравнения, как эта.

Как только вы нашли какой-то код, который занимает много времени (50 мс - порог Chrome), у вас есть несколько вариантов:

  1. Вырежьте часть/всю эту задачу, которая может быть ненужной
  2. Выясните, как выполнить ту же задачу быстрее
  3. Разделите код на несколько асинхронных шагов

(1) и (2) может быть трудным или невозможным, но иногда это действительно легко и должно быть вашими первыми попытками. При необходимости всегда должно быть возможно сделать (3). Для этого вы будете использовать что-то вроде:

setTimeout(functionToRunVerySoonButNotNow);

или же

// This one is not available natively in IE, but there are polyfills available.
Promise.resolve().then(functionToRunVerySoonButNotNow);

Вы можете прочитать больше об асинхронной природе JavaScript здесь.

Ответ 2

Это всего лишь предупреждения, как все упоминали. Однако, если вы заинтересованы в разрешении этих проблем (что следует сделать), вам необходимо сначала определить причину, по которой выдается предупреждение. Там нет ни одной причины, из-за которой вы можете получить предупреждение о принудительном оплавлении. Кто-то создал список для некоторых возможных вариантов. Вы можете следить за обсуждением для получения дополнительной информации.
Вот суть возможных причин:

Что заставляет макет/оплавление

Все нижеприведенные свойства или методы, когда их запрашивают/вызывают в JavaScript, заставят браузер синхронно вычислять стиль и макет *. Это также называется перекомпоновкой или компоновкой макета, и является обычным узким местом производительности.

Элемент

Box метрики
  • 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() может вызвать двойной принудительный макет (источник)
Также…

getComputedStyle

window.getComputedStyle() будет обычно вызывать стиль recalc (source)

window.getComputedStyle() также форсирует макет, если выполняется любое из следующих условий:

  1. Элемент находится в теневом дереве
  2. Есть медиа-запросы (связанные с окном просмотра). В частности, одно из следующего: (источник) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio, max-aspect-ratio
    • device-pixel-ratio, resolution, orientation
  3. Запрашиваемое свойство является одним из следующих: (источник)
    • 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() только заставляет стиль

формы

События мыши

  • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY (источник)

документ

  • doc.scrollingElement только заставляет стиль

Спектр

  • range.getClientRects(), range.getBoundingClientRect()

SVG

contenteditable

  • Много-много всего, включая копирование изображения в буфер обмена (источник)

Проверьте больше здесь.

Также здесь приведен исходный код Chromium из оригинального выпуска и обсуждение API производительности для предупреждений.


Изменить: Там также статья о том, как свести к минимуму перекомпоновку макета на PageSpeed Insight от Google. Это объясняет, что такое перекомпоновка браузера:

Reflow - это имя процесса веб-браузера для повторного расчета положений и геометрии элементов в документе с целью повторного рендеринга части или всего документа. Поскольку reflow является операцией блокировки пользователя в браузере, разработчикам полезно понять, как улучшить время перекомпоновки, а также понять влияние различных свойств документа (глубина DOM, эффективность правил CSS, различные типы изменений стиля) на перекомпоновку. время. Иногда для перекомпоновки одного элемента в документе может потребоваться перекомпоновка его родительских элементов, а также любых элементов, следующих за ним.

Кроме того, это объясняет, как минимизировать это:

  1. Уменьшите ненужную глубину DOM. Изменения на одном уровне в дереве DOM могут вызвать изменения на каждом уровне дерева - вплоть до корня и вплоть до потомков модифицированного узла. Это приводит к увеличению времени, затрачиваемого на выполнение оплавления.
  2. Минимизируйте правила CSS и удалите неиспользуемые правила CSS.
  3. Если вы делаете сложные изменения рендеринга, такие как анимация, делайте это из потока. Используйте абсолютное положение или фиксированное положение для достижения этой цели.
  4. Избегайте ненужных сложных CSS-селекторов - в частности, потомков-селекторов - которые требуют больше ресурсов процессора для сопоставления селекторов.

Ответ 3

Несколько идей:

  • Удалите половину своего кода (возможно, через комментирование).

    • Есть ли проблема? Отлично, вы сузили возможности! Повторить.

    • Не проблема? Хорошо, посмотрите на половину вы прокомментировали!

  • Используете ли вы какую-либо систему контроля версий (например, Git)? Если да, git checkout некоторые из ваших последних коммитов. Когда появилась проблема? Посмотрите на фиксацию, чтобы увидеть, какой код изменился, когда проблема впервые появилась.

Ответ 4

В моем случае я обнаружил, что это на самом деле было вызвано кодом из расширения. Для меня расширение было AdBlock.

Чтобы определить источник проблемы, запустите приложение и запишите его на вкладке "Производительность Chrome ".

Там вы можете проверить различные функции, которые потребовалось много времени для запуска. В моем случае, тот, который коррелировал с предупреждениями в консоли, был из файла, который был загружен расширением AdBlock, но это может быть что-то другое в вашем случае.

Проверьте эти файлы и попытайтесь определить, является ли это каким-либо кодом расширения или вашим. (Если это ваше, значит, вы нашли источник вашей проблемы.)

Ответ 5

Посмотрите консоль Chrome на вкладке "Сеть" и найдите сценарии, которые занимают больше всего времени.

В моем случае появился набор Angular add на скриптах, которые я включил, но еще не использовал в приложении:

<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-utils/0.1.1/angular-ui-utils.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-aria.min.js"></script>

Это были только файлы JavaScript, которые занимали больше времени, чем время, указанное в задаче "Long Running Task".

Все эти файлы запускаются на других моих сайтах без каких-либо ошибок, но я получаю эту ошибку "Long Running Task" в новом веб-приложении, которое едва ли имело какие-либо функции. Ошибка была немедленно устранена после удаления.

Моя лучшая догадка заключается в том, что эти дополнения Angular выглядели рекурсивно во все более глубокие разделы DOM для их начальных тегов - не обнаруживая их, они должны были пройти весь DOM перед выходом, что заняло больше времени, чем ожидало Chrome - таким образом предупреждение.

Ответ 7

Если вы используете Chrome Canary (или бета-версию), просто установите флажок "Скрыть нарушения".

Hide Violations Checkbox in Chrome 56 Console

Ответ 8

Я нашел решение в исходном коде 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()


Использование:

nextTick(function() {
  // your code
});

После ввода этого трюкового кода все предупреждающие сообщения исчезли.

Ответ 9

Я нашел корень этого сообщения в своем коде, который искал и скрывал или показывал узлы (в автономном режиме). Это был мой код:

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 мс: Chromium performance profiler layout recalculation reflow

Сейчас:

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 мс: Chromium profiler dark

И я чувствую, что поиск работает быстрее (229 узлов).

Ответ 10

Для Chrome 57:

  1. Перейти к консоли

  2. Нажмите значок фильтра рядом с флажком "Сохранить журнал"

  3. Установите флажок "Скрыть нарушения", чтобы скрыть предупреждение.

Console

Для Chrome 58+:

  1. Перейти к консоли
  2. Установите Verbose в раскрывающемся списке уровня ведения журнала, чтобы увидеть нарушения, или снимите флажок, чтобы скрыть его.

Ответ 11

Запустите профилировщик производительности в Google Chrome, как указано выше. Что может частично помочь вам, это изменить порядок в вашем коде javascript, в частности поставить проблемные части кода в самом его конце.

Ответ 12

Это ошибка нарушения от Google Chrome, которая показывает, когда включен Verbose уровень ведения журнала.

Пример сообщения об ошибке:

screenshot of the warning

Объяснение:

Reflow - это имя процесса веб-браузера для повторного расчета положений и геометрии элементов в документе с целью повторного рендеринга части или всего документа. Поскольку reflow является операцией блокировки пользователя в браузере, разработчикам полезно понять, как улучшить время перекомпоновки, а также понять влияние различных свойств документа (глубина DOM, эффективность правил CSS, различные типы изменений стиля) на перекомпоновку. время. Иногда для перекомпоновки одного элемента в документе может потребоваться перекомпоновка его родительских элементов, а также любых элементов, следующих за ним.

Оригинальная статья: Минимизация перекомпоновки браузера Линдси Саймон, разработчиком UX, опубликована на developers.google.com.

И это ссылка, которую Google Chrome дает вам в профилировщике производительности, в профилях макетов (лиловые регионы), для получения дополнительной информации о предупреждении.

Ответ 13

Принудительное переопределение часто происходит, когда у вас есть функция, вызванная несколько раз до конца выполнения.

Например, у вас может быть проблема на смартфоне, но не на классическом браузере.

Я предлагаю использовать setTimeout для решения проблемы.

Это не очень важно, но я повторяю, проблема возникает, когда вы вызываете функцию несколько раз, а не когда функция занимает более 50 мс. Я думаю, что вы ошибаетесь в своих ответах.

  1. Выключите вызовы "1 на 1" и перезагрузите код, чтобы увидеть, все ли выдает ошибку.
  2. Если второй скрипт вызывает ошибку, используйте setTimeOut основываясь на продолжительности нарушения.

Ответ 14

Это не ошибка, просто сообщение. Чтобы выполнить это изменение сообщения
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> (пример)
в
<!DOCTYPE html> (источник Firefox ожидает этого)

Сообщение было показано в Google Chrome 74 и Opera 60. После смены было понятно, 0 многословно.
Подход к решению

Ответ 15

У вас должен быть повторяющийся селектор классов, который неоднократно ищется в JavaScript. Если что-то должно использовать идентификатор, используйте его как идентификатор, а не как класс. Это случилось со мной.