Чуть-чуть Vue.js в Drupal 8

Прикручивал тут к одному проекту на Drupal 8 Vue.js для фронт-энда. Нужно было несколько интерактивных фильтров на паре страниц сделать, на Vue ложилось идеально. Но поскольку кроме фильтров больше ни для чего Vue был не нужен, в полный headless не пошел. Ограничился эдаким headless-less подходом, когда на нужных страницах разметку генерирует все еще Друпал, а Vue берет с неё свои компоненты, данные в которых переданы через пропы, а незначащая разметка — через слоты.

Изначально хотел описать по порядку, что и как я делал, но по ходу применения этого гибридного headless-less варианта пришел к выводу, что делать так вообще не надо. Лучше (если Vue нужен только для конкретных страниц) — отдавать что-нибудь вроде

<master-component :data="jsonData"></master-component>

где в jsonData упаковано всё что нужно для генерации разметки на стороне Vue. А дальше пусть Vue сам разберется.

Потому что иначе постоянно возникают ограничения и несовместимости, возникают и выносят мозг. Перечислю некоторые из них.

Во-первых, часть разметки все равно придется перенести в компоненты. Вот например представим, что нам надо список товаров фильтровать с помощью формы из нескольких чекбоксов. Если попробовать вставить разметку формы в слот и передать через scope значения в v-model, связь получится односторонняя. Фильтр примет начальные значения из scope, но передавать изменения обратно в компонент — не будет. То есть полностью сохранить темплейт на стороне Друпала не получится.

Во-вторых, вот есть у Vue.js транзишны для анимации. Пользоваться удобно, enter/leave можно сделать за 5 минут просто обернув компонент в <transition></transition> и добавив три строчки CSS. Но есть в транзишнах вот такая клевая штука для списков. То есть анимация вставки/удаления элементов списка. Если мы генерируем разметку на стороне Друпала:

{% for product in products %}
  <product :data="{{ product_data }}">
    <div>Разметка товара</div>
  </product>
{% endfor %}

то list-транзишны работать не будут, нужно чтобы список был сгенерирован на стороне Vue.js через v-for. То есть, опять-таки, чем больше передадим во Vue.js в виде json-данных — тем лучше.

Далее, если эти компоненты product переданы через слот в родительский компонент Products, мы будем испытывать затруднения если захотим обратиться к каждому из дочерних product-ов (например, чтобы определить количество видимых в данных момент товаров). Так-то во Vue можно задать дочерним компонентам ref и обращаться в родительском через this.$refs. Но, опять же, для этого компоненты должны создаваться в родительском темплейте на стороне Vue. А так пришлось изгаляться с jQuery (что при наличии транзишнов не очень-то весело).

Наконец, следует помнить, что вся Vue-related разметка, сгенерированная на стороне Друпала, не проходит через систему сборки, в отличие от файлов .vue с компонентами. Из-за этого нужно быть осторожным в её написании. Например, взяли мы и деструктурировали для удобства данные, передаваемые в scoped-slot:

<div slot-scope="{ results }">
  <p v-show="results">There are results!</p>
</div>

И всё, пока-пока, IE11. А на стороне Vue этот код был бы собран с поддержкой совместимости.

В общем, эксперимент с headless-less показал, что для небольших интерактивностей Vue без headless использовать можно, но органичения подстерегают повсюду. И если есть такая возможность — на страницах с компонентами лучше сразу возвращать данные в json и строить компоненты на стороне Vue.js.

Комментарии