11 lines
66 KiB
JSON
11 lines
66 KiB
JSON
{
|
||
"index": 324,
|
||
"slug": "использование-jquery-в-webpack",
|
||
"title": "jQuery в Webpack: как связать модульный код и legacy-плагины",
|
||
"excerpt": "После миграции на Webpack плагин видит $ только на одной странице или получает другой объект jQuery. Разбираем границы ProvidePlugin, window.jQuery и externals и заканчиваем проверяемым критерием готовности.",
|
||
"contentHtml": "<p>После переноса старого фронтенда в Webpack кнопка с inputmask перестаёт работать. В консоли появляется <code>jQuery is not defined</code>, <code>$(...).inputmask is not a function</code> или плагин загружается только на странице с «правильным» порядком скриптов. Ошибка часто выглядит случайной: новый модуль импортирует jQuery, а legacy-файл ищет её в <code>window</code>. Цена ошибки — сломанная форма, дублированная библиотека в нескольких бандлах и релиз, который нельзя уверенно проверить одной сборкой.</p>\n<p>Тезис простой: сначала нужно назвать потребителя jQuery, затем выбрать один способ доставки для каждого entry. Явный <code>import</code> связывает зависимость с графом модулей. <code>ProvidePlugin</code> помогает старому коду, который обращается к свободным <code>$</code> или <code>jQuery</code>. Глобальный объект нужен только тем потребителям, которые действительно читают <code>window.jQuery</code>. CDN и <code>externals</code> образуют другой контракт: Webpack больше не кладёт библиотеку в бандл и ждёт готового глобального объекта снаружи.</p>\n<h2>Симптом начинается с границы видимости</h2>\n<p>Модуль с <code>import $ from 'jquery'</code> получает значение из графа Webpack. Это локальная переменная модуля. Она не обещает, что отдельный <code><script></code> в HTML увидит <code>window.$</code>. Обратное тоже верно: наличие <code>window.jQuery</code> не добавляет зависимость в граф и не делает её доступной каждому исходному файлу без настройки.</p>\n<p><code>ProvidePlugin</code> решает третий случай. Webpack замечает свободный идентификатор в модуле и автоматически подставляет импорт. Поэтому код с <code>$(selector)</code> может собраться без строки <code>import</code>. Это не запись переменной в глобальный объект браузера и не исправление порядка независимых тегов <code>script</code>.</p>\n<figure><img src=\"/assets/illustrations/jquery-webpack.svg\" alt=\"Схема связи jQuery, Webpack и legacy-плагина\" /><figcaption>Один entry должен доставить один объект jQuery раньше плагина, который его использует. Иллюстрация показывает границу между графом модулей и глобальным API браузера.</figcaption></figure>\n<h2>Три способа доставки</h2>\n<p>В проекте с несколькими entry не стоит начинать с единого глобального правила. Сначала составьте карту потребителей.</p>\n<table><thead><tr><th scope=\"col\">Способ</th><th scope=\"col\">Что получает код</th><th scope=\"col\">Когда подходит</th><th scope=\"col\">Чего он не делает</th></tr></thead><tbody><tr><td>Явный import</td><td>Локальный объект из графа Webpack</td><td>Новый код и код, который можно менять</td><td>Не создаёт <code>window.jQuery</code> сам по себе</td></tr><tr><td>ProvidePlugin</td><td>Автоматически подставленный импорт для свободного имени</td><td>Legacy-модули с <code>$</code> или <code>jQuery</code></td><td>Не чинит внешний script и не гарантирует порядок загрузки</td></tr><tr><td>externals или CDN</td><td>Глобальный объект, предоставленный HTML или платформой</td><td>Одна контролируемая внешняя загрузка</td><td>Не включает jQuery в бандл и не проверяет URL CDN</td></tr></tbody></table>\n<p>Для переходного проекта обычно работает связка: новый код импортирует jQuery явно, а ограниченный legacy-слой получает <code>ProvidePlugin</code>. Если плагин проверяет именно <code>window.jQuery</code>, добавьте глобальный экспорт в одном entry. Не смешивайте этот режим с CDN без причины. Иначе один бандл будет использовать пакет, а другой — внешний файл.</p>\n<h2>Минимальная конфигурация для переходного проекта</h2>\n<p>Ниже учебный пример. В нём два entry, один пакет <code>jquery</code> и legacy-код, который ещё использует свободное имя. Пример не измеряет размер бандла, не доказывает совместимость конкретного плагина и не заменяет проверку вашей версии Webpack и jQuery.</p>\n<pre><code>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};</code></pre>\n<p>В новом модуле зависимость остаётся видимой:</p>\n<pre><code>import $ from 'jquery';\n\nexport function mount(form) {\n return $(form).find('[data-mask]').length;\n}</code></pre>\n<p>В legacy-модуле строка импорта может отсутствовать:</p>\n<pre><code>$('.phone').inputmask('+7 (999) 999-99-99');</code></pre>\n<p>Оба фрагмента должны ссылаться на один экземпляр, если Webpack собирает их в общий runtime или правильно выносит общий модуль. Это нужно проверить в фактической конфигурации. Само совпадение версий в <code>package.json</code> такого доказательства не даёт.</p>\n<h2>Когда нужен window.jQuery</h2>\n<p>Некоторые старые плагины не экспортируют функцию как модуль. Они выполняются сразу и ищут <code>window.jQuery</code>. Для такого кода сделайте отдельный модуль-инициализатор и импортируйте его до плагина:</p>\n<pre><code>// 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';</code></pre>\n<p>Такой порядок относится к импортам внутри одного entry. Он не управляет отдельным CDN-скриптом, который HTML загрузит позже. В браузере проверьте именно <code>window.jQuery</code> перед и после подключения плагина, а затем вызовите метод плагина на реальном элементе формы.</p>\n<h2>Отрицательный путь: CDN и externals</h2>\n<p>Внешняя загрузка имеет смысл, когда платформа уже отдаёт jQuery или несколько приложений должны использовать один URL. Тогда Webpack не должен одновременно включать пакет в тот же бандл. Пример для глобального объекта:</p>\n<pre><code>module.exports = {\n externals: {\n jquery: 'jQuery'\n }\n};</code></pre>\n<p>Теперь <code>import $ from 'jquery'</code> в собранном коде означает обращение к внешнему <code>jQuery</code>. HTML обязан загрузить библиотеку раньше бандла:</p>\n<pre><code><script src=\"https://cdn.example.test/jquery.min.js\"></script>\n<script src=\"/assets/legacy.js\"></script></code></pre>\n<p>URL в примере учебный. Не подставляйте его в рабочую страницу. Для production нужны зафиксированная версия, контроль доступности, политика безопасности и понятный план отказа. Если CDN недоступен, Webpack не сможет компенсировать это настройкой <code>ProvidePlugin</code>. Если браузер загрузит две версии jQuery, плагины могут зарегистрироваться в одном объекте, а приложение — работать с другим.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<table><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td><code>jQuery is not defined</code></td><td>Плагин выполняется до глобального объекта</td><td>Порядок Network и <code>typeof window.jQuery</code> перед импортом</td><td>Собрать entry с инициализатором или исправить порядок внешних scripts</td></tr><tr><td><code>$(...).inputmask is not a function</code></td><td>Плагин получил другой объект или не загрузился</td><td>Сравнить <code>window.jQuery === $</code> и проверить регистрацию <code>$.fn.inputmask</code></td><td>Оставить один источник jQuery и импортировать плагин после него</td></tr><tr><td>Работает только на одной странице</td><td>ProvidePlugin действует только в собранных модулях этого entry</td><td>Сравнить entry, HTML и содержимое chunk-файлов</td><td>Добавить зависимость в нужный entry, а не рассчитывать на соседний бандл</td></tr><tr><td>Размер растёт после добавления второго entry</td><td>Нет общего chunk или настроены две независимые поставки</td><td>Посмотреть stats и число включённых модулей jquery</td><td>Настроить общую доставку только после проверки поведения legacy-кода</td></tr><tr><td>CDN-версия ломает плагин</td><td>Версия или порядок внешней загрузки не совпадает с контрактом</td><td>Проверить фактический URL, версию и момент выполнения плагина</td><td>Зафиксировать совместимую версию или вернуть пакет в граф Webpack</td></tr></tbody></table>\n<h2>Порядок действий</h2>\n<ol><li>Найдите все обращения к <code>$</code>, <code>jQuery</code> и <code>window.jQuery</code>. Отдельно отметьте модули, которые выполняются сразу при импорте.</li><li>Для каждого entry выберите источник: пакет в графе Webpack или внешний глобальный script. Не оставляйте выбор на уровне случайного HTML-порядка.</li><li>Оставьте явный <code>import $ from 'jquery'</code> в новом коде. Добавьте <code>ProvidePlugin</code> только к переходному слою, который нельзя быстро изменить.</li><li>Если плагин читает <code>window.jQuery</code>, создайте один инициализатор и импортируйте его перед этим плагином.</li><li>Соберите каждый entry и проверьте, что в нём есть ожидаемая зависимость или явно описан <code>externals</code>.</li><li>Откройте реальную форму. Проверьте <code>window.jQuery</code>, <code>$.fn.jquery</code>, метод legacy-плагина и обработчик пользовательского события.</li><li>После миграции потребителя удалите лишнюю глобальную настройку и повторите проверку. Иначе временная совместимость станет постоянной зависимостью.</li></ol>\n<h2>Ограничения</h2>\n<p><code>ProvidePlugin</code> не превращает любой свободный идентификатор в безопасную архитектуру. Он скрывает зависимость в исходном файле и усложняет перенос модуля в другой сборщик. Используйте его как переходный слой и уменьшайте область действия.</p>\n<p>Один объект jQuery не гарантирует совместимость. Плагин может требовать конкретную версию, поддерживать только старый API или конфликтовать с <code>noConflict</code>. Проверяйте реальный сценарий, а не только успешную компиляцию.</p>\n<p>В серверном рендеринге и Web Worker нет обычного <code>window</code>. Код, который без проверки обращается к <code>window.jQuery</code>, должен выполняться только в браузерном entry. Это отдельное ограничение окружения, а не проблема Webpack.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Работу можно считать законченной, когда каждый entry имеет один документированный источник jQuery, сборка не содержит непреднамеренной второй копии, legacy-плагин получает тот же объект, что и приложение, а форма проходит реальный сценарий ввода. Дополнительно зафиксируйте проверку для страницы без legacy-кода: она не должна получать глобальную зависимость только потому, что она есть в соседнем бандле.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://webpack.js.org/plugins/provide-plugin/\" target=\"_blank\" rel=\"noopener\">Webpack: ProvidePlugin</a> — описывает автоматическую подстановку модулей и пример с <code>$</code>, <code>jQuery</code> и <code>window.jQuery</code>.</li><li><a href=\"https://webpack.js.org/configuration/externals/\" target=\"_blank\" rel=\"noopener\">Webpack: externals</a> — фиксирует контракт зависимостей, которые остаются вне сборки.</li><li><a href=\"https://api.jquery.com/jQuery/\" target=\"_blank\" rel=\"noopener\">jQuery API: jQuery()</a> — официальный справочник вызова jQuery и публичного объекта библиотеки.</li></ul>",
|
||
"contentHtml": "<p>После переноса старого фронтенда в Webpack кнопка с inputmask перестаёт работать. В консоли появляется <code>jQuery is not defined</code>, <code>$(...).inputmask is not a function</code> или плагин загружается только на странице с «правильным» порядком скриптов. Ошибка часто выглядит случайной: новый модуль импортирует jQuery, а legacy-файл ищет её в <code>window</code>. Цена ошибки — сломанная форма, дублированная библиотека в нескольких бандлах и релиз, который нельзя уверенно проверить одной сборкой.</p>\n<p>Тезис простой: сначала нужно назвать потребителя jQuery, затем выбрать один способ доставки для каждого entry. Явный <code>import</code> связывает зависимость с графом модулей. <code>ProvidePlugin</code> помогает старому коду, который обращается к свободным <code>$</code> или <code>jQuery</code>. Глобальный объект нужен только тем потребителям, которые действительно читают <code>window.jQuery</code>. CDN и <code>externals</code> образуют другой контракт: Webpack больше не кладёт библиотеку в бандл и ждёт готового глобального объекта снаружи.</p>\n<h2>Симптом начинается с границы видимости</h2>\n<p>Модуль с <code>import $ from 'jquery'</code> получает значение из графа Webpack. Это локальная переменная модуля. Она не обещает, что отдельный <code><script></code> в HTML увидит <code>window.$</code>. Обратное тоже верно: наличие <code>window.jQuery</code> не добавляет зависимость в граф и не делает её доступной каждому исходному файлу без настройки.</p>\n<p><code>ProvidePlugin</code> решает третий случай. Webpack замечает свободный идентификатор в модуле и автоматически подставляет импорт. Поэтому код с <code>$(selector)</code> может собраться без строки <code>import</code>. Это не запись переменной в глобальный объект браузера и не исправление порядка независимых тегов <code>script</code>.</p>\n<figure><img src=\"/assets/illustrations/jquery-webpack.svg\" alt=\"Схема связи jQuery, Webpack и legacy-плагина\" /><figcaption>Один entry должен доставить один объект jQuery раньше плагина, который его использует. Иллюстрация показывает границу между графом модулей и глобальным API браузера.</figcaption></figure>\n<h2>Три способа доставки</h2>\n<p>В проекте с несколькими entry не стоит начинать с единого глобального правила. Сначала составьте карту потребителей.</p>\n<table><thead><tr><th scope=\"col\">Способ</th><th scope=\"col\">Что получает код</th><th scope=\"col\">Когда подходит</th><th scope=\"col\">Чего он не делает</th></tr></thead><tbody><tr><td>Явный import</td><td>Локальный объект из графа Webpack</td><td>Новый код и код, который можно менять</td><td>Не создаёт <code>window.jQuery</code> сам по себе</td></tr><tr><td>ProvidePlugin</td><td>Автоматически подставленный импорт для свободного имени</td><td>Legacy-модули с <code>$</code> или <code>jQuery</code></td><td>Не чинит внешний script и не гарантирует порядок загрузки</td></tr><tr><td>externals или CDN</td><td>Глобальный объект, предоставленный HTML или платформой</td><td>Одна контролируемая внешняя загрузка</td><td>Не включает jQuery в бандл и не проверяет URL CDN</td></tr></tbody></table>\n<p>Для переходного проекта обычно работает связка: новый код импортирует jQuery явно, а ограниченный legacy-слой получает <code>ProvidePlugin</code>. Если плагин проверяет именно <code>window.jQuery</code>, добавьте глобальный экспорт в одном entry. Не смешивайте этот режим с CDN без причины. Иначе один бандл будет использовать пакет, а другой — внешний файл.</p>\n<h2>Минимальная конфигурация для переходного проекта</h2>\n<p>Ниже учебный пример. В нём два entry, один пакет <code>jquery</code> и legacy-код, который ещё использует свободное имя. Пример не измеряет размер бандла, не доказывает совместимость конкретного плагина и не заменяет проверку вашей версии Webpack и jQuery.</p>\n<pre><code>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};</code></pre>\n<p>В новом модуле зависимость остаётся видимой:</p>\n<pre><code>import $ from 'jquery';\n\nexport function mount(form) {\n return $(form).find('[data-mask]').length;\n}</code></pre>\n<p>В legacy-модуле строка импорта может отсутствовать:</p>\n<pre><code>$('.phone').inputmask('+7 (999) 999-99-99');</code></pre>\n<p>Оба фрагмента должны ссылаться на один экземпляр, если Webpack собирает их в общий runtime или правильно выносит общий модуль. Это нужно проверить в фактической конфигурации. Само совпадение версий в <code>package.json</code> такого доказательства не даёт.</p>\n<h2>Когда нужен window.jQuery</h2>\n<p>Некоторые старые плагины не экспортируют функцию как модуль. Они выполняются сразу и ищут <code>window.jQuery</code>. Для такого кода сделайте отдельный модуль-инициализатор и импортируйте его до плагина:</p>\n<pre><code>// 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';</code></pre>\n<p>Такой порядок относится к импортам внутри одного entry. Он не управляет отдельным CDN-скриптом, который HTML загрузит позже. В браузере проверьте именно <code>window.jQuery</code> перед и после подключения плагина, а затем вызовите метод плагина на реальном элементе формы.</p>\n<h2>Отрицательный путь: CDN и externals</h2>\n<p>Внешняя загрузка имеет смысл, когда платформа уже отдаёт jQuery или несколько приложений должны использовать один URL. Тогда Webpack не должен одновременно включать пакет в тот же бандл. Пример для глобального объекта:</p>\n<pre><code>module.exports = {\n externals: {\n jquery: 'jQuery'\n }\n};</code></pre>\n<p>Теперь <code>import $ from 'jquery'</code> в собранном коде означает обращение к внешнему <code>jQuery</code>. HTML обязан загрузить библиотеку раньше бандла:</p>\n<pre><code><script src=\"https://cdn.example.test/jquery.min.js\"></script>\n<script src=\"/assets/legacy.js\"></script></code></pre>\n<p>URL в примере учебный. Не подставляйте его в рабочую страницу. Для production нужны зафиксированная версия, контроль доступности, политика безопасности и понятный план отказа. Если CDN недоступен, Webpack не сможет компенсировать это настройкой <code>ProvidePlugin</code>. Если браузер загрузит две версии jQuery, плагины могут зарегистрироваться в одном объекте, а приложение — работать с другим.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<table><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td><code>jQuery is not defined</code></td><td>Плагин выполняется до глобального объекта</td><td>Порядок Network и <code>typeof window.jQuery</code> перед импортом</td><td>Собрать entry с инициализатором или исправить порядок внешних scripts</td></tr><tr><td><code>$(...).inputmask is not a function</code></td><td>Плагин получил другой объект или не загрузился</td><td>Сравнить <code>window.jQuery === $</code> и проверить регистрацию <code>$.fn.inputmask</code></td><td>Оставить один источник jQuery и импортировать плагин после него</td></tr><tr><td>Работает только на одной странице</td><td>ProvidePlugin действует только в собранных модулях этого entry</td><td>Сравнить entry, HTML и содержимое chunk-файлов</td><td>Добавить зависимость в нужный entry, а не рассчитывать на соседний бандл</td></tr><tr><td>Размер растёт после добавления второго entry</td><td>Нет общего chunk или настроены две независимые поставки</td><td>Посмотреть stats и число включённых модулей jquery</td><td>Настроить общую доставку только после проверки поведения legacy-кода</td></tr><tr><td>CDN-версия ломает плагин</td><td>Версия или порядок внешней загрузки не совпадает с контрактом</td><td>Проверить фактический URL, версию и момент выполнения плагина</td><td>Зафиксировать совместимую версию или вернуть пакет в граф Webpack</td></tr></tbody></table>\n<h2>Порядок действий</h2>\n<ol><li>Найдите все обращения к <code>$</code>, <code>jQuery</code> и <code>window.jQuery</code>. Отдельно отметьте модули, которые выполняются сразу при импорте.</li><li>Для каждого entry выберите источник: пакет в графе Webpack или внешний глобальный script. Не оставляйте выбор на уровне случайного HTML-порядка.</li><li>Оставьте явный <code>import $ from 'jquery'</code> в новом коде. Добавьте <code>ProvidePlugin</code> только к переходному слою, который нельзя быстро изменить.</li><li>Если плагин читает <code>window.jQuery</code>, создайте один инициализатор и импортируйте его перед этим плагином.</li><li>Соберите каждый entry и проверьте, что в нём есть ожидаемая зависимость или явно описан <code>externals</code>.</li><li>Откройте реальную форму. Проверьте <code>window.jQuery</code>, <code>$.fn.jquery</code>, метод legacy-плагина и обработчик пользовательского события.</li><li>После миграции потребителя удалите лишнюю глобальную настройку и повторите проверку. Иначе временная совместимость станет постоянной зависимостью.</li></ol>\n<h2>Ограничения</h2>\n<p><code>ProvidePlugin</code> не превращает любой свободный идентификатор в безопасную архитектуру. Он скрывает зависимость в исходном файле и усложняет перенос модуля в другой сборщик. Используйте его как переходный слой и уменьшайте область действия.</p>\n<p>Один объект jQuery не гарантирует совместимость. Плагин может требовать конкретную версию, поддерживать только старый API или конфликтовать с <code>noConflict</code>. Проверяйте реальный сценарий, а не только успешную компиляцию.</p>\n<p>В серверном рендеринге и Web Worker нет обычного <code>window</code>. Код, который без проверки обращается к <code>window.jQuery</code>, должен выполняться только в браузерном entry. Это отдельное ограничение окружения, а не проблема Webpack.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Работу можно считать законченной, когда каждый entry имеет один документированный источник jQuery, сборка не содержит непреднамеренной второй копии, legacy-плагин получает тот же объект, что и приложение, а форма проходит реальный сценарий ввода. Дополнительно зафиксируйте проверку для страницы без legacy-кода: она не должна получать глобальную зависимость только потому, что она есть в соседнем бандле.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://webpack.js.org/plugins/provide-plugin/\" target=\"_blank\" rel=\"noopener\">Webpack: ProvidePlugin</a> — описывает автоматическую подстановку модулей и пример с <code>$</code>, <code>jQuery</code> и <code>window.jQuery</code>.</li><li><a href=\"https://webpack.js.org/configuration/externals/\" target=\"_blank\" rel=\"noopener\">Webpack: externals</a> — фиксирует контракт зависимостей, которые остаются вне сборки.</li><li><a href=\"https://api.jquery.com/jQuery/\" target=\"_blank\" rel=\"noopener\">jQuery API: jQuery()</a> — официальный справочник вызова jQuery и публичного объекта библиотеки.</li></ul>",
|
||
"contentHtml": "<p>После переноса старого фронтенда в Webpack кнопка с inputmask перестаёт работать. В консоли появляется <code>jQuery is not defined</code>, <code>$(...).inputmask is not a function</code> или плагин загружается только на странице с «правильным» порядком скриптов. Ошибка часто выглядит случайной: новый модуль импортирует jQuery, а legacy-файл ищет её в <code>window</code>. Цена ошибки — сломанная форма, дублированная библиотека в нескольких бандлах и релиз, который нельзя уверенно проверить одной сборкой.</p>\n<p>Тезис простой: сначала нужно назвать потребителя jQuery, затем выбрать один способ доставки для каждого entry. Явный <code>import</code> связывает зависимость с графом модулей. <code>ProvidePlugin</code> помогает старому коду, который обращается к свободным <code>$</code> или <code>jQuery</code>. Глобальный объект нужен только тем потребителям, которые действительно читают <code>window.jQuery</code>. CDN и <code>externals</code> образуют другой контракт: Webpack больше не кладёт библиотеку в бандл и ждёт готового глобального объекта снаружи.</p>\n<h2>Симптом начинается с границы видимости</h2>\n<p>Модуль с <code>import $ from 'jquery'</code> получает значение из графа Webpack. Это локальная переменная модуля. Она не обещает, что отдельный <code><script></code> в HTML увидит <code>window.$</code>. Обратное тоже верно: наличие <code>window.jQuery</code> не добавляет зависимость в граф и не делает её доступной каждому исходному файлу без настройки.</p>\n<p><code>ProvidePlugin</code> решает третий случай. Webpack замечает свободный идентификатор в модуле и автоматически подставляет импорт. Поэтому код с <code>$(selector)</code> может собраться без строки <code>import</code>. Это не запись переменной в глобальный объект браузера и не исправление порядка независимых тегов <code>script</code>.</p>\n<figure><img src=\"/assets/illustrations/jquery-webpack.svg\" alt=\"Схема связи jQuery, Webpack и legacy-плагина\" /><figcaption>Один entry должен доставить один объект jQuery раньше плагина, который его использует. Иллюстрация показывает границу между графом модулей и глобальным API браузера.</figcaption></figure>\n<h2>Три способа доставки</h2>\n<p>В проекте с несколькими entry не стоит начинать с единого глобального правила. Сначала составьте карту потребителей.</p>\n<table><thead><tr><th scope=\"col\">Способ</th><th scope=\"col\">Что получает код</th><th scope=\"col\">Когда подходит</th><th scope=\"col\">Чего он не делает</th></tr></thead><tbody><tr><td>Явный import</td><td>Локальный объект из графа Webpack</td><td>Новый код и код, который можно менять</td><td>Не создаёт <code>window.jQuery</code> сам по себе</td></tr><tr><td>ProvidePlugin</td><td>Автоматически подставленный импорт для свободного имени</td><td>Legacy-модули с <code>$</code> или <code>jQuery</code></td><td>Не чинит внешний script и не гарантирует порядок загрузки</td></tr><tr><td>externals или CDN</td><td>Глобальный объект, предоставленный HTML или платформой</td><td>Одна контролируемая внешняя загрузка</td><td>Не включает jQuery в бандл и не проверяет URL CDN</td></tr></tbody></table>\n<p>Для переходного проекта обычно работает связка: новый код импортирует jQuery явно, а ограниченный legacy-слой получает <code>ProvidePlugin</code>. Если плагин проверяет именно <code>window.jQuery</code>, добавьте глобальный экспорт в одном entry. Не смешивайте этот режим с CDN без причины. Иначе один бандл будет использовать пакет, а другой — внешний файл.</p>\n<h2>Минимальная конфигурация для переходного проекта</h2>\n<p>Ниже учебный пример. В нём два entry, один пакет <code>jquery</code> и legacy-код, который ещё использует свободное имя. Пример не измеряет размер бандла, не доказывает совместимость конкретного плагина и не заменяет проверку вашей версии Webpack и jQuery.</p>\n<pre><code>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};</code></pre>\n<p>В новом модуле зависимость остаётся видимой:</p>\n<pre><code>import $ from 'jquery';\n\nexport function mount(form) {\n return $(form).find('[data-mask]').length;\n}</code></pre>\n<p>В legacy-модуле строка импорта может отсутствовать:</p>\n<pre><code>$('.phone').inputmask('+7 (999) 999-99-99');</code></pre>\n<p>Оба фрагмента должны ссылаться на один экземпляр, если Webpack собирает их в общий runtime или правильно выносит общий модуль. Это нужно проверить в фактической конфигурации. Само совпадение версий в <code>package.json</code> такого доказательства не даёт.</p>\n<h2>Когда нужен window.jQuery</h2>\n<p>Некоторые старые плагины не экспортируют функцию как модуль. Они выполняются сразу и ищут <code>window.jQuery</code>. Для такого кода сделайте отдельный модуль-инициализатор и импортируйте его до плагина:</p>\n<pre><code>// 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';</code></pre>\n<p>Такой порядок относится к импортам внутри одного entry. Он не управляет отдельным CDN-скриптом, который HTML загрузит позже. В браузере проверьте именно <code>window.jQuery</code> перед и после подключения плагина, а затем вызовите метод плагина на реальном элементе формы.</p>\n<h2>Отрицательный путь: CDN и externals</h2>\n<p>Внешняя загрузка имеет смысл, когда платформа уже отдаёт jQuery или несколько приложений должны использовать один URL. Тогда Webpack не должен одновременно включать пакет в тот же бандл. Пример для глобального объекта:</p>\n<pre><code>module.exports = {\n externals: {\n jquery: 'jQuery'\n }\n};</code></pre>\n<p>Теперь <code>import $ from 'jquery'</code> в собранном коде означает обращение к внешнему <code>jQuery</code>. HTML обязан загрузить библиотеку раньше бандла:</p>\n<pre><code><script src=\"https://cdn.example.test/jquery.min.js\"></script>\n<script src=\"/assets/legacy.js\"></script></code></pre>\n<p>URL в примере учебный. Не подставляйте его в рабочую страницу. Для production нужны зафиксированная версия, контроль доступности, политика безопасности и понятный план отказа. Если CDN недоступен, Webpack не сможет компенсировать это настройкой <code>ProvidePlugin</code>. Если браузер загрузит две версии jQuery, плагины могут зарегистрироваться в одном объекте, а приложение — работать с другим.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<table><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td><code>jQuery is not defined</code></td><td>Плагин выполняется до глобального объекта</td><td>Порядок Network и <code>typeof window.jQuery</code> перед импортом</td><td>Собрать entry с инициализатором или исправить порядок внешних scripts</td></tr><tr><td><code>$(...).inputmask is not a function</code></td><td>Плагин получил другой объект или не загрузился</td><td>Сравнить <code>window.jQuery === $</code> и проверить регистрацию <code>$.fn.inputmask</code></td><td>Оставить один источник jQuery и импортировать плагин после него</td></tr><tr><td>Работает только на одной странице</td><td>ProvidePlugin действует только в собранных модулях этого entry</td><td>Сравнить entry, HTML и содержимое chunk-файлов</td><td>Добавить зависимость в нужный entry, а не рассчитывать на соседний бандл</td></tr><tr><td>Размер растёт после добавления второго entry</td><td>Нет общего chunk или настроены две независимые поставки</td><td>Посмотреть stats и число включённых модулей jquery</td><td>Настроить общую доставку только после проверки поведения legacy-кода</td></tr><tr><td>CDN-версия ломает плагин</td><td>Версия или порядок внешней загрузки не совпадает с контрактом</td><td>Проверить фактический URL, версию и момент выполнения плагина</td><td>Зафиксировать совместимую версию или вернуть пакет в граф Webpack</td></tr></tbody></table>\n<h2>Порядок действий</h2>\n<ol><li>Найдите все обращения к <code>$</code>, <code>jQuery</code> и <code>window.jQuery</code>. Отдельно отметьте модули, которые выполняются сразу при импорте.</li><li>Для каждого entry выберите источник: пакет в графе Webpack или внешний глобальный script. Не оставляйте выбор на уровне случайного HTML-порядка.</li><li>Оставьте явный <code>import $ from 'jquery'</code> в новом коде. Добавьте <code>ProvidePlugin</code> только к переходному слою, который нельзя быстро изменить.</li><li>Если плагин читает <code>window.jQuery</code>, создайте один инициализатор и импортируйте его перед этим плагином.</li><li>Соберите каждый entry и проверьте, что в нём есть ожидаемая зависимость или явно описан <code>externals</code>.</li><li>Откройте реальную форму. Проверьте <code>window.jQuery</code>, <code>$.fn.jquery</code>, метод legacy-плагина и обработчик пользовательского события.</li><li>После миграции потребителя удалите лишнюю глобальную настройку и повторите проверку. Иначе временная совместимость станет постоянной зависимостью.</li></ol>\n<h2>Ограничения</h2>\n<p><code>ProvidePlugin</code> не превращает любой свободный идентификатор в безопасную архитектуру. Он скрывает зависимость в исходном файле и усложняет перенос модуля в другой сборщик. Используйте его как переходный слой и уменьшайте область действия.</p>\n<p>Один объект jQuery не гарантирует совместимость. Плагин может требовать конкретную версию, поддерживать только старый API или конфликтовать с <code>noConflict</code>. Проверяйте реальный сценарий, а не только успешную компиляцию.</p>\n<p>В серверном рендеринге и Web Worker нет обычного <code>window</code>. Код, который без проверки обращается к <code>window.jQuery</code>, должен выполняться только в браузерном entry. Это отдельное ограничение окружения, а не проблема Webpack.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Работу можно считать законченной, когда каждый entry имеет один документированный источник jQuery, сборка не содержит непреднамеренной второй копии, legacy-плагин получает тот же объект, что и приложение, а форма проходит реальный сценарий ввода. Дополнительно зафиксируйте проверку для страницы без legacy-кода: она не должна получать глобальную зависимость только потому, что она есть в соседнем бандле.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://webpack.js.org/plugins/provide-plugin/\" target=\"_blank\" rel=\"noopener\">Webpack: ProvidePlugin</a> — описывает автоматическую подстановку модулей и пример с <code>$</code>, <code>jQuery</code> и <code>window.jQuery</code>.</li><li><a href=\"https://webpack.js.org/configuration/externals/\" target=\"_blank\" rel=\"noopener\">Webpack: externals</a> — фиксирует контракт зависимостей, которые остаются вне сборки.</li><li><a href=\"https://api.jquery.com/jQuery/\" target=\"_blank\" rel=\"noopener\">jQuery API: jQuery()</a> — официальный справочник вызова jQuery и публичного объекта библиотеки.</li></ul>",
|
||
"contentHtml": "<p>После переноса старого фронтенда в Webpack кнопка с inputmask перестаёт работать. В консоли появляется <code>jQuery is not defined</code>, <code>$(...).inputmask is not a function</code> или плагин загружается только на странице с «правильным» порядком скриптов. Ошибка часто выглядит случайной: новый модуль импортирует jQuery, а legacy-файл ищет её в <code>window</code>. Цена ошибки — сломанная форма, дублированная библиотека в нескольких бандлах и релиз, который нельзя уверенно проверить одной сборкой.</p>\n<p>Тезис простой: сначала нужно назвать потребителя jQuery, затем выбрать один способ доставки для каждого entry. Явный <code>import</code> связывает зависимость с графом модулей. <code>ProvidePlugin</code> помогает старому коду, который обращается к свободным <code>$</code> или <code>jQuery</code>. Глобальный объект нужен только тем потребителям, которые действительно читают <code>window.jQuery</code>. CDN и <code>externals</code> образуют другой контракт: Webpack больше не кладёт библиотеку в бандл и ждёт готового глобального объекта снаружи.</p>\n<h2>Симптом начинается с границы видимости</h2>\n<p>Модуль с <code>import $ from 'jquery'</code> получает значение из графа Webpack. Это локальная переменная модуля. Она не обещает, что отдельный <code><script></code> в HTML увидит <code>window.$</code>. Обратное тоже верно: наличие <code>window.jQuery</code> не добавляет зависимость в граф и не делает её доступной каждому исходному файлу без настройки.</p>\n<p><code>ProvidePlugin</code> решает третий случай. Webpack замечает свободный идентификатор в модуле и автоматически подставляет импорт. Поэтому код с <code>$(selector)</code> может собраться без строки <code>import</code>. Это не запись переменной в глобальный объект браузера и не исправление порядка независимых тегов <code>script</code>.</p>\n<figure><img src=\"/assets/illustrations/jquery-webpack.svg\" alt=\"Схема связи jQuery, Webpack и legacy-плагина\" /><figcaption>Один entry должен доставить один объект jQuery раньше плагина, который его использует. Иллюстрация показывает границу между графом модулей и глобальным API браузера.</figcaption></figure>\n<h2>Три способа доставки</h2>\n<p>В проекте с несколькими entry не стоит начинать с единого глобального правила. Сначала составьте карту потребителей.</p>\n<table><thead><tr><th scope=\"col\">Способ</th><th scope=\"col\">Что получает код</th><th scope=\"col\">Когда подходит</th><th scope=\"col\">Чего он не делает</th></tr></thead><tbody><tr><td>Явный import</td><td>Локальный объект из графа Webpack</td><td>Новый код и код, который можно менять</td><td>Не создаёт <code>window.jQuery</code> сам по себе</td></tr><tr><td>ProvidePlugin</td><td>Автоматически подставленный импорт для свободного имени</td><td>Legacy-модули с <code>$</code> или <code>jQuery</code></td><td>Не чинит внешний script и не гарантирует порядок загрузки</td></tr><tr><td>externals или CDN</td><td>Глобальный объект, предоставленный HTML или платформой</td><td>Одна контролируемая внешняя загрузка</td><td>Не включает jQuery в бандл и не проверяет URL CDN</td></tr></tbody></table>\n<p>Для переходного проекта обычно работает связка: новый код импортирует jQuery явно, а ограниченный legacy-слой получает <code>ProvidePlugin</code>. Если плагин проверяет именно <code>window.jQuery</code>, добавьте глобальный экспорт в одном entry. Не смешивайте этот режим с CDN без причины. Иначе один бандл будет использовать пакет, а другой — внешний файл.</p>\n<h2>Минимальная конфигурация для переходного проекта</h2>\n<p>Ниже учебный пример. В нём два entry, один пакет <code>jquery</code> и legacy-код, который ещё использует свободное имя. Пример не измеряет размер бандла, не доказывает совместимость конкретного плагина и не заменяет проверку вашей версии Webpack и jQuery.</p>\n<pre><code>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};</code></pre>\n<p>В новом модуле зависимость остаётся видимой:</p>\n<pre><code>import $ from 'jquery';\n\nexport function mount(form) {\n return $(form).find('[data-mask]').length;\n}</code></pre>\n<p>В legacy-модуле строка импорта может отсутствовать:</p>\n<pre><code>$('.phone').inputmask('+7 (999) 999-99-99');</code></pre>\n<p>Оба фрагмента должны ссылаться на один экземпляр, если Webpack собирает их в общий runtime или правильно выносит общий модуль. Это нужно проверить в фактической конфигурации. Само совпадение версий в <code>package.json</code> такого доказательства не даёт.</p>\n<h2>Когда нужен window.jQuery</h2>\n<p>Некоторые старые плагины не экспортируют функцию как модуль. Они выполняются сразу и ищут <code>window.jQuery</code>. Для такого кода сделайте отдельный модуль-инициализатор и импортируйте его до плагина:</p>\n<pre><code>// 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';</code></pre>\n<p>Такой порядок относится к импортам внутри одного entry. Он не управляет отдельным CDN-скриптом, который HTML загрузит позже. В браузере проверьте именно <code>window.jQuery</code> перед и после подключения плагина, а затем вызовите метод плагина на реальном элементе формы.</p>\n<h2>Отрицательный путь: CDN и externals</h2>\n<p>Внешняя загрузка имеет смысл, когда платформа уже отдаёт jQuery или несколько приложений должны использовать один URL. Тогда Webpack не должен одновременно включать пакет в тот же бандл. Пример для глобального объекта:</p>\n<pre><code>module.exports = {\n externals: {\n jquery: 'jQuery'\n }\n};</code></pre>\n<p>Теперь <code>import $ from 'jquery'</code> в собранном коде означает обращение к внешнему <code>jQuery</code>. HTML обязан загрузить библиотеку раньше бандла:</p>\n<pre><code><script src=\"https://cdn.example.test/jquery.min.js\"></script>\n<script src=\"/assets/legacy.js\"></script></code></pre>\n<p>URL в примере учебный. Не подставляйте его в рабочую страницу. Для production нужны зафиксированная версия, контроль доступности, политика безопасности и понятный план отказа. Если CDN недоступен, Webpack не сможет компенсировать это настройкой <code>ProvidePlugin</code>. Если браузер загрузит две версии jQuery, плагины могут зарегистрироваться в одном объекте, а приложение — работать с другим.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<table><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td><code>jQuery is not defined</code></td><td>Плагин выполняется до глобального объекта</td><td>Порядок Network и <code>typeof window.jQuery</code> перед импортом</td><td>Собрать entry с инициализатором или исправить порядок внешних scripts</td></tr><tr><td><code>$(...).inputmask is not a function</code></td><td>Плагин получил другой объект или не загрузился</td><td>Сравнить <code>window.jQuery === $</code> и проверить регистрацию <code>$.fn.inputmask</code></td><td>Оставить один источник jQuery и импортировать плагин после него</td></tr><tr><td>Работает только на одной странице</td><td>ProvidePlugin действует только в собранных модулях этого entry</td><td>Сравнить entry, HTML и содержимое chunk-файлов</td><td>Добавить зависимость в нужный entry, а не рассчитывать на соседний бандл</td></tr><tr><td>Размер растёт после добавления второго entry</td><td>Нет общего chunk или настроены две независимые поставки</td><td>Посмотреть stats и число включённых модулей jquery</td><td>Настроить общую доставку только после проверки поведения legacy-кода</td></tr><tr><td>CDN-версия ломает плагин</td><td>Версия или порядок внешней загрузки не совпадает с контрактом</td><td>Проверить фактический URL, версию и момент выполнения плагина</td><td>Зафиксировать совместимую версию или вернуть пакет в граф Webpack</td></tr></tbody></table>\n<h2>Порядок действий</h2>\n<ol><li>Найдите все обращения к <code>$</code>, <code>jQuery</code> и <code>window.jQuery</code>. Отдельно отметьте модули, которые выполняются сразу при импорте.</li><li>Для каждого entry выберите источник: пакет в графе Webpack или внешний глобальный script. Не оставляйте выбор на уровне случайного HTML-порядка.</li><li>Оставьте явный <code>import $ from 'jquery'</code> в новом коде. Добавьте <code>ProvidePlugin</code> только к переходному слою, который нельзя быстро изменить.</li><li>Если плагин читает <code>window.jQuery</code>, создайте один инициализатор и импортируйте его перед этим плагином.</li><li>Соберите каждый entry и проверьте, что в нём есть ожидаемая зависимость или явно описан <code>externals</code>.</li><li>Откройте реальную форму. Проверьте <code>window.jQuery</code>, <code>$.fn.jquery</code>, метод legacy-плагина и обработчик пользовательского события.</li><li>После миграции потребителя удалите лишнюю глобальную настройку и повторите проверку. Иначе временная совместимость станет постоянной зависимостью.</li></ol>\n<h2>Ограничения</h2>\n<p><code>ProvidePlugin</code> не превращает любой свободный идентификатор в безопасную архитектуру. Он скрывает зависимость в исходном файле и усложняет перенос модуля в другой сборщик. Используйте его как переходный слой и уменьшайте область действия.</p>\n<p>Один объект jQuery не гарантирует совместимость. Плагин может требовать конкретную версию, поддерживать только старый API или конфликтовать с <code>noConflict</code>. Проверяйте реальный сценарий, а не только успешную компиляцию.</p>\n<p>В серверном рендеринге и Web Worker нет обычного <code>window</code>. Код, который без проверки обращается к <code>window.jQuery</code>, должен выполняться только в браузерном entry. Это отдельное ограничение окружения, а не проблема Webpack.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Работу можно считать законченной, когда каждый entry имеет один документированный источник jQuery, сборка не содержит непреднамеренной второй копии, legacy-плагин получает тот же объект, что и приложение, а форма проходит реальный сценарий ввода. Дополнительно зафиксируйте проверку для страницы без legacy-кода: она не должна получать глобальную зависимость только потому, что она есть в соседнем бандле.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://webpack.js.org/plugins/provide-plugin/\" target=\"_blank\" rel=\"noopener\">Webpack: ProvidePlugin</a> — описывает автоматическую подстановку модулей и пример с <code>$</code>, <code>jQuery</code> и <code>window.jQuery</code>.</li><li><a href=\"https://webpack.js.org/configuration/externals/\" target=\"_blank\" rel=\"noopener\">Webpack: externals</a> — фиксирует контракт зависимостей, которые остаются вне сборки.</li><li><a href=\"https://api.jquery.com/jQuery/\" target=\"_blank\" rel=\"noopener\">jQuery API: jQuery()</a> — официальный справочник вызова jQuery и публичного объекта библиотеки.</li></ul>"
|
||
}
|