Кастомизация Vue-компонента - мутация, клон, порядок загрузки
Правим чужой Vue-компонент, не трогая его исходники: мутацией на месте или клоном рядом. Правка файлов ядра при первом же обновлении продукта откатывается, а мутация переживает обновление.
Решение
Тип компонента
Смотрим, как объявлен исходный компонент:
import { BitrixVue } from 'ui.vue3';
// мутабельный: объявлен методом обёртки и дальше доступен по имениconst Widget = BitrixVue.mutableComponent('local-widget', { template: 'Список задач',});
// классический: обычный объект Vue, имени в обёртке у него нетexport const Plain = { template: 'Список задач' };Мутабельным компонент делает только объявление через обёртку платформы. Имя пишут в kebab-case и начинают с имени модуля, иначе решения разных вендоров столкнутся именами.
Проверяем тип возвращаемым значением:
const applied = BitrixVue.mutateComponent('local-widget', { template: `<div class="my-frame">#PARENT_TEMPLATE#</div>`,});console.log(applied); // false - компонент классический, правка не примениласьДля классического компонента вызов возвращает признак неудачи и делает это молча: ошибки в консоли не будет. Такому компоненту доступно только клонирование.
Мутация
Заменяем шаблон и метод оригинала:
BitrixVue.mutateComponent('local-widget', { template: `<div class="my-frame">#PARENT_TEMPLATE#</div>`, // оригинал внутри methods: { save(item) { this.trackChange(item); // своя строка перед штатной логикой return this.parentSave(item); // вызов исходного метода }, },});Мутация применяет только перечисленные ключи и накапливается: второй вызов не отменяет первый. К исходным частям обращаются через префиксы, а не копируют их код к себе.
Дотягиваемся до данных и наблюдателей:
BitrixVue.mutateComponent('local-widget', { data() { return { ...this.parentData(), compactMode: true }; // данные плюс своё }, watch: { items(value) { this.parentWatchItems(value); }, // наблюдатель оригинала }, replaceEmits: ['local:widget:saved'], // список событий целиком});Родительский префикс даёт доступ к исходной части, префикс замены затирает её целиком. Списки примесей, внедрений и событий меняются только вторым способом.
Мутация в третьей версии обёртки действует лишь до создания экземпляров. Уже отрисованные экземпляры и клоны оригинала правку не получат, поэтому регистрируют её при загрузке расширения.
Клон
Делаем независимую копию оригинала:
const Compact = BitrixVue.cloneComponent('local-widget', { template: `Компактный список`,});// клон снимается с ОРИГИНАЛА, а не с уже мутированной версии компонента// клонировать можно и классический компонент, которому мутация недоступнаКлон нужен там, где правка требуется на одном экране, а штатный вид компонента остаётся на всех остальных. Мутация же меняет компонент везде, где он выводится.
Порядок загрузки
Объявляем оригинал зависимостью своего расширения:
return [ 'js' => 'dist/widget-custom.bundle.js', 'rel' => ['ui.vue3', 'local.widget'], // оригинал подключится раньше нас];Обращение к компоненту по имени до загрузки оригинала даёт некорректный результат. Ошибки при этом не будет: клон соберётся пустым, а мутация уйдёт в никуда.
Проверка результата
Включаем режим разработки обёртки:
define('VUEJS_DEBUG', true); // обёртка перестаёт молчать об ошибкахdefine('VUEJS_LOCALIZATION_DEBUG', true); // коды фраз вместо переводовДерево компонентов и применённые правки смотрят браузерным расширением Vue.js devtools. Само расширение обёртки требует модуль интерфейсов версии 22.100.0 и выше.
Пересобираем исходники после каждой правки:
cd /local/jsbitrix build -w # флаг включает слежение за изменениями исходниковБез пересборки страница получит прежний бандл, и правка не появится. Мутацию перепроверяют после каждого обновления продукта: состав ключей у штатного компонента между версиями меняется.
Типичные проблемы
Мутация не применилась, ошибки в консоли нет.
Компонент объявлен обычным объектом Vue, а не через мутабельное объявление обёртки. Вызов mutateComponent вернул признак неудачи молча, и правка нигде не применилась.
Правка видна не везде: часть блоков осталась прежней.
Мутацию зарегистрировали после того, как экземпляры компонента уже отрисовались на странице. На отрисованные экземпляры и на клоны оригинала мутация не действует.
Клон получился пустым, а оригинал при этом работает.
Клон снимали по имени раньше, чем на странице загрузилось расширение с оригиналом. Оригинал объявляют зависимостью своего расширения, и тогда порядок держит платформа.
Правки чужого компонента исчезли после обновления продукта.
Компонент правили прямо в файлах ядра, а обновление вернуло их к исходному виду. Свой код держат в отдельном расширении и меняют компонент мутацией.
В собранном бандле изменений нет, хотя исходники правились.
Сборщик не запускали после правки, и страница получила прежний собранный файл. Слежение за изменениями включает флаг у команды сборки расширений.
Частые вопросы
Как поменять штатный компонент Битрикса, не трогая его файлы?
Мутацией: обёртка применяет к оригиналу только перечисленные ключи, а к исходным частям даёт доступ через префиксы. Правка живёт в своём расширении и переживает обновление продукта, в отличие от правки файлов ядра.
Мутация или клон - что выбрать?
Мутация меняет компонент везде, где он выводится, клон - только там, где подключён сам клон. Если штатный вид компонента должен где-то остаться прежним, берут клон.
Почему mutateComponent вернул false?
Компонент классический, а не мутабельный. Мутация работает только с тем, что объявлено через mutableComponent; классический компонент копируют вызовом cloneComponent, который работает с обоими видами.
Компонент не рендерится после обновления Битрикса, ошибок в консоли нет.
Сначала проверяют версию модуля интерфейсов и пересобирают свои расширения командой сборки. Потом сверяют мутацию с оригиналом: состав ключей у штатного компонента между версиями меняется, и правка уходит в никуда.
Где искать имя штатного компонента, который надо поправить?
В папке компонентов первого уровня внутри расширения: имя мутабельного компонента пишется в kebab-case и начинается с имени модуля. У классического компонента имени в обёртке нет, его находят по экспорту расширения.
Смежное
- Свои расширения JS - оглавление подтемы
- BitrixVue 3 - устройство обёртки целиком
- Vue-приложение на странице: запуск, компоненты, данные - своё приложение вместо правки чужого
- Своё расширение JS: каталог, зависимости, подключение - куда положить код мутации
- Расширение JS не подключается: разбор причин - если оригинал не доехал до страницы
- Сборка фронтенда рядом с шаблоном: исходники, бандл, приложение - пересборка исходников и выкладка
- Cannot read properties of undefined: разбор причин на витрине - обращение к незагруженному коду
- Кастомизация чужого компонента: шаблон вместо правки ядра - тот же приём для компонентов 2.0