Как интегрировать Vue 3 в шаблон компонента Bitrix и создать рекурсивный список?

Я подключил Vue к шаблону компонента в Bitrix. В файле template.php определён div#app, к которому подключается Vue через CreateApp(). Скрипт, содержащий Vue, находится в файле vue.js и подключён в template.php. Всё функционирует без сбоев, однако теперь возникла необходимость выводить рекурсивный список, который хранится в памяти в виде объекта с вложенными структурами. Я не уверен, как это реализовать без создания отдельных компонентов Vue. Можете ли вы помочь с следующим? <br/> <br/> 1. Как можно создать и подключить компонент Vue при текущем подходе? Возможно ли написать разметку компонента непосредственно в template.php и подключить её как компонент? Например, с использованием ob_start() для сохранения разметки в переменную, а затем передать эту переменную в JavaScript и использовать её в vue.js для создания шаблона компонента? <br/> <br/> Либо, <br/> <br/> 2. Существует ли способ вывести рекурсивный объект без необходимости создавать дополнительный компонент?
  • 28 ноября 2024 г. 14:23
Ответы на вопрос 2
Ваша задача может быть решена с использованием различных подходов. Ниже приведены варианты для создания рекурсивного списка в Vue 3, а также несколько советов по работе с компонентами в контексте Bitrix.

### Вариант 1: Использование обертки `ob_start()` для передачи разметки в компонент Vue

Вы можете использовать `ob_start()` для генерации шаблона прямо в `template.php`, а затем передать этот шаблон в ваш компонент Vue.

1. В `template.php` создайте HTML-шаблон с использованием `ob_start()`:

```php
<?php ob_start(); ?>
<ul>
    <li v-for="item in items" :key="item.id">
        {{ item.name }}
        <ul v-if="item.children && item.children.length > 0">
            <li-item :items="item.children"></li-item>
        </ul>
    </li>
</ul>
<?php $template = ob_get_clean(); ?>
```

2. Передайте сгенерированную разметку в Vue-приложение:

```html
<div id="app">
    <div v-html="template"></div>
</div>
<script>
const app = Vue.createApp({
    data() {
        return {
            items: [] // Здесь вы можете загрузить ваши данные
        };
    },
    mounted() {
        // Здесь вы можете выполнить AJAX-запрос или получить ваши данные
        // и передать их в `this.items`
    }
});

app.component('li-item', {
    props: ['items'],
    template: `<?= $template ?>`
});

app.mount('#app');
</script>
```

Этот подход позволяет вам использовать разметку, определённую в PHP, внутри компонента Vue.

### Вариант 2: Реализация рекурсивного списка без дополнительных компонентов

Если вы хотите избежать создания отдельных компонентов для рекурсии, вы можете использовать встроенные шаблоны Vue и рекурсивный рендеринг прямо внутри основного Vue-приложения.

```javascript
const app = Vue.createApp({
    data() {
        return {
            items: [
                {
                    id: 1,
                    name: 'Item 1',
                    children: [
                        { id: 11, name: 'Item 1.1', children: [] },
                        { id: 12, name: 'Item 1.2', children: [] }
                    ]
                },
                {
                    id: 2,
                    name: 'Item 2',
                    children: []
                }
            ]
        };
    },
    template: `
        <ul>
            <li v-for="item in items" :key="item.id">
                {{ item.name }}
                <recursive-list v-if="item.children.length" :items="item.children"></recursive-list>
            </li>
        </ul>
    `
});

// Рекурсивный компонент внутри основного приложения
app.component('recursive-list', {
    props: ['items'],
    template: `
        <ul>
            <li v-for="item in items" :key="item.id">
                {{ item.name }}
                <recursive-list v-if="item.children.length" :items="item.children"></recursive-list>
            </li>
        </ul>
    `
});

app.mount('#app');
```

### Заключение

Оба подхода имеют свои преимущества:

- **Подход с `ob_start()`** позволяет вам использовать PHP-генерируемую разметку, что может быть полезно в контексте интеграции с Bitrix.
- **Рекурсивный компонент в Vue** предлагает более гибкое решение для работы с данными без создания дополнительных зависимых компонентов.

Выберите подход, который лучше соответствует вашей архитектуре приложения и требованиям проекта.
Я так понял до сих пор вы использовали vue вообще без компонентов - что есть в шаблоне - то и рисуется. Таким образом вы теряете 99% пользы и удобства vue. 

Конечно же можно создать в tamplate компонент, прямо там его добавить в vue и горя не знать.

Просто делаешь рекурсивный компонент:
var RecursiveComponent = {
  name: 'recursive-component',
  props: ['i'],
  setup() {
    const open = ref(false)
    return { open }
  },
  template: `
    <div>
      <recursive-component v-if="open" :i="i ? i+1 : 1"></recursive-component>
      <button @click="open=!open">toggle {{i}}</button>
    </div>
  `
}

И просто его используешь:
Vue.createApp({
  components: { RecursiveComponent },
  setup() {
    return {
      message: 'Привет Vue!'
    }
  }
}).mount('#app');

<div id="app">
  {{ message }}
  <recursive-component></recursive-component>
</div>


P.S. Теоретически вместо строки в template вы действительно можете забуфферизировать вывод через ob_start, положить в переменную, а потом эту переменную положить в template. Но насколько я помню в битриксе не всё так просто с буферизацией и не факт что оно заработает и ничего не сломает...
Похожие вопросы