Bridge: админка ProcessWire без jQuery — и все старые модули на месте

Мы сделали для ProcessWire новую админку на нативных веб-компонентах. jQuery в ней не грузится вовсе, пока на экране не появится модуль, которому он нужен: тогда старый стек подгружается для этого экрана, и модуль работает как раньше. Lighthouse на рабочих экранах — 98 против 69 у стандартной темы.

Админка ProcessWire собрана из полей, а поля — из модулей, которые годами писались под jQuery, jQuery UI и inputfields.js. Поэтому стандартная тема грузит этот стек на каждом экране, даже там, где он никому не нужен. Просто убрать его нельзя: сломается всё, что написано не тобой.

Bridge — обычная тема админки, AdminThemeBridge, которая решает это иначе. Рамку и основные экраны она рисует сама, на веб-компонентах и небольшом рантайме на TypeScript, а старый стек грузит только там, где он действительно нужен. Код открыт: github.com/theoretic/bridge, модуль для установки — github.com/theoretic/AdminThemeBridge. Вопросы и отчёты об ошибках — в теме на форуме ProcessWire.

Мост, а не замена

Название говорит, что это такое: мост между админкой эпохи jQuery и современным вебом. Всё, что можно сделать нативно, уже нативно. Чужие модули остаются на своём берегу и работают так же, как работали.

Нативные экраны. Большинство полей ядра — текст, списки, чекбоксы, даты, ссылки на страницы, файлы, картинки с кадрированием, повторители, языковые вкладки — и экраны ядра: дерево страниц, редактор, Lister, модули, поля, шаблоны, пользователи, логи. Редактор сохраняет страницу без перезагрузки; Shift+клик по кнопке сохраняет по-старому.

Старый стек по требованию. Если на экране есть поле, процесс или скрипт сайта, написанный под jQuery, Bridge подгружает jQuery, jQuery UI, inputfields.js, WireTabs и остальное — в том же порядке, с теми же глобальными переменными и событиями, что и ядро. Сервер узнаёт об этом, когда рисует поле; браузер подстраховывает, если поле пришло позже, по AJAX. Свёрнутое поле тянет стек, только когда его открывают.

Ядро не трогаем. Папка wire/ остаётся нетронутой копией оригинала. Id и классы в DOM, AJAX-адреса, имена классов модулей и хукаемые методы — те же, что в ядре, поэтому модули и хуки находят то, что ищут. Обновление ProcessWire — по-прежнему замена wire/.

Редактор страницы

Редактор страницы в светлой схеме. Цвета — пресеты AdminThemeBoss Ноэля Боссарта: эта тема во многом и вдохновила Bridge, от неё же расширенные хлебные крошки.
Редактор страницы в светлой схеме. Цвета — пресеты AdminThemeBoss Ноэля Боссарта: эта тема во многом и вдохновила Bridge, от неё же расширенные хлебные крошки.

Сколько это весит

98
было 69
Lighthouse, производительность
Среднее по четырём экранам: дерево страниц, редактор, Lister, модули
A
38КБ
было 272 КБ
JavaScript на экран
После gzip. jQuery на этих экранах не грузится вовсе
A
55КБ
было 118 КБ
CSS на экран
После gzip, светлая и тёмная схемы в одном наборе
A
71КБ
было 237 КБ
Шрифты
Иконки Font Awesome нарезаны маленькими наборами вместо полного файла в ~300 КБ
A

Честно о цифрах

Lighthouse 12, десктопный профиль, под залогиненным пользователем, с холодным кэшем, медиана трёх прогонов. Все темы стояли на одной и той же копии реального сайта; «было» — стандартная тема Uikit. AdminThemeBoss, на котором этот сайт жил до Bridge, набирает 81. Нагляднее всего разница во времени до отрисовки основного содержимого (LCP): 0,9 секунды против 3,6.

Мерили на локальной машине. На настоящем сервере абсолютные цифры будут выше — смысл в разнице между темами. И это экраны без старых полей: экран, где есть поле эпохи jQuery, загрузит стек для этого поля, как и должен. Bridge не делает старые модули легче — он перестаёт брать за них плату там, где их нет.

Совместимость проверяется, а не обещается

«Все старые модули работают» — громкое обещание, поэтому его проверяет отдельный набор тестов. Он открывает каждый экран админки дважды, под Uikit и под Bridge, и сравнивает то, что видят модули.

На копиях девяти наших реальных сайтов это 1 434 экрана, и ни один не отличается из-за темы. Под проверку попал 41 сторонний модуль из 45, установленных на этих сайтах; у оставшихся четырёх экраны админки падают и под Uikit — на копиях они не работают сами по себе. Поведение интерфейса отдельно проверяется в Chromium и Firefox, и любая ошибка на странице или в консоли валит проверку.

Чего не проверяли: Pro-модули — ProFields, FormBuilder, ListerPro. Ни на одном из наших сайтов их нет.

Старые редакторы в тёмной схеме

CKEditor — поле эпохи jQuery, как и TinyMCE. На таком экране Bridge подгружает для них старый стек, а тёмная схема дотягивается и до самих редакторов.
CKEditor — поле эпохи jQuery, как и TinyMCE. На таком экране Bridge подгружает для них старый стек, а тёмная схема дотягивается и до самих редакторов.

Первая боевая установка — этот сайт

17 октября 2026 года Bridge встал на atis.pro, и админка этого сайта работает на нём до сих пор. Установка пошла не так тремя разными способами — и ни в одном из них Bridge не был виноват напрямую.

Память. Установка упала на нехватке памяти. Виноватым оказался другой наш модуль, StaticPages 0.6.3: его файл настроек обращался к самому модулю, а настройки читаются при каждой пересборке кэша модулей — так пересборка запускала саму себя. Исправлено в 0.6.4.

Тема без настройки. Падение пришлось на момент, когда модуль уже был записан в базу, а его сведения в кэш — ещё нет. Без них ProcessWire загрузил тему слишком рано, когда ещё не было известно, какая страница открыта, и админка показалась в Times New Roman и без меню. Кнопка обновления модулей не помогала: в 3.0.274 она ничего не делает, пока не изменится какой-нибудь файл модуля.

Чужая настройка. У AdminThemeBoss была включена «активировать для всех пользователей», и на каждом запросе она возвращала всем тему Uikit.

На следующий день, на testfly.me, та же настройка Boss сохранила пользователя ещё на старте, и это уронило хук самого сайта. Теперь тема знает об этом сама: с 0.8.1 она замечает пропавший кэш и пересобирает его, с 0.8.2 не даёт себя выбрать, пока у Boss включена эта настройка. Остальное собрано в чек-лист установки на живой сайт.

Первые отзывы

19 октября, через пять дней после того, как был написан план, Bridge вышел публичной бетой 0.8.2 с анонсом на форуме ProcessWire. Первые сообщения о проблемах пришли в первые же сутки — в теме и в личку. Каждое мы воспроизводили на чистой установке с тем самым модулем, чинили и закрывали тестом.

Самое интересное прислал автор ProcessShowcase: его модуль под Bridge выглядел сломанным. Модуль пользуется UIkit напрямую — сетками, карточками, UIkit.notification(), — потому что стандартная тема грузит UIkit на каждой странице. Bridge его не грузил.

Решение в 0.9.5 устроено так же, как мост для jQuery: слой UIkit подгружается только там, где нужен, — если в разметке есть UIkit, если скрипт модуля обращается к UIkit или модуль сам добавил его файлы. Это таблица стилей UIkit без базовых правил, перекрашенная в цвета темы для обеих схем (32 КБ после gzip), в отдельном каскадном слое, так что перекрасить саму админку она не может. Экраны ядра её не грузят никогда. А вызов UIkit.modal.confirm(), сделанный раньше, чем слой загрузился, не теряется: слой догружается и выполняет его.

Для авторов модулей

// run your Inputfield natively, without the legacy stack:
// implement BridgeComponent, or add the class to the theme's map
$wire->addHookAfter('AdminThemeBridge::nativeMap', function(HookEvent $e) {
	$map = $e->return;
	$map['InputfieldMyMarkup'] = array('kind' => 'wrapper');
	$e->return = $map;
});

// a module built on UIkit asks for the layer itself (0.9.5)
if(method_exists($adminTheme, 'needUikit')) $adminTheme->needUikit('MyModule');

Если ничего из этого не делать, ничего и не меняется: модуль получает старый стек, как сегодня. Нативным можно объявить только то, чему не нужны ни jQuery, ни inputfields.js. Интерфейс BridgeComponent объявляется и без установленной темы. Подробности — в контракте компонентов, docs/contract.md в репозитории.