jQuery и чистый JavaScript: аналоги методов и когда jQuery ещё нужен
Опубликовано: 26.09.2026 · Обновлено: 26.09.2026 · Актуально для jQuery 3.7.1 и 4.0.0
Большинство задач, ради которых в 2010-х подключали jQuery, — выбор элементов, классы, события, Ajax-запросы, — сегодня решаются нативными методами браузера без единой внешней библиотеки. jQuery остаётся разумным выбором в легаси-проектах на плагинах, в CMS-темах (WordPress, Bitrix) и там, где на нём завязаны виджеты вроде DataTables или Select2. Ниже — таблицы соответствий методов jQuery и querySelector, classList, fetch и других API браузера, с рабочими примерами кода.
Нужен ли jQuery в 2026
В 2010 году jQuery решал реальную проблему: одни и те же операции с DOM в IE, Firefox и раннем Chrome приходилось писать по-разному, а нативных querySelectorAll, classList и fetch либо не было, либо они работали не везде одинаково. Сейчас актуальные браузеры поддерживают эти API напрямую, и часть задач, под которые раньше брали jQuery, закрывается одной строкой без библиотеки. Но это не значит, что jQuery нужно выпиливать из каждого проекта — решение зависит от ситуации, а не от принципа.
| Ситуация | Что делать |
| Legacy-сайт с готовыми jQuery-плагинами (слайдеры, галереи, datepicker), которые работают и не требуют доработки | Оставить как есть. Переписывать рабочий код на чистый JS ради самого факта — трата времени без пользы для пользователя |
| Тема WordPress или Bitrix, где jQuery подключается самой платформой или ядром темы | Оставить. jQuery — часть инфраструктуры площадки, убрать его из одной темы обычно нельзя, не сломав чужие плагины |
| Админка на jQuery UI, DataTables, Select2 без прямых аналогов той же зрелости под чистый JS или нужный фреймворк | Оставить. Миграция ради идеологии обойдётся дороже, чем выгода от отказа от одной библиотеки |
| Новый проект на React, Vue или другом фреймворке с виртуальным DOM | jQuery не нужен и обычно вреден — см. вопрос про совместимость с React/Vue в конце статьи |
| Небольшой лендинг или промо-страница с парой интерактивных блоков (бургер-меню, табы, простая форма) | Чистый JS. Тянуть библиотеку ради toggleClass и обработчика клика — плохой размен веса страницы на удобство |
| Много кода уже написано на jQuery, но проект живой и будет развиваться дальше | Постепенный переход: новые модули — без jQuery, старые трогать по мере необходимости (см. раздел «Как переписывать постепенно») |
Точную долю сайтов на jQuery публикует W3Techs — конкретный процент здесь не приводим, он быстро устаревает; смотрите актуальный отчёт на их сайте, если это важно для решения.
Готовность DOM
Классический код на jQuery почти всегда начинается с обёртки, которая откладывает выполнение до готовности документа:
// jQuery
$(document).ready(function () {
// код, которому нужен готовый DOM
});
// или сокращённая форма — то же самое
$(function () {
// код
});
У нативного эквивалента то же назначение — сработать, когда HTML разобран (но не обязательно догружены картинки и стили):
// Чистый JS
document.addEventListener("DOMContentLoaded", function () {
// код, которому нужен готовый DOM
});
Разница, которую стоит знать: если DOMContentLoaded уже случился к моменту подписки (скрипт подключили асинхронно и он выполнился с опозданием), обработчик не сработает никогда — событие не повторяется, в отличие от $(fn), который выполнит колбэк сразу же, если DOM уже готов.
Часто обёртка вообще не нужна: если подключить скрипт с атрибутом defer или как <script type="module">, браузер и так выполнит его после разбора DOM, но до события DOMContentLoaded — то есть достаточно просто писать код на верхнем уровне файла:
<script src="/assets/main.js" defer></script>
// main.js — сработает после разбора DOM, обёртка не нужна
const button = document.querySelector(".menu-toggle");
button.addEventListener("click", () => { /* ... */ });
Выбор элементов
Базовая механика выбора элементов в jQuery и в нативном DOM похожа — оба принимают CSS-селектор, — но результат устроен по-разному. jQuery всегда возвращает jQuery-объект (даже пустой, если ничего не найдено), с которым можно спокойно продолжать вызывать методы — они просто ничего не сделают. Нативный querySelector при отсутствии совпадения возвращает null, и следующее же обращение к свойству бросит ошибку, если не подстраховаться.
| jQuery | Нативный JavaScript |
$("#id") | document.getElementById("id") или document.querySelector("#id") |
$(".card") | document.querySelectorAll(".card") |
$("a[href^=http]") | document.querySelectorAll('a[href^="http"]') |
$el.find(".title") | el.querySelectorAll(".title") |
$el.closest(".card") | el.closest(".card") |
$el.parent() | el.parentElement |
$el.children() | el.children |
$el.eq(2) | elements[2] (NodeList поддерживает индексацию) |
$el.first() | document.querySelector(sel) сам вернёт первое совпадение |
$el.filter(fn) | Array.from(elements).filter(fn) |
Метод .closest() у jQuery и нативный Element.closest() работают одинаково по смыслу: проверяют сам элемент, затем поднимаются по предкам до первого совпадения с селектором. Разница в том, что jQuery делает это для каждого элемента набора и возвращает набор без дублей, а нативный метод — для одного элемента и возвращает элемент или null. Подробное описание есть на странице .closest().
NodeList, который возвращает querySelectorAll, — не массив. У него есть forEach (во всех актуальных браузерах), но нет map, filter и find — для них оберните результат в Array.from(...) или разверните спредом: [...document.querySelectorAll(".card")].
И главное отличие в поведении «на отсутствие элемента»: jQuery молча ничего не делает, если селектор ничего не нашёл, — цепочку из .addClass(), .text() и любых других методов на пустом jQuery-объекте можно вызывать без страха. Нативный код так не работает:
// jQuery — безопасно, даже если .banner нет на странице
$(".banner").addClass("is-visible").text("Привет");
// Нативный JS — упадёт с TypeError, если элемента нет
document.querySelector(".banner").classList.add("is-visible"); // Cannot read properties of null
// Безопасный вариант — опциональная цепочка
document.querySelector(".banner")?.classList.add("is-visible");
Классы, атрибуты и стили
Работа с классами переносится почти буквально — у нативного classList практически один в один те же операции, что у jQuery:
| jQuery | Нативный JavaScript |
$el.addClass("active") | el.classList.add("active") |
$el.removeClass("active") | el.classList.remove("active") |
$el.toggleClass("active") | el.classList.toggle("active") |
$el.hasClass("active") | el.classList.contains("active") |
Подробности поведения — на странице .addClass(). Обратите внимание: у jQuery toggleClass("active", false) позволяет явно задать состояние вторым булевым аргументом — у classList.toggle он тоже есть (el.classList.toggle("active", false)), так что и здесь замена прямая.
С атрибутами и данными разница чуть тоньше — jQuery смешивает в одном методе разные вещи, которые в нативном DOM разведены:
| jQuery | Нативный JavaScript |
$el.attr("href") / $el.attr("href", "/x") | el.getAttribute("href") / el.setAttribute("href", "/x") |
$el.prop("checked") / $el.prop("checked", true) | el.checked / el.checked = true (свойство DOM-объекта, не атрибут разметки) |
$el.data("userId") | el.dataset.userId (для data-user-id) |
Про .attr() vs .prop() подробно — на странице .attr(); общее правило: атрибут — это то, что написано в HTML-разметке, свойство — текущее состояние живого DOM-объекта, и для чекбоксов, значений полей ввода и selected у option обычно нужно именно свойство.
С .data() нативная замена не полностью эквивалентна, и об этом стоит знать заранее (детали — на странице .data()):
- jQuery
.data() при чтении автоматически пытается распарсить значение атрибута как число, булево значение или JSON — data-count="3" вернётся числом 3, а не строкой. dataset всегда отдаёт строку, парсить JSON.parse() нужно самостоятельно.
- jQuery хранит данные из
.data("key", value) во внутреннем JS-объекте, а не в разметке — так можно привязать к элементу произвольный объект, и это не отразится ни в одном data-* атрибуте. dataset — двусторонняя связь со строковыми атрибутами: записать можно только строку, и она реально появится в HTML.
Стили и размеры — последняя пара в этом разделе:
| jQuery | Нативный JavaScript |
$el.css("color", "red") | el.style.color = "red" |
$el.css("background-color") (итоговое, вычисленное значение) | getComputedStyle(el).backgroundColor |
$el.width() / .height() (ширина/высота содержимого, без padding и border) | el.getBoundingClientRect().width — с border; для содержимого без border ближе el.clientWidth минус padding |
Точное совпадение чисел зависит от box-sizing и от того, что нужно — содержимое, содержимое с отступами или полный размер с рамкой. Если счёт идёт на пиксели, сверяйте свойство по описанию getBoundingClientRect на MDN, а не полагайтесь на совпадение jQuery-метода и нативного свойства.
Манипуляции с DOM
Вставка, замена и удаление узлов — там, где jQuery исторически выигрывал в лаконичности, потому что старый DOM API требовал создавать узлы через document.createElement и вставлять их по одному. Современный ChildNode/ParentNode API это исправил: методы вроде append() и before() принимают сразу несколько аргументов, и это могут быть как узлы, так и строки.
| jQuery | Нативный JavaScript |
$el.html("<b>текст</b>") | el.innerHTML = "<b>текст</b>" |
$el.text("текст") | el.textContent = "текст" |
$el.append(child) | el.append(child) — принимает несколько узлов и строк сразу |
$el.prepend(child) | el.prepend(child) |
$el.before(node) | el.before(node) |
$el.after(node) | el.after(node) |
$el.remove() | el.remove() |
$el.empty() | el.replaceChildren() (без аргументов — просто очищает узел) |
$el.clone() | el.cloneNode(true) (без обработчиков и данных — см. ниже) |
По .clone() нюанс: без аргументов jQuery копирует только разметку (глубоко, со всеми потомками), а форма .clone(true) дополнительно копирует обработчики событий и данные .data(), привязанные через jQuery. Нативный cloneNode() не копирует ни обработчики, ни JS-данные ни в каком варианте — навешивайте их заново после клонирования. И учтите, что $el.clone() всегда глубокий, а нативному методу нужно явно передать true.
replaceChildren() удобен и для замены содержимого целиком: el.replaceChildren(newNode1, newNode2) одной командой убирает старых детей и вставляет новых, без промежуточного innerHTML = "".
Для точечной вставки HTML-строки в конкретное место, без создания промежуточных узлов и без замены всего содержимого, есть insertAdjacentHTML — аналога с таким же названием у jQuery нет, но по сути он делает то же, что связка .before()/.after()/.append()/.prepend() со строкой:
// Четыре позиции вставки
el.insertAdjacentHTML("beforebegin", html); // перед элементом
el.insertAdjacentHTML("afterbegin", html); // в начало, первым потомком
el.insertAdjacentHTML("beforeend", html); // в конец, последним потомком
el.insertAdjacentHTML("afterend", html); // после элемента
И $el.html(userInput), и el.innerHTML = userInput одинаково опасны с непроверенным пользовательским текстом: браузер выполнит вложенный <script> или onerror в <img>. Библиотека здесь ни при чём — для текста используйте .text()/textContent, а для HTML от пользователя нужна отдельная санитизация (например, DOMPurify) до вставки.
События
Подписка на события — область, где нативный API почти полностью повторяет то, что умеет jQuery, кроме одной вещи — пространств имён событий, аналога которым в браузере просто нет.
| jQuery | Нативный JavaScript |
$el.on("click", handler) | el.addEventListener("click", handler) |
$el.off("click", handler) | el.removeEventListener("click", handler) — нужна та же ссылка на функцию, что при подписке |
$el.one("click", handler) | el.addEventListener("click", handler, { once: true }) |
$el.trigger("custom", data) | el.dispatchEvent(new CustomEvent("custom", { detail: data, bubbles: true })) |
Делегирование — второй частый паттерн, когда обработчик вешают на общего предка, а не на каждый дочерний элемент по отдельности (например, чтобы работать с элементами списка, которые ещё будут добавлены):
// jQuery
$("#list").on("click", ".item", function (event) {
console.log("клик по", this);
});
// Чистый JS
document.querySelector("#list").addEventListener("click", (event) => {
const item = event.target.closest(".item");
if (item) {
console.log("клик по", item);
}
});
Снятие сразу нескольких обработчиков — там, где в jQuery помогали пространства имён (click.myPlugin), — в нативном API решается через общий AbortController: один вызов abort() отписывает сразу все обработчики, которым передали его signal:
const controller = new AbortController();
el.addEventListener("click", onClick, { signal: controller.signal });
el.addEventListener("mouseenter", onHover, { signal: controller.signal });
window.addEventListener("resize", onResize, { signal: controller.signal });
// Один вызов снимает все три обработчика сразу —
// аналог $(el).off(".myPlugin") без строковых пространств имён
controller.abort();
Полного эквивалента строковым пространствам имён (click.myPlugin) в браузере нет: jQuery группирует обработчики по строке, нативный код — по общему AbortSignal, заведённому заранее для конкретной фичи. Подробнее про делегирование — в статье события и делегирование в jQuery.
Ajax
Здесь разница не только в синтаксисе, но и в поведении на ошибках — это единственное место в статье, где нативный API ведёт себя не так, как многие ожидают, глядя на jQuery.
| jQuery | Нативный JavaScript |
$.get(url) | fetch(url) |
$.getJSON(url) | fetch(url).then(r => r.json()) |
$.post(url, data) | fetch(url, { method: "POST", body: ... }) |
$.ajax({ ..., timeout: 5000 }) | fetch(url, { signal: AbortSignal.timeout(5000) }) |
Ключевое отличие. $.ajax() сама вызывает error/.fail() на HTTP-ошибке — 404, 500 и так далее. fetch() так не делает: промис резолвится для любого дошедшего до браузера ответа независимо от статуса и реджектится только при сетевой ошибке. Проверять response.ok (true для статусов 200–299) и вручную бросать исключение — шаг, который легко забыть при переходе с jQuery.
Пример «было / стало» с обработкой ошибок в обоих вариантах:
// jQuery — .fail() сработает и на сетевой ошибке, и на HTTP 404/500
$.ajax({
url: "/api/orders",
method: "GET",
dataType: "json"
})
.done(function (orders) {
renderOrders(orders);
})
.fail(function (jqXHR) {
showError("Не удалось загрузить заказы: " + jqXHR.status);
});
// Чистый JS — response.ok нужно проверить самому
async function loadOrders() {
try {
const response = await fetch("/api/orders");
if (!response.ok) {
throw new Error("Ошибка сервера: " + response.status);
}
const orders = await response.json();
renderOrders(orders);
} catch (error) {
showError("Не удалось загрузить заказы: " + error.message);
}
}
Отправка данных формы, включая файлы, — раньше это была одна из причин держать jQuery, а сейчас FormData и fetch справляются без него:
// jQuery
const formData = new FormData(document.querySelector("#upload-form"));
$.ajax({
url: "/api/upload",
method: "POST",
data: formData,
processData: false, // не превращать FormData в query-строку
contentType: false // не задавать Content-Type — его выставит браузер сам, с boundary
}).done(() => showSuccess());
// Чистый JS
const formData = new FormData(document.querySelector("#upload-form"));
const response = await fetch("/api/upload", {
method: "POST",
body: formData // Content-Type с нужным boundary браузер выставит сам
});
if (response.ok) showSuccess();
Для JSON-тела запроса заголовок задают явно, а тело сериализуют вручную:
const response = await fetch("/api/orders", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ productId: 42, qty: 1 })
});
Таймаут запроса — заменяется AbortSignal.timeout(ms): он создаёт сигнал, который сам отменит запрос через указанное время, если ответ не пришёл, и запрос завершится ошибкой TimeoutError:
const response = await fetch("/api/orders", {
signal: AbortSignal.timeout(5000) // отмена через 5 секунд
});
И jqXHR у jQuery, и объект fetch — промисы, к которым можно привязать несколько обработчиков и использовать async/await. Все опции классического подхода — на странице jQuery.ajax().
Анимация
Анимации — единственный раздел, где нативная замена не однострочная: у jQuery .animate(), .fadeIn() и .slideUp() сами считают промежуточные кадры и сами регулируют высоту/прозрачность, а в браузере эту работу делят между собой CSS и Web Animations API.
Для простого появления/исчезновения проще всего CSS-переход и переключение класса:
/* CSS */
.box {
opacity: 0;
transition: opacity 0.3s ease;
}
.box.is-visible {
opacity: 1;
}
// jQuery
$(".box").fadeIn(300);
// Чистый JS — переход делает CSS, JS только переключает класс
document.querySelector(".box").classList.add("is-visible");
Если анимация нужна из чистого JS без заранее прописанного CSS-перехода — например, значения кадров вычисляются на лету, — ближе всего к .animate() по смыслу (и тоже возвращает объект, за завершением которого можно проследить) Element.animate() из Web Animations API:
// jQuery
$(".box").animate({ opacity: 0, marginLeft: "40px" }, 300);
// Чистый JS
const animation = el.animate(
[
{ opacity: 1, marginLeft: "0px" },
{ opacity: 0, marginLeft: "40px" }
],
{ duration: 300, fill: "forwards", easing: "ease" }
);
// Как и .promise() у jQuery, .finished — это промис
animation.finished.then(() => el.remove());
.slideUp()/.slideDown() заменяются сложнее — CSS не умеет анимировать переход к height: auto напрямую. Варианты: измерить целевую высоту через scrollHeight и анимировать до конкретного пикселя, использовать CSS-трюк с grid-template-rows: 0fr → 1fr на обёртке, либо, если сама анимация сворачивания не принципиальна, обойтись атрибутом hidden или display: none без перехода.
Утилиты: each, map, extend и другие
У jQuery есть набор вспомогательных функций общего назначения, не привязанных к DOM, — часть из них с приходом ES6 стала не нужна, а часть в jQuery 4.0 попросту убрали из библиотеки.
| jQuery | Нативный JavaScript |
$.each(array, fn) | array.forEach(fn) (для объектов — Object.entries(obj).forEach(...)) |
$.map(array, fn) | array.map(fn) (нюанс — см. ниже) |
$.grep(array, fn) | array.filter(fn) |
$.inArray(value, array) | array.includes(value) или array.indexOf(value) !== -1 |
$.extend({}, a, b) (поверхностное слияние) | Object.assign({}, a, b) или { ...a, ...b } |
$.extend(true, {}, a, b) (глубокое слияние) | прямого аналога нет — см. оговорку ниже |
jQuery.each() как метод — на странице jQuery.each(), версия для элементов — .each() | — |
Метод $.map() и Array.prototype.map() — не полностью одно и то же: $.map() выбрасывает из результата null/undefined и «расплющивает» результаты-массивы на один уровень, а нативный .map() сохраняет длину и не разворачивает вложенные массивы. Для разворачивания есть flatMap(), но null/undefined всё равно фильтруются отдельно (.filter(Boolean)).
С $.extend(true, ...) честная оговорка: он рекурсивно сливает вложенные объекты из нескольких источников в один, и прямого нативного аналога такому слиянию нет. Похожая по названию structuredClone() решает другую задачу — делает глубокую копию одного значения, а не сливает несколько, и бросает исключение на функциях, DOM-узлах и других несериализуемых значениях, которые $.extend скопировал бы по ссылке. Для глубокого слияния всё ещё нужна своя рекурсивная функция или сторонняя утилита. Подробнее — на страницах jQuery.extend(), jQuery.grep() и jQuery.inArray().
Важно для апгрейда на jQuery 4.0. jQuery.isArray(), jQuery.trim() и jQuery.parseJSON() были deprecated ещё в 3.x, а в 4.0 полностью удалены — если в коде остались такие вызовы, после обновления они упадут с «is not a function». Замены: Array.isArray(value), value.trim(), JSON.parse(value). jQuery Migrate для веток 3.x предупреждает об их использовании заранее — см. руководство по обновлению до jQuery 4.0.
Как переписывать постепенно
Переписывать рабочий сайт с jQuery на чистый JS одним рывком — плохая идея почти всегда: риск сломать что-то в проверенном коде обычно выше, чем выгода от отказа от библиотеки. Практичнее двигаться постепенно.
- Новый функционал пишите без jQuery сразу, а не «допишу как есть, потом перепишу» — второго захода обычно не случается.
- Не трогайте работающие jQuery-плагины (готовые слайдеры, датапикеры, интеграции вроде Select2) только ради принципа — переписывайте их, когда они реально мешают: конфликтуют с новым кодом, тормозят или требуют доработки, которую проще сделать нативно.
- Заведите пару маленьких вспомогательных функций-обёрток вместо частичной jQuery — они закрывают 80% типичных вызовов и делают разницу в коде менее болезненной для тех, кто привык к
$().
- Переходите постранично или по модулям, а не по всему сайту сразу — так проще откатить один раздел, если что-то пошло не так, и легче протестировать изменения вручную перед выкладкой.
Небольшой набор хелперов, который закрывает самые частые вызовы — выбор элементов, коллекцию как массив и подписку на события с делегированием:
// helpers.js — маленькая обвязка для новых модулей без jQuery
export const qs = (sel, ctx = document) => ctx.querySelector(sel);
export const qsa = (sel, ctx = document) => [...ctx.querySelectorAll(sel)];
export function on(el, event, selectorOrHandler, maybeHandler) {
if (typeof selectorOrHandler === "function") {
el.addEventListener(event, selectorOrHandler);
return;
}
// делегирование: on(list, "click", ".item", handler)
el.addEventListener(event, (e) => {
const target = e.target.closest(selectorOrHandler);
if (target && el.contains(target)) maybeHandler.call(target, e);
});
}
// Использование — синтаксис похож на jQuery, зависимостей нет
import { qs, qsa, on } from "./helpers.js";
on(qs("#list"), "click", ".item", function (event) {
this.classList.toggle("is-active");
});
Итог и чек-лист
Для большинства новых интерфейсов нативных API браузера достаточно: выбор элементов через querySelector(All), классы через classList, события через addEventListener, запросы через fetch. Держать jQuery ради них отдельно уже не нужно.
- Легаси-проект с работающими jQuery-плагинами — не трогайте без причины.
- Тема CMS (WordPress, Bitrix), где jQuery — часть платформы, — оставьте как есть.
- Новый проект на React/Vue или другом фреймворке с виртуальным DOM — обходитесь без jQuery.
- Небольшая страница с парой интерактивных блоков — пишите на нативном JS, библиотека не окупится.
- При работе с формами и файлами используйте
FormData + fetch вместо $.ajax и не забывайте проверять response.ok.
- При обновлении существующего кода с 3.x на jQuery 4.0 сначала прогоните проект через jQuery Migrate — он укажет на удалённые методы вроде
$.trim() и $.parseJSON() до того, как они начнут падать в проде.
- Если решаете подключить jQuery 4.0 в новом месте — сверьтесь со статьёй как подключить jQuery и с руководством по обновлению на официальном сайте.
Частые вопросы
Нужно ли учить jQuery в 2026 году?
Если вы поддерживаете чужой код, работаете с WordPress/Bitrix-темами или legacy-админками — да, это практический навык: огромное количество работающих сайтов и плагинов написано на jQuery, и его придётся читать и чинить независимо от того, на чём вы пишете новые проекты. Для нового проекта «с нуля» без таких ограничений специально изучать jQuery ради самого факта уже не обязательно — полезнее один раз разобраться с querySelector, fetch и остальными нативными API из этой статьи.
Что быстрее — jQuery или нативный JavaScript?
Без конкретных цифр бенчмарков: jQuery в итоге вызывает те же нативные методы браузера через слой абстракции, поэтому прямой вызов API обычно не медленнее, а на массовых DOM-операциях в цикле — быстрее. Для типичной вёрстки разница не заметна на глаз. Если производительность конкретной страницы критична — измеряйте её через вкладку Performance в DevTools на реальном коде, а не по чужим синтетическим тестам.
Чем заменить $(document).ready()?
Событием DOMContentLoaded на document, либо вообще без обработчика — если подключить скрипт с атрибутом defer или как type="module", браузер и так выполнит его после разбора DOM. Подробнее — в разделе «Готовность DOM» ниже.
Чем заменить $.ajax()?
Функцией fetch() в связке с async/await. Главное отличие: fetch, в отличие от $.ajax, не бросает ошибку сама на HTTP 404/500 — нужна ручная проверка response.ok. Таймаут задаётся через AbortSignal.timeout() вместо опции timeout. Разбор с примерами — в разделе «Ajax» ниже.
Можно ли использовать jQuery вместе с React или Vue?
Технически да — через jQuery.noConflict(). Но обычно не стоит: React и Vue управляют содержимым компонентов через виртуальный DOM, а jQuery меняет реальные узлы напрямую — если оба слоя трогают один элемент, один незаметно перезатирает изменения другого, и такие баги трудно отследить. Если jQuery нужен только ради одного плагина без аналога под React/Vue, изолируйте его в отдельном DOM-поддереве, которым фреймворк не управляет.
Какой размер у jQuery?
Минифицированный файл полной сборки jQuery 4.0.0 весит около 77 КБ (78 748 байт по данным npm-пакета), slim-сборки без Ajax и эффектов — около 55 КБ; при передаче со сжатием gzip или brotli объём в несколько раз меньше. Для сравнения, jquery-3.7.1.min.js — около 85 КБ. Немного по меркам крупных фреймворков, но заметно, когда вся остальная логика страницы умещается в паре килобайт нативного кода без единой зависимости.
[ наверх ]