Vue.js интеграция
Imhotep View имеет встроенную поддержку Vue.js для создания реактивных интерфейсов. Интеграция позволяет использовать Vue-компоненты напрямую в Moon-шаблонах, автоматически собирая все зависимости и ресурсы.
Структура директорий
resources/views/
├── vue/
│ ├── components/ # Vue-компоненты
│ │ ├── Header.vue # SFC компонент <v-header />
│ │ ├── Footer.vue
│ │ ├── Alert/ # Классический компонент (папка)
│ │ │ ├── Alert.js # JS логика компонента
│ │ │ └── Alert.scss # Стили компонента (или .css)
│ │ └── UI/ # Вложенные директории
│ │ ├── Select.vue # <v-ui-select />
│ │ └── Button/ # Классический компонент <v-ui-button /> во вложенной папке
│ │ ├── Button.js
│ │ └── Button.scss
│ ├── composables/ # Композаблы (реактивные функции)
│ │ ├── useAlert.js
│ │ └── useUser.js
│ ├── app.js # Основное приложение (обязательно)
│ └── utils.js # Вспомогательные функции (опционально)
Инициализация Vue
Для работы Vue-компонентов необходимо выполнить начальную настройку в Moon-шаблоне.
Базовый шаблон
<!DOCTYPE html>
<html>
<head>
<title>Vue-интеграция</title>
{{-- Подключение стилей компонентов --}}
@vuehead
</head>
<body>
{{-- Использование компонентов --}}
<v-greeting :message="Hello, Imhotep!" />
<script src="https://unpkg.com/vue@latest/dist/vue.global.js"></script>
{{-- Инициализация Vue (всегда в конце body) --}}
@vue([
'user' => auth()->user(),
'settings' => $settings
])
</body>
</html>
Директива @vuehead
Подключает стили и внешние ресурсы компонентов. Размещается внутри тега <head>.
Директива @vuecomposables
Управляет загрузкой композаблов. Если директива не указана, при инициализации через @vue будут загружены все композаблы из папки composables/.
Если директива указана, будут загружены только перечисленные композаблы, остальные игнорируются.
{{-- Без указания - загружаются все композаблы --}}
@vue([])
{{-- Загрузка только указанных композаблов --}}
@vuecomposables(['useAlert', 'useUser'])
@vue(['user' => auth()->user()])
{{-- Загрузка композаблов из пространства имен --}}
@vuecomposables(['admin::useAlert', 'admin::useUser'])
@vue([], 'admin')
Важно: @vuecomposables должна вызываться до @vue
Директива @vue
Производит полную инициализацию Vue-приложения: подключает компоненты, композаблы, стили и передает данные. Размещается в конце <body>.
{{-- Передача данных в компоненты --}}
@vue(['user' => auth()->user(), 'errors' => $errors])
{{-- Инициализация с указанием пространства имен --}}
@vue([], 'admin') // Будет искать admin::vue.app
{{-- Инициализация без данных --}}
@vue([])
Важно: @vue должна вызываться после всех компонентов и после @vuecomposables (если используется).
Файл app.js
При инициализации через @vue автоматически подключается файл resources/views/vue/app.js, который отвечает за регистрацию и монтирование компонентов на странице.
// resources/views/vue/app.js
const { createApp } = Vue;
class componentRegistryPlugin {
constructor() {
this.components = new Map();
this.instances = new Set();
}
register(name, component) {
this.components.set(name, component)
this.instances.forEach(app => {
if (!app.component(name)) {
app.component(name, component)
}
})
return this
}
install(app) {
// Регистрируем все компоненты в новом приложении
this.components.forEach((component, name) => {
app.component(name, component)
})
// Сохраняем экземпляр для будущих обновлений
this.instances.add(app)
// Добавляем метод для динамической регистрации
app.config.globalProperties.$registerComponent = (name, component) => {
this.register(name, component)
}
}
}
const componentRegistry = new componentRegistryPlugin();
if (window.$vueComponents) {
window.$vueComponents.forEach(comp => {
componentRegistry.register(comp.name, comp.component);
const el = document.querySelector('[data-v-component="' + comp.name + '"]');
if (el) {
let initialData = JSON.parse(el.dataset.initial || '{}');
for (let key in el.dataset) {
if (key === 'initial' || !key.startsWith('initial')) continue;
let name = key.replace('initial', '');
name = name.charAt(0).toLowerCase() + name.slice(1);
initialData[name] = JSON.parse(el.dataset[key] || '{}');
}
createApp(comp.component, {...initialData}).use(componentRegistry).mount(el)
}
});
}
Файл utils.js
Если вашим компонентам нужны общие функции, вы можете разместить их в файл resources/views/vue/utils.js, который автоматически подключается при инициализации.
// resources/views/vue/utils.js
const formatPrice = (price) => {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
}).format(price);
};
Создание компонентов
Вы можете использовать два способа создания Vue-компонентов.
Однофайловые компоненты (SFC)
Однофайловые компоненты (файлы .vue) объединяют шаблон, логику и стили в одном файле. Это предпочтительный способ для большинства случаев.
<!-- resources/views/vue/components/Greeting.vue -->
<template>
<div class="greeting">
<p>{{ greeting }}</p>
<button @click="count++">Счетчик: {{ count }}</button>
</div>
</template>
<script>
export default {
props: {
message: {
type: String,
default: 'Hello World!'
}
},
data() {
return {
count: 0,
greeting: this.message
};
},
mounted() {
console.log('Компонент смонтирован');
}
}
</script>
<style lang="scss">
.greeting {
padding: 20px;
background: #f5f5f5;
border-radius: 8px;
p {
color: #2c3e50;
font-weight: bold;
}
}
</style>
Классический компонент
Состоит из двух файлов: ComponentName.js и ComponentName.scss (или .css). Менее удобный вариант чем SFC.
Важно: Для классического компонента обязательно наличие (window.$vueComponents = window.$vueComponents || []).push({ ... }); для правильной инициализации. Имена JS и SCSS файлов должны совпадать с именем компонента.
// resources/views/vue/components/Alert/Alert.js
(window.$vueComponents = window.$vueComponents || []).push({
name: 'alert',
component: {
setup() {
const { state } = useAlert();
return { state };
},
template: `
<transition name="fade-alert">
<div v-if="state.visible" class="alert" :class="'is-' + state.type">
{{ state.message }}
</div>
</transition>
`
}
});
// resources/views/vue/components/Alert/Alert.scss
.alert {
z-index: 9999;
position: fixed;
top: 2rem; right: 2rem;
display: flex;
align-items: center;
gap: 1rem;
min-width: 300px;
padding: 1rem 1.25rem;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
color: #1f2937;
}
/* Vue Transitions */
.fade-alert-enter-active, .fade-alert-leave-active {
transition: all 0.3s ease;
}
.fade-alert-enter-from, .fade-alert-leave-to {
opacity: 0;
transform: translateY(-20px);
}
Именование компонентов
Имя компонента формируется из пути к файлу относительно папки components/:
- Путь преобразуется в kebab-case (строчные буквы с дефисами)
- Вложенные директории добавляются через дефис:
UI/Select.vue→v-ui-select - Компонент из папки
Alert/Alert.js→v-alert
Использование компонентов
Использование Vue-компонентов доступно только из Moon-шаблонов. Все Vue-компоненты начинаются с префикса v-, например <v-alert />.
Передача данных в компоненты
Для передачи данных в компонент используйте классический вариант :name="$name" или data-initial="$user".
<div>
{{-- Подключение Vue-компонента --}}
<v-alert :message="Hello" />
{{-- Через data-initial атрибуты --}}
<v-user-profile data-initial="$user" />
</div>
Компоненты с пространствами имен
{{-- Vue-компонент из пространства имен: admin --}}
<admin::v-alert />
Создание композаблов
Композаблы размещаются в папке resources/views/vue/composables. Названием файла является именем композабла. Имя всегда начинается с use (например, useAlert). Композаблы автоматически подключаются во время инициализации тега @vue.
// resources/views/vue/composables/useUser.js
export function useUser() {
const name = 'Алексей';
const age = 30;
function greet() {
console.log(`Привет, ${name}!`);
}
return { name, age, greet };
}
Если необходимо подключить только определенные композаблы, передайте их список в директиву @vuecomposables перед @vue:
// resources/views/layout/default.moon.php
<body>
@vuecomposables(['useUser', 'useAlert'])
@vue([])
</body>