{ "index": 324, "slug": "использование-jquery-в-webpack", "title": "jQuery в Webpack: как связать модульный код и legacy-плагины", "excerpt": "После миграции на Webpack плагин видит $ только на одной странице или получает другой объект jQuery. Разбираем границы ProvidePlugin, window.jQuery и externals и заканчиваем проверяемым критерием готовности.", "contentHtml": "

После переноса старого фронтенда в Webpack кнопка с inputmask перестаёт работать. В консоли появляется jQuery is not defined, $(...).inputmask is not a function или плагин загружается только на странице с «правильным» порядком скриптов. Ошибка часто выглядит случайной: новый модуль импортирует jQuery, а legacy-файл ищет её в window. Цена ошибки — сломанная форма, дублированная библиотека в нескольких бандлах и релиз, который нельзя уверенно проверить одной сборкой.

\n

Тезис простой: сначала нужно назвать потребителя jQuery, затем выбрать один способ доставки для каждого entry. Явный import связывает зависимость с графом модулей. ProvidePlugin помогает старому коду, который обращается к свободным $ или jQuery. Глобальный объект нужен только тем потребителям, которые действительно читают window.jQuery. CDN и externals образуют другой контракт: Webpack больше не кладёт библиотеку в бандл и ждёт готового глобального объекта снаружи.

\n

Симптом начинается с границы видимости

\n

Модуль с import $ from 'jquery' получает значение из графа Webpack. Это локальная переменная модуля. Она не обещает, что отдельный <script> в HTML увидит window.$. Обратное тоже верно: наличие window.jQuery не добавляет зависимость в граф и не делает её доступной каждому исходному файлу без настройки.

\n

ProvidePlugin решает третий случай. Webpack замечает свободный идентификатор в модуле и автоматически подставляет импорт. Поэтому код с $(selector) может собраться без строки import. Это не запись переменной в глобальный объект браузера и не исправление порядка независимых тегов script.

\n
\"Схема
Один entry должен доставить один объект jQuery раньше плагина, который его использует. Иллюстрация показывает границу между графом модулей и глобальным API браузера.
\n

Три способа доставки

\n

В проекте с несколькими entry не стоит начинать с единого глобального правила. Сначала составьте карту потребителей.

\n
СпособЧто получает кодКогда подходитЧего он не делает
Явный importЛокальный объект из графа WebpackНовый код и код, который можно менятьНе создаёт window.jQuery сам по себе
ProvidePluginАвтоматически подставленный импорт для свободного имениLegacy-модули с $ или jQueryНе чинит внешний script и не гарантирует порядок загрузки
externals или CDNГлобальный объект, предоставленный HTML или платформойОдна контролируемая внешняя загрузкаНе включает jQuery в бандл и не проверяет URL CDN
\n

Для переходного проекта обычно работает связка: новый код импортирует jQuery явно, а ограниченный legacy-слой получает ProvidePlugin. Если плагин проверяет именно window.jQuery, добавьте глобальный экспорт в одном entry. Не смешивайте этот режим с CDN без причины. Иначе один бандл будет использовать пакет, а другой — внешний файл.

\n

Минимальная конфигурация для переходного проекта

\n

Ниже учебный пример. В нём два entry, один пакет jquery и legacy-код, который ещё использует свободное имя. Пример не измеряет размер бандла, не доказывает совместимость конкретного плагина и не заменяет проверку вашей версии Webpack и jQuery.

\n
const webpack = require('webpack');\n\nmodule.exports = {\n  entry: {\n    legacy: './src/legacy-entry.js',\n    modern: './src/modern-entry.js'\n  },\n  plugins: [\n    new webpack.ProvidePlugin({\n      $: 'jquery',\n      jQuery: 'jquery'\n    })\n  ]\n};
\n

В новом модуле зависимость остаётся видимой:

\n
import $ from 'jquery';\n\nexport function mount(form) {\n  return $(form).find('[data-mask]').length;\n}
\n

В legacy-модуле строка импорта может отсутствовать:

\n
$('.phone').inputmask('+7 (999) 999-99-99');
\n

Оба фрагмента должны ссылаться на один экземпляр, если Webpack собирает их в общий runtime или правильно выносит общий модуль. Это нужно проверить в фактической конфигурации. Само совпадение версий в package.json такого доказательства не даёт.

\n

Когда нужен window.jQuery

\n

Некоторые старые плагины не экспортируют функцию как модуль. Они выполняются сразу и ищут window.jQuery. Для такого кода сделайте отдельный модуль-инициализатор и импортируйте его до плагина:

\n
// src/jquery-global.js\nimport $ from 'jquery';\n\nwindow.$ = $;\nwindow.jQuery = $;\n\n// src/legacy-entry.js\nimport './jquery-global';\nimport 'inputmask/dist/jquery.inputmask';\nimport './legacy-app';
\n

Такой порядок относится к импортам внутри одного entry. Он не управляет отдельным CDN-скриптом, который HTML загрузит позже. В браузере проверьте именно window.jQuery перед и после подключения плагина, а затем вызовите метод плагина на реальном элементе формы.

\n

Отрицательный путь: CDN и externals

\n

Внешняя загрузка имеет смысл, когда платформа уже отдаёт jQuery или несколько приложений должны использовать один URL. Тогда Webpack не должен одновременно включать пакет в тот же бандл. Пример для глобального объекта:

\n
module.exports = {\n  externals: {\n    jquery: 'jQuery'\n  }\n};
\n

Теперь import $ from 'jquery' в собранном коде означает обращение к внешнему jQuery. HTML обязан загрузить библиотеку раньше бандла:

\n
<script src=\"https://cdn.example.test/jquery.min.js\"></script>\n<script src=\"/assets/legacy.js\"></script>
\n

URL в примере учебный. Не подставляйте его в рабочую страницу. Для production нужны зафиксированная версия, контроль доступности, политика безопасности и понятный план отказа. Если CDN недоступен, Webpack не сможет компенсировать это настройкой ProvidePlugin. Если браузер загрузит две версии jQuery, плагины могут зарегистрироваться в одном объекте, а приложение — работать с другим.

\n

Симптом → причина → проверка → действие

\n
СимптомПричинаПроверкаДействие
jQuery is not definedПлагин выполняется до глобального объектаПорядок Network и typeof window.jQuery перед импортомСобрать entry с инициализатором или исправить порядок внешних scripts
$(...).inputmask is not a functionПлагин получил другой объект или не загрузилсяСравнить window.jQuery === $ и проверить регистрацию $.fn.inputmaskОставить один источник jQuery и импортировать плагин после него
Работает только на одной страницеProvidePlugin действует только в собранных модулях этого entryСравнить entry, HTML и содержимое chunk-файловДобавить зависимость в нужный entry, а не рассчитывать на соседний бандл
Размер растёт после добавления второго entryНет общего chunk или настроены две независимые поставкиПосмотреть stats и число включённых модулей jqueryНастроить общую доставку только после проверки поведения legacy-кода
CDN-версия ломает плагинВерсия или порядок внешней загрузки не совпадает с контрактомПроверить фактический URL, версию и момент выполнения плагинаЗафиксировать совместимую версию или вернуть пакет в граф Webpack
\n

Порядок действий

\n
  1. Найдите все обращения к $, jQuery и window.jQuery. Отдельно отметьте модули, которые выполняются сразу при импорте.
  2. Для каждого entry выберите источник: пакет в графе Webpack или внешний глобальный script. Не оставляйте выбор на уровне случайного HTML-порядка.
  3. Оставьте явный import $ from 'jquery' в новом коде. Добавьте ProvidePlugin только к переходному слою, который нельзя быстро изменить.
  4. Если плагин читает window.jQuery, создайте один инициализатор и импортируйте его перед этим плагином.
  5. Соберите каждый entry и проверьте, что в нём есть ожидаемая зависимость или явно описан externals.
  6. Откройте реальную форму. Проверьте window.jQuery, $.fn.jquery, метод legacy-плагина и обработчик пользовательского события.
  7. После миграции потребителя удалите лишнюю глобальную настройку и повторите проверку. Иначе временная совместимость станет постоянной зависимостью.
\n

Ограничения

\n

ProvidePlugin не превращает любой свободный идентификатор в безопасную архитектуру. Он скрывает зависимость в исходном файле и усложняет перенос модуля в другой сборщик. Используйте его как переходный слой и уменьшайте область действия.

\n

Один объект jQuery не гарантирует совместимость. Плагин может требовать конкретную версию, поддерживать только старый API или конфликтовать с noConflict. Проверяйте реальный сценарий, а не только успешную компиляцию.

\n

В серверном рендеринге и Web Worker нет обычного window. Код, который без проверки обращается к window.jQuery, должен выполняться только в браузерном entry. Это отдельное ограничение окружения, а не проблема Webpack.

\n

Проверяемый критерий готовности

\n

Работу можно считать законченной, когда каждый entry имеет один документированный источник jQuery, сборка не содержит непреднамеренной второй копии, legacy-плагин получает тот же объект, что и приложение, а форма проходит реальный сценарий ввода. Дополнительно зафиксируйте проверку для страницы без legacy-кода: она не должна получать глобальную зависимость только потому, что она есть в соседнем бандле.

\n

Проверяемые источники

\n", "contentHtml": "

После переноса старого фронтенда в Webpack кнопка с inputmask перестаёт работать. В консоли появляется jQuery is not defined, $(...).inputmask is not a function или плагин загружается только на странице с «правильным» порядком скриптов. Ошибка часто выглядит случайной: новый модуль импортирует jQuery, а legacy-файл ищет её в window. Цена ошибки — сломанная форма, дублированная библиотека в нескольких бандлах и релиз, который нельзя уверенно проверить одной сборкой.

\n

Тезис простой: сначала нужно назвать потребителя jQuery, затем выбрать один способ доставки для каждого entry. Явный import связывает зависимость с графом модулей. ProvidePlugin помогает старому коду, который обращается к свободным $ или jQuery. Глобальный объект нужен только тем потребителям, которые действительно читают window.jQuery. CDN и externals образуют другой контракт: Webpack больше не кладёт библиотеку в бандл и ждёт готового глобального объекта снаружи.

\n

Симптом начинается с границы видимости

\n

Модуль с import $ from 'jquery' получает значение из графа Webpack. Это локальная переменная модуля. Она не обещает, что отдельный <script> в HTML увидит window.$. Обратное тоже верно: наличие window.jQuery не добавляет зависимость в граф и не делает её доступной каждому исходному файлу без настройки.

\n

ProvidePlugin решает третий случай. Webpack замечает свободный идентификатор в модуле и автоматически подставляет импорт. Поэтому код с $(selector) может собраться без строки import. Это не запись переменной в глобальный объект браузера и не исправление порядка независимых тегов script.

\n
\"Схема
Один entry должен доставить один объект jQuery раньше плагина, который его использует. Иллюстрация показывает границу между графом модулей и глобальным API браузера.
\n

Три способа доставки

\n

В проекте с несколькими entry не стоит начинать с единого глобального правила. Сначала составьте карту потребителей.

\n
СпособЧто получает кодКогда подходитЧего он не делает
Явный importЛокальный объект из графа WebpackНовый код и код, который можно менятьНе создаёт window.jQuery сам по себе
ProvidePluginАвтоматически подставленный импорт для свободного имениLegacy-модули с $ или jQueryНе чинит внешний script и не гарантирует порядок загрузки
externals или CDNГлобальный объект, предоставленный HTML или платформойОдна контролируемая внешняя загрузкаНе включает jQuery в бандл и не проверяет URL CDN
\n

Для переходного проекта обычно работает связка: новый код импортирует jQuery явно, а ограниченный legacy-слой получает ProvidePlugin. Если плагин проверяет именно window.jQuery, добавьте глобальный экспорт в одном entry. Не смешивайте этот режим с CDN без причины. Иначе один бандл будет использовать пакет, а другой — внешний файл.

\n

Минимальная конфигурация для переходного проекта

\n

Ниже учебный пример. В нём два entry, один пакет jquery и legacy-код, который ещё использует свободное имя. Пример не измеряет размер бандла, не доказывает совместимость конкретного плагина и не заменяет проверку вашей версии Webpack и jQuery.

\n
const webpack = require('webpack');\n\nmodule.exports = {\n  entry: {\n    legacy: './src/legacy-entry.js',\n    modern: './src/modern-entry.js'\n  },\n  plugins: [\n    new webpack.ProvidePlugin({\n      $: 'jquery',\n      jQuery: 'jquery'\n    })\n  ]\n};
\n

В новом модуле зависимость остаётся видимой:

\n
import $ from 'jquery';\n\nexport function mount(form) {\n  return $(form).find('[data-mask]').length;\n}
\n

В legacy-модуле строка импорта может отсутствовать:

\n
$('.phone').inputmask('+7 (999) 999-99-99');
\n

Оба фрагмента должны ссылаться на один экземпляр, если Webpack собирает их в общий runtime или правильно выносит общий модуль. Это нужно проверить в фактической конфигурации. Само совпадение версий в package.json такого доказательства не даёт.

\n

Когда нужен window.jQuery

\n

Некоторые старые плагины не экспортируют функцию как модуль. Они выполняются сразу и ищут window.jQuery. Для такого кода сделайте отдельный модуль-инициализатор и импортируйте его до плагина:

\n
// src/jquery-global.js\nimport $ from 'jquery';\n\nwindow.$ = $;\nwindow.jQuery = $;\n\n// src/legacy-entry.js\nimport './jquery-global';\nimport 'inputmask/dist/jquery.inputmask';\nimport './legacy-app';
\n

Такой порядок относится к импортам внутри одного entry. Он не управляет отдельным CDN-скриптом, который HTML загрузит позже. В браузере проверьте именно window.jQuery перед и после подключения плагина, а затем вызовите метод плагина на реальном элементе формы.

\n

Отрицательный путь: CDN и externals

\n

Внешняя загрузка имеет смысл, когда платформа уже отдаёт jQuery или несколько приложений должны использовать один URL. Тогда Webpack не должен одновременно включать пакет в тот же бандл. Пример для глобального объекта:

\n
module.exports = {\n  externals: {\n    jquery: 'jQuery'\n  }\n};
\n

Теперь import $ from 'jquery' в собранном коде означает обращение к внешнему jQuery. HTML обязан загрузить библиотеку раньше бандла:

\n
<script src=\"https://cdn.example.test/jquery.min.js\"></script>\n<script src=\"/assets/legacy.js\"></script>
\n

URL в примере учебный. Не подставляйте его в рабочую страницу. Для production нужны зафиксированная версия, контроль доступности, политика безопасности и понятный план отказа. Если CDN недоступен, Webpack не сможет компенсировать это настройкой ProvidePlugin. Если браузер загрузит две версии jQuery, плагины могут зарегистрироваться в одном объекте, а приложение — работать с другим.

\n

Симптом → причина → проверка → действие

\n
СимптомПричинаПроверкаДействие
jQuery is not definedПлагин выполняется до глобального объектаПорядок Network и typeof window.jQuery перед импортомСобрать entry с инициализатором или исправить порядок внешних scripts
$(...).inputmask is not a functionПлагин получил другой объект или не загрузилсяСравнить window.jQuery === $ и проверить регистрацию $.fn.inputmaskОставить один источник jQuery и импортировать плагин после него
Работает только на одной страницеProvidePlugin действует только в собранных модулях этого entryСравнить entry, HTML и содержимое chunk-файловДобавить зависимость в нужный entry, а не рассчитывать на соседний бандл
Размер растёт после добавления второго entryНет общего chunk или настроены две независимые поставкиПосмотреть stats и число включённых модулей jqueryНастроить общую доставку только после проверки поведения legacy-кода
CDN-версия ломает плагинВерсия или порядок внешней загрузки не совпадает с контрактомПроверить фактический URL, версию и момент выполнения плагинаЗафиксировать совместимую версию или вернуть пакет в граф Webpack
\n

Порядок действий

\n
  1. Найдите все обращения к $, jQuery и window.jQuery. Отдельно отметьте модули, которые выполняются сразу при импорте.
  2. Для каждого entry выберите источник: пакет в графе Webpack или внешний глобальный script. Не оставляйте выбор на уровне случайного HTML-порядка.
  3. Оставьте явный import $ from 'jquery' в новом коде. Добавьте ProvidePlugin только к переходному слою, который нельзя быстро изменить.
  4. Если плагин читает window.jQuery, создайте один инициализатор и импортируйте его перед этим плагином.
  5. Соберите каждый entry и проверьте, что в нём есть ожидаемая зависимость или явно описан externals.
  6. Откройте реальную форму. Проверьте window.jQuery, $.fn.jquery, метод legacy-плагина и обработчик пользовательского события.
  7. После миграции потребителя удалите лишнюю глобальную настройку и повторите проверку. Иначе временная совместимость станет постоянной зависимостью.
\n

Ограничения

\n

ProvidePlugin не превращает любой свободный идентификатор в безопасную архитектуру. Он скрывает зависимость в исходном файле и усложняет перенос модуля в другой сборщик. Используйте его как переходный слой и уменьшайте область действия.

\n

Один объект jQuery не гарантирует совместимость. Плагин может требовать конкретную версию, поддерживать только старый API или конфликтовать с noConflict. Проверяйте реальный сценарий, а не только успешную компиляцию.

\n

В серверном рендеринге и Web Worker нет обычного window. Код, который без проверки обращается к window.jQuery, должен выполняться только в браузерном entry. Это отдельное ограничение окружения, а не проблема Webpack.

\n

Проверяемый критерий готовности

\n

Работу можно считать законченной, когда каждый entry имеет один документированный источник jQuery, сборка не содержит непреднамеренной второй копии, legacy-плагин получает тот же объект, что и приложение, а форма проходит реальный сценарий ввода. Дополнительно зафиксируйте проверку для страницы без legacy-кода: она не должна получать глобальную зависимость только потому, что она есть в соседнем бандле.

\n

Проверяемые источники

\n", "contentHtml": "

После переноса старого фронтенда в Webpack кнопка с inputmask перестаёт работать. В консоли появляется jQuery is not defined, $(...).inputmask is not a function или плагин загружается только на странице с «правильным» порядком скриптов. Ошибка часто выглядит случайной: новый модуль импортирует jQuery, а legacy-файл ищет её в window. Цена ошибки — сломанная форма, дублированная библиотека в нескольких бандлах и релиз, который нельзя уверенно проверить одной сборкой.

\n

Тезис простой: сначала нужно назвать потребителя jQuery, затем выбрать один способ доставки для каждого entry. Явный import связывает зависимость с графом модулей. ProvidePlugin помогает старому коду, который обращается к свободным $ или jQuery. Глобальный объект нужен только тем потребителям, которые действительно читают window.jQuery. CDN и externals образуют другой контракт: Webpack больше не кладёт библиотеку в бандл и ждёт готового глобального объекта снаружи.

\n

Симптом начинается с границы видимости

\n

Модуль с import $ from 'jquery' получает значение из графа Webpack. Это локальная переменная модуля. Она не обещает, что отдельный <script> в HTML увидит window.$. Обратное тоже верно: наличие window.jQuery не добавляет зависимость в граф и не делает её доступной каждому исходному файлу без настройки.

\n

ProvidePlugin решает третий случай. Webpack замечает свободный идентификатор в модуле и автоматически подставляет импорт. Поэтому код с $(selector) может собраться без строки import. Это не запись переменной в глобальный объект браузера и не исправление порядка независимых тегов script.

\n
\"Схема
Один entry должен доставить один объект jQuery раньше плагина, который его использует. Иллюстрация показывает границу между графом модулей и глобальным API браузера.
\n

Три способа доставки

\n

В проекте с несколькими entry не стоит начинать с единого глобального правила. Сначала составьте карту потребителей.

\n
СпособЧто получает кодКогда подходитЧего он не делает
Явный importЛокальный объект из графа WebpackНовый код и код, который можно менятьНе создаёт window.jQuery сам по себе
ProvidePluginАвтоматически подставленный импорт для свободного имениLegacy-модули с $ или jQueryНе чинит внешний script и не гарантирует порядок загрузки
externals или CDNГлобальный объект, предоставленный HTML или платформойОдна контролируемая внешняя загрузкаНе включает jQuery в бандл и не проверяет URL CDN
\n

Для переходного проекта обычно работает связка: новый код импортирует jQuery явно, а ограниченный legacy-слой получает ProvidePlugin. Если плагин проверяет именно window.jQuery, добавьте глобальный экспорт в одном entry. Не смешивайте этот режим с CDN без причины. Иначе один бандл будет использовать пакет, а другой — внешний файл.

\n

Минимальная конфигурация для переходного проекта

\n

Ниже учебный пример. В нём два entry, один пакет jquery и legacy-код, который ещё использует свободное имя. Пример не измеряет размер бандла, не доказывает совместимость конкретного плагина и не заменяет проверку вашей версии Webpack и jQuery.

\n
const webpack = require('webpack');\n\nmodule.exports = {\n  entry: {\n    legacy: './src/legacy-entry.js',\n    modern: './src/modern-entry.js'\n  },\n  plugins: [\n    new webpack.ProvidePlugin({\n      $: 'jquery',\n      jQuery: 'jquery'\n    })\n  ]\n};
\n

В новом модуле зависимость остаётся видимой:

\n
import $ from 'jquery';\n\nexport function mount(form) {\n  return $(form).find('[data-mask]').length;\n}
\n

В legacy-модуле строка импорта может отсутствовать:

\n
$('.phone').inputmask('+7 (999) 999-99-99');
\n

Оба фрагмента должны ссылаться на один экземпляр, если Webpack собирает их в общий runtime или правильно выносит общий модуль. Это нужно проверить в фактической конфигурации. Само совпадение версий в package.json такого доказательства не даёт.

\n

Когда нужен window.jQuery

\n

Некоторые старые плагины не экспортируют функцию как модуль. Они выполняются сразу и ищут window.jQuery. Для такого кода сделайте отдельный модуль-инициализатор и импортируйте его до плагина:

\n
// src/jquery-global.js\nimport $ from 'jquery';\n\nwindow.$ = $;\nwindow.jQuery = $;\n\n// src/legacy-entry.js\nimport './jquery-global';\nimport 'inputmask/dist/jquery.inputmask';\nimport './legacy-app';
\n

Такой порядок относится к импортам внутри одного entry. Он не управляет отдельным CDN-скриптом, который HTML загрузит позже. В браузере проверьте именно window.jQuery перед и после подключения плагина, а затем вызовите метод плагина на реальном элементе формы.

\n

Отрицательный путь: CDN и externals

\n

Внешняя загрузка имеет смысл, когда платформа уже отдаёт jQuery или несколько приложений должны использовать один URL. Тогда Webpack не должен одновременно включать пакет в тот же бандл. Пример для глобального объекта:

\n
module.exports = {\n  externals: {\n    jquery: 'jQuery'\n  }\n};
\n

Теперь import $ from 'jquery' в собранном коде означает обращение к внешнему jQuery. HTML обязан загрузить библиотеку раньше бандла:

\n
<script src=\"https://cdn.example.test/jquery.min.js\"></script>\n<script src=\"/assets/legacy.js\"></script>
\n

URL в примере учебный. Не подставляйте его в рабочую страницу. Для production нужны зафиксированная версия, контроль доступности, политика безопасности и понятный план отказа. Если CDN недоступен, Webpack не сможет компенсировать это настройкой ProvidePlugin. Если браузер загрузит две версии jQuery, плагины могут зарегистрироваться в одном объекте, а приложение — работать с другим.

\n

Симптом → причина → проверка → действие

\n
СимптомПричинаПроверкаДействие
jQuery is not definedПлагин выполняется до глобального объектаПорядок Network и typeof window.jQuery перед импортомСобрать entry с инициализатором или исправить порядок внешних scripts
$(...).inputmask is not a functionПлагин получил другой объект или не загрузилсяСравнить window.jQuery === $ и проверить регистрацию $.fn.inputmaskОставить один источник jQuery и импортировать плагин после него
Работает только на одной страницеProvidePlugin действует только в собранных модулях этого entryСравнить entry, HTML и содержимое chunk-файловДобавить зависимость в нужный entry, а не рассчитывать на соседний бандл
Размер растёт после добавления второго entryНет общего chunk или настроены две независимые поставкиПосмотреть stats и число включённых модулей jqueryНастроить общую доставку только после проверки поведения legacy-кода
CDN-версия ломает плагинВерсия или порядок внешней загрузки не совпадает с контрактомПроверить фактический URL, версию и момент выполнения плагинаЗафиксировать совместимую версию или вернуть пакет в граф Webpack
\n

Порядок действий

\n
  1. Найдите все обращения к $, jQuery и window.jQuery. Отдельно отметьте модули, которые выполняются сразу при импорте.
  2. Для каждого entry выберите источник: пакет в графе Webpack или внешний глобальный script. Не оставляйте выбор на уровне случайного HTML-порядка.
  3. Оставьте явный import $ from 'jquery' в новом коде. Добавьте ProvidePlugin только к переходному слою, который нельзя быстро изменить.
  4. Если плагин читает window.jQuery, создайте один инициализатор и импортируйте его перед этим плагином.
  5. Соберите каждый entry и проверьте, что в нём есть ожидаемая зависимость или явно описан externals.
  6. Откройте реальную форму. Проверьте window.jQuery, $.fn.jquery, метод legacy-плагина и обработчик пользовательского события.
  7. После миграции потребителя удалите лишнюю глобальную настройку и повторите проверку. Иначе временная совместимость станет постоянной зависимостью.
\n

Ограничения

\n

ProvidePlugin не превращает любой свободный идентификатор в безопасную архитектуру. Он скрывает зависимость в исходном файле и усложняет перенос модуля в другой сборщик. Используйте его как переходный слой и уменьшайте область действия.

\n

Один объект jQuery не гарантирует совместимость. Плагин может требовать конкретную версию, поддерживать только старый API или конфликтовать с noConflict. Проверяйте реальный сценарий, а не только успешную компиляцию.

\n

В серверном рендеринге и Web Worker нет обычного window. Код, который без проверки обращается к window.jQuery, должен выполняться только в браузерном entry. Это отдельное ограничение окружения, а не проблема Webpack.

\n

Проверяемый критерий готовности

\n

Работу можно считать законченной, когда каждый entry имеет один документированный источник jQuery, сборка не содержит непреднамеренной второй копии, legacy-плагин получает тот же объект, что и приложение, а форма проходит реальный сценарий ввода. Дополнительно зафиксируйте проверку для страницы без legacy-кода: она не должна получать глобальную зависимость только потому, что она есть в соседнем бандле.

\n

Проверяемые источники

\n", "contentHtml": "

После переноса старого фронтенда в Webpack кнопка с inputmask перестаёт работать. В консоли появляется jQuery is not defined, $(...).inputmask is not a function или плагин загружается только на странице с «правильным» порядком скриптов. Ошибка часто выглядит случайной: новый модуль импортирует jQuery, а legacy-файл ищет её в window. Цена ошибки — сломанная форма, дублированная библиотека в нескольких бандлах и релиз, который нельзя уверенно проверить одной сборкой.

\n

Тезис простой: сначала нужно назвать потребителя jQuery, затем выбрать один способ доставки для каждого entry. Явный import связывает зависимость с графом модулей. ProvidePlugin помогает старому коду, который обращается к свободным $ или jQuery. Глобальный объект нужен только тем потребителям, которые действительно читают window.jQuery. CDN и externals образуют другой контракт: Webpack больше не кладёт библиотеку в бандл и ждёт готового глобального объекта снаружи.

\n

Симптом начинается с границы видимости

\n

Модуль с import $ from 'jquery' получает значение из графа Webpack. Это локальная переменная модуля. Она не обещает, что отдельный <script> в HTML увидит window.$. Обратное тоже верно: наличие window.jQuery не добавляет зависимость в граф и не делает её доступной каждому исходному файлу без настройки.

\n

ProvidePlugin решает третий случай. Webpack замечает свободный идентификатор в модуле и автоматически подставляет импорт. Поэтому код с $(selector) может собраться без строки import. Это не запись переменной в глобальный объект браузера и не исправление порядка независимых тегов script.

\n
\"Схема
Один entry должен доставить один объект jQuery раньше плагина, который его использует. Иллюстрация показывает границу между графом модулей и глобальным API браузера.
\n

Три способа доставки

\n

В проекте с несколькими entry не стоит начинать с единого глобального правила. Сначала составьте карту потребителей.

\n
СпособЧто получает кодКогда подходитЧего он не делает
Явный importЛокальный объект из графа WebpackНовый код и код, который можно менятьНе создаёт window.jQuery сам по себе
ProvidePluginАвтоматически подставленный импорт для свободного имениLegacy-модули с $ или jQueryНе чинит внешний script и не гарантирует порядок загрузки
externals или CDNГлобальный объект, предоставленный HTML или платформойОдна контролируемая внешняя загрузкаНе включает jQuery в бандл и не проверяет URL CDN
\n

Для переходного проекта обычно работает связка: новый код импортирует jQuery явно, а ограниченный legacy-слой получает ProvidePlugin. Если плагин проверяет именно window.jQuery, добавьте глобальный экспорт в одном entry. Не смешивайте этот режим с CDN без причины. Иначе один бандл будет использовать пакет, а другой — внешний файл.

\n

Минимальная конфигурация для переходного проекта

\n

Ниже учебный пример. В нём два entry, один пакет jquery и legacy-код, который ещё использует свободное имя. Пример не измеряет размер бандла, не доказывает совместимость конкретного плагина и не заменяет проверку вашей версии Webpack и jQuery.

\n
const webpack = require('webpack');\n\nmodule.exports = {\n  entry: {\n    legacy: './src/legacy-entry.js',\n    modern: './src/modern-entry.js'\n  },\n  plugins: [\n    new webpack.ProvidePlugin({\n      $: 'jquery',\n      jQuery: 'jquery'\n    })\n  ]\n};
\n

В новом модуле зависимость остаётся видимой:

\n
import $ from 'jquery';\n\nexport function mount(form) {\n  return $(form).find('[data-mask]').length;\n}
\n

В legacy-модуле строка импорта может отсутствовать:

\n
$('.phone').inputmask('+7 (999) 999-99-99');
\n

Оба фрагмента должны ссылаться на один экземпляр, если Webpack собирает их в общий runtime или правильно выносит общий модуль. Это нужно проверить в фактической конфигурации. Само совпадение версий в package.json такого доказательства не даёт.

\n

Когда нужен window.jQuery

\n

Некоторые старые плагины не экспортируют функцию как модуль. Они выполняются сразу и ищут window.jQuery. Для такого кода сделайте отдельный модуль-инициализатор и импортируйте его до плагина:

\n
// src/jquery-global.js\nimport $ from 'jquery';\n\nwindow.$ = $;\nwindow.jQuery = $;\n\n// src/legacy-entry.js\nimport './jquery-global';\nimport 'inputmask/dist/jquery.inputmask';\nimport './legacy-app';
\n

Такой порядок относится к импортам внутри одного entry. Он не управляет отдельным CDN-скриптом, который HTML загрузит позже. В браузере проверьте именно window.jQuery перед и после подключения плагина, а затем вызовите метод плагина на реальном элементе формы.

\n

Отрицательный путь: CDN и externals

\n

Внешняя загрузка имеет смысл, когда платформа уже отдаёт jQuery или несколько приложений должны использовать один URL. Тогда Webpack не должен одновременно включать пакет в тот же бандл. Пример для глобального объекта:

\n
module.exports = {\n  externals: {\n    jquery: 'jQuery'\n  }\n};
\n

Теперь import $ from 'jquery' в собранном коде означает обращение к внешнему jQuery. HTML обязан загрузить библиотеку раньше бандла:

\n
<script src=\"https://cdn.example.test/jquery.min.js\"></script>\n<script src=\"/assets/legacy.js\"></script>
\n

URL в примере учебный. Не подставляйте его в рабочую страницу. Для production нужны зафиксированная версия, контроль доступности, политика безопасности и понятный план отказа. Если CDN недоступен, Webpack не сможет компенсировать это настройкой ProvidePlugin. Если браузер загрузит две версии jQuery, плагины могут зарегистрироваться в одном объекте, а приложение — работать с другим.

\n

Симптом → причина → проверка → действие

\n
СимптомПричинаПроверкаДействие
jQuery is not definedПлагин выполняется до глобального объектаПорядок Network и typeof window.jQuery перед импортомСобрать entry с инициализатором или исправить порядок внешних scripts
$(...).inputmask is not a functionПлагин получил другой объект или не загрузилсяСравнить window.jQuery === $ и проверить регистрацию $.fn.inputmaskОставить один источник jQuery и импортировать плагин после него
Работает только на одной страницеProvidePlugin действует только в собранных модулях этого entryСравнить entry, HTML и содержимое chunk-файловДобавить зависимость в нужный entry, а не рассчитывать на соседний бандл
Размер растёт после добавления второго entryНет общего chunk или настроены две независимые поставкиПосмотреть stats и число включённых модулей jqueryНастроить общую доставку только после проверки поведения legacy-кода
CDN-версия ломает плагинВерсия или порядок внешней загрузки не совпадает с контрактомПроверить фактический URL, версию и момент выполнения плагинаЗафиксировать совместимую версию или вернуть пакет в граф Webpack
\n

Порядок действий

\n
  1. Найдите все обращения к $, jQuery и window.jQuery. Отдельно отметьте модули, которые выполняются сразу при импорте.
  2. Для каждого entry выберите источник: пакет в графе Webpack или внешний глобальный script. Не оставляйте выбор на уровне случайного HTML-порядка.
  3. Оставьте явный import $ from 'jquery' в новом коде. Добавьте ProvidePlugin только к переходному слою, который нельзя быстро изменить.
  4. Если плагин читает window.jQuery, создайте один инициализатор и импортируйте его перед этим плагином.
  5. Соберите каждый entry и проверьте, что в нём есть ожидаемая зависимость или явно описан externals.
  6. Откройте реальную форму. Проверьте window.jQuery, $.fn.jquery, метод legacy-плагина и обработчик пользовательского события.
  7. После миграции потребителя удалите лишнюю глобальную настройку и повторите проверку. Иначе временная совместимость станет постоянной зависимостью.
\n

Ограничения

\n

ProvidePlugin не превращает любой свободный идентификатор в безопасную архитектуру. Он скрывает зависимость в исходном файле и усложняет перенос модуля в другой сборщик. Используйте его как переходный слой и уменьшайте область действия.

\n

Один объект jQuery не гарантирует совместимость. Плагин может требовать конкретную версию, поддерживать только старый API или конфликтовать с noConflict. Проверяйте реальный сценарий, а не только успешную компиляцию.

\n

В серверном рендеринге и Web Worker нет обычного window. Код, который без проверки обращается к window.jQuery, должен выполняться только в браузерном entry. Это отдельное ограничение окружения, а не проблема Webpack.

\n

Проверяемый критерий готовности

\n

Работу можно считать законченной, когда каждый entry имеет один документированный источник jQuery, сборка не содержит непреднамеренной второй копии, legacy-плагин получает тот же объект, что и приложение, а форма проходит реальный сценарий ввода. Дополнительно зафиксируйте проверку для страницы без legacy-кода: она не должна получать глобальную зависимость только потому, что она есть в соседнем бандле.

\n

Проверяемые источники

\n" }