Где я должен поместить <script>теги в разметке HTML? - PullRequest
1325 голосов
/ 12 января 2009

При встраивании JavaScript в документ HTML, где лучше разместить теги <script> и включенный JavaScript? Кажется, я вспоминаю, что вы не должны помещать их в раздел <head>, но размещение в начале раздела <body> тоже плохо, так как JavaScript должен быть проанализирован до полной визуализации страницы ( или что-то типа того). Похоже, что end раздела <body> остается логическим местом для тегов <script>.

Итак, где это правильное место для размещения тегов <script>?

(Этот вопрос ссылается на этот вопрос , в котором было предложено переместить вызовы функций JavaScript из тегов <a> в теги <script>. Я специально использую jQuery, но более общие ответы тоже уместны.)

Ответы [ 20 ]

1672 голосов
/ 06 июня 2014

Вот что происходит, когда браузер загружает веб-сайт с тегом <script>:

  1. Получить страницу HTML (например, index.html)
  2. Начать разбор HTML
  3. Синтаксический анализатор обнаружил тег <script>, ссылающийся на внешний файл сценария.
  4. Браузер запрашивает файл скрипта. Тем временем парсер блокирует и прекращает анализ другого HTML-кода на вашей странице.
  5. Через некоторое время скрипт загружается и впоследствии выполняется.
  6. Анализатор продолжает синтаксический анализ остальной части HTML-документа.

Шаг # 4 вызывает плохой пользовательский опыт. Ваш веб-сайт в основном перестает загружаться, пока вы не загрузите все скрипты. Если есть одна вещь, которую пользователи ненавидят, это ждет загрузки веб-сайта.

Почему это вообще происходит?

Любой скрипт может вставить свой собственный HTML с помощью document.write() или других манипуляций с DOM. Это подразумевает, что синтаксический анализатор должен ждать, пока скрипт не будет загружен и выполнен, прежде чем он сможет безопасно проанализировать остальную часть документа. В конце концов, скрипт мог бы вставить свой собственный HTML в документ.

Однако большинство разработчиков JavaScript больше не манипулируют DOM , пока документ загружается. Вместо этого они ждут, пока документ не будет загружен, прежде чем изменять его. Например:

<!-- index.html -->
<html>
    <head>
        <title>My Page</title>
        <script type="text/javascript" src="my-script.js"></script>
    </head>
    <body>
        <div id="user-greeting">Welcome back, user</div>
    </body>
</html>

Javascript:

// my-script.js
document.addEventListener("DOMContentLoaded", function() { 
    // this function runs when the DOM is ready, i.e. when the document has been parsed
    document.getElementById("user-greeting").textContent = "Welcome back, Bart";
});

Поскольку ваш браузер не знает, что my-script.js не будет изменять документ, пока он не будет загружен и выполнен, синтаксический анализатор прекращает синтаксический анализ.

Устаревшая рекомендация

Старый подход к решению этой проблемы заключался в размещении тегов <script> внизу вашего <body>, поскольку это гарантирует, что анализатор не будет заблокирован до самого конца.

У этого подхода есть своя проблема: браузер не может начать загрузку сценариев, пока весь документ не будет проанализирован. Для больших веб-сайтов с большими сценариями и таблицами стилей возможность загрузки сценария как можно скорее очень важна для производительности. Если ваш сайт не загружается в течение 2 секунд, люди переходят на другой сайт.

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

Современный подход

Сегодня браузеры поддерживают атрибуты async и defer в скриптах. Эти атрибуты сообщают браузеру, что безопасно продолжать анализ во время загрузки сценариев.

асинхронный

<script type="text/javascript" src="path/to/script1.js" async></script>
<script type="text/javascript" src="path/to/script2.js" async></script>

Сценарии с асинхронным атрибутом выполняются асинхронно. Это означает, что скрипт выполняется сразу после загрузки, без блокировки браузера.
Это означает, что сценарий 2 можно загрузить и выполнить до сценария 1.

Согласно http://caniuse.com/#feat=script-async, 94,57% всех браузеров поддерживают это.

Перенести

<script type="text/javascript" src="path/to/script1.js" defer></script>
<script type="text/javascript" src="path/to/script2.js" defer></script>

Сценарии с атрибутом defer выполняются по порядку (то есть сначала сценарий 1, затем сценарий 2). Это также не блокирует браузер.

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

Согласно http://caniuse.com/#feat=script-defer, 94,59% всех браузеров поддерживают это. 94,92% поддерживают его хотя бы частично.

Важное замечание о совместимости браузера: в некоторых случаях IE <= 9 может выполнять отложенные сценарии не по порядку. Если вам требуется поддержка этих браузеров, сначала прочтите <a href="https://github.com/h5bp/lazyweb-requests/issues/42" rel="noreferrer"> this !

Заключение

Современным уровнем техники является помещение сценариев в тег <head> и использование атрибутов async или defer. Это позволяет загружать ваши скрипты как можно быстрее, не блокируя ваш браузер.

Хорошо, что ваш веб-сайт должен по-прежнему корректно загружаться в 6% браузеров, которые не поддерживают эти атрибуты, а другие 94% ускоряются.

227 голосов
/ 12 января 2009

Непосредственно перед закрывающим тегом тела, как указано в

http://developer.yahoo.com/performance/rules.html#js_bottom

Поместите сценарии внизу

Проблема, вызванная скриптами, заключается в том, что они блокируют параллельные загрузки. Спецификация HTTP / 1.1 предполагает, что браузеры загружают не более двух компонентов параллельно для каждого имени хоста. Если вы обслуживаете свои изображения с нескольких имен хостов, вы можете получить более двух загрузок параллельно. Однако во время загрузки скрипта браузер не будет запускать другие загрузки, даже на разных именах хостов.

66 голосов
/ 06 февраля 2015

Неблокирующие теги скрипта могут быть размещены где угодно:

<script src="script.js" async></script>
<script src="script.js" defer></script>
<script src="script.js" async defer></script>
  • async сценарий будет выполняться асинхронно, как только он станет доступен
  • defer сценарий выполняется после завершения анализа документа
  • async defer скрипт возвращается к отложенному поведению, если асинхронность не поддерживается

Такие сценарии будут выполняться асинхронно / после того, как документ будет готов, что означает, что вы не можете сделать это:

<script src="jquery.js" async></script>
<script>jQuery(something);</script>
<!--
  * might throw "jQuery is not defined" error
  * defer will not work either
-->

или это:

<script src="document.write(something).js" async></script>
<!--
  * might issue "cannot write into document from an asynchronous script" warning
  * defer will not work either
-->

или это:

<script src="jquery.js" async></script>
<script src="jQuery(something).js" async></script>
<!--
  * might throw "jQuery is not defined" error (no guarantee which script runs first)
  * defer will work in sane browsers
-->

или это:

<script src="document.getElementById(header).js" async></script>
<div id="header"></div>
<!--
  * might not locate #header (script could fire before parser looks at the next line)
  * defer will work in sane browsers
-->

Сказав это, асинхронные сценарии предлагают следующие преимущества:

  • Параллельная загрузка ресурсов :
    Браузер может загружать таблицы стилей, изображения и другие сценарии параллельно, не дожидаясь загрузки и выполнения сценария.
  • Независимость от исходного порядка :
    Вы можете размещать скрипты внутри головы или тела, не беспокоясь о блокировке (полезно, если вы используете CMS). Порядок исполнения все еще имеет значение.

Можно обойти проблемы порядка выполнения с помощью внешних сценариев, поддерживающих обратные вызовы. Многие сторонние API JavaScript поддерживают неблокирующее выполнение. Вот пример асинхронной загрузки Google Maps API .

36 голосов
/ 13 января 2009

Стандартный совет, продвигаемый Yahoo! Команда исключительной производительности должна поместить теги <script> в конец тела документа, чтобы они не блокировали отображение страницы.

Но есть несколько новых подходов, которые предлагают более высокую производительность, как описано в этом ответе о времени загрузки файла Google Analytics JavaScript:

Есть несколько замечательных слайдов от Стива Соудерса (эксперт по работе на стороне клиента) о:

  • Различные способы параллельной загрузки внешних файлов JavaScript
  • их влияние на время загрузки и рендеринг страницы
  • какие индикаторы «в процессе» отображается в браузере (например, «загрузка» в строке состояния, курсор мыши в виде песочных часов).
23 голосов
/ 12 января 2009

Если вы используете JQuery, поместите javascript в любое удобное для вас место и используйте $(document).ready(), чтобы убедиться, что все загружено правильно перед выполнением каких-либо функций.

На заметку: мне нравятся все мои теги сценариев в разделе <head>, так как это самое чистое место.

10 голосов
/ 12 января 2009

XHTML не будет проверяться, если скрипт находится где-то еще, кроме элемента head. оказывается, что он может быть везде.

Вы можете отложить выполнение с помощью чего-то вроде jQuery, поэтому не имеет значения, где оно находится (за исключением небольшого снижения производительности во время синтаксического анализа).

9 голосов
/ 16 июля 2016
<script src="myjs.js"></script>
</body>
Тег

должен всегда использоваться перед закрытием тела или Нижняя часть файла HTML .

затем вы можете увидеть содержимое страницы перед загрузкой js файла.

отметьте это, если требуется: http://stevesouders.com/hpws/rule-js-bottom.php

6 голосов
/ 12 января 2009

Обычный (и общепринятый) ответ - «внизу», потому что тогда весь DOM будет загружен, прежде чем что-либо сможет начать выполняться.

Существуют несогласные по разным причинам, начиная с имеющейся практики намеренного начала исполнения с события загрузки страницы.

3 голосов
/ 01 февраля 2019

Современный подход в 2019 году - использование скриптов типа модуля ES6 .

<script type="module" src="..."></script>

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

Различия между скриптом и модулем описаны здесь:

https://stackoverflow.com/a/53821485/731548

Выполнение модуля по сравнению со скриптом описано здесь:

https://developers.google.com/web/fundamentals/primers/modules#defer

Поддержка показана здесь:

https://caniuse.com/#feat=es6-module

2 голосов
/ 13 января 2009

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

Есть несколько разных техник, но наиболее прямым является использование document.createElement ("script"), когда инициируется событие window.onload. Затем сценарий загружается первым при отображении самой страницы, что не влияет на время, в течение которого пользователь должен ждать появления страницы.

Это, естественно, требует, чтобы сам скрипт не использовался для рендеринга страницы.

Для получения дополнительной информации см. Пост Сочетание асинхронных сценариев от Стива Соудерса (создателя YSlow, но теперь в Google).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...