Форум программистов, компьютерный форум, киберфорум
Jason-Webb
Войти
Регистрация
Восстановить пароль

Vue SFC компонент на PHP с Fusion

Запись от Jason-Webb размещена 09.04.2025 в 22:23
Показов 2517 Комментарии 0
Метки fusion, php, vue

Нажмите на изображение для увеличения
Название: 34823801-6a6a-431a-ae13-fa39e21bc3e5.jpg
Просмотров: 161
Размер:	169.6 Кб
ID:	10568
PHP на сервере и JavaScript на клиенте — классическое сочетание, которое, несмотря на свою эффективность, создает определенный когнитивный диссонанс при разработке. В этом контексте появляются различные решения, пытающиеся сгладить этот разрыв, и одним из таких революционных подходов стал Fusion.

Fusion — это новый инструмент, представленный в феврале 2025 года на конференции Laracon EU, который позволяет писать PHP-код непосредственно в Vue SFC (Single File Components). Это кардинально меняет парадигму разработки, объединяя серверную и клиентскую логику в одном файле. По сути, Fusion — это мост между backend и frontend, который устраняет необходимость дублирования кода и логики между серверной и клиентской частями приложения.

Особенности синтаксиса и отличия Fusion от Livewire и Inertia.js



Синтаксис Fusion отличается лаконичностью и интуитивностью. Вместо создания отдельных контроллеров и компонентов, разработчик может написать код в едином файле:

TypeScript
1
2
3
4
5
6
7
8
9
<php>
  // Определение пропса в PHP
  $name = prop(Auth::user()->name);
</php>
 
<template>
  <!-- Использование в Vue! -->
  Hello {{ name }}!
</template>
В отличие от Livewire, который полностью основан на PHP и генерирует JavaScript под капотом, Fusion сохраняет преимущества Vue SFC, при этом позволяя напрямую интегрировать PHP-код. Если сравнивать с Inertia.js, который соединяет Laravel и JavaScript-фреймворки через JSON API, Fusion идет дальше, предлагая более тесную интеграцию кода.

Где завкладка “Vue” в консоле моего Vue.js приложения ?
Всем привет Я установил Vue.js devtools на Google Chrome Version 85.0(kubuntu 18) Но я не вижу...

Vue.js не видит пути из vue.config.js
Здравствуйте. Пытаюсь сделать многостраничное приложение. Следую инструкции для Vue.js 3, сделал...

Vue.draggable + vue-lazyload
Есть лист на 800+ элементов, и количество элементов будет только увеличиваться. Реализовано это всё...

vue 3. Как использовать метод из миксина с параметрами в компоненте vue по нажатию на кнопку?
Как вызвать метод query в компонент при клике на кнопку Subscribe axiosMixin.js const...


Ключевые проблемы традиционных методов обмена данными



Традиционные методы обмена данными между PHP и фронтендом страдают от нескольких фундаментальных проблем:
1. Избыточный сетевой обмен — каждый запрос данных требует отдельного HTTP-запроса.
2. Дублирование кода валидации и бизнес-логики на фронтенде и бэкенде.
3. Сложность поддержания согласованности между моделями данных.
4. Необходимость создания и поддержки API-контрактов.
5. Разрыв контекста разработки, вынуждающий программиста постоянно переключаться между разными файлами.

Fusion решает эти проблемы, позволяя работать в едином контексте компонента. Более того он делает возможным вызов PHP-функций с фронтенда почти так же просто, как вызов JavaScript-функций, используя механизм RPC (Remote Procedure Call).

Целевая аудитория Fusion



Fusion особенно привлекателен для определенных групп разработчиков:
  • Команды с сильным PHP-бэкграундом, желающие использовать современный фронтенд без глубокого погружения в JavaScript-экосистему.
  • Разработчики Laravel, стремящиеся упростить архитектуру своих приложений.
  • Проекты с интенсивным обменом данными между клиентом и сервером.
  • JavaScript-разработчики, приходящие в экосистему Laravel, которым привычнее файловая маршрутизация и компонентный подход.

Fusion может быть особенно полезен в проектах, где требуется быстрое прототипирование и итеративная разработка функционала с тесной интеграцией клиентской и серверной частей.

Концепция Vue SFC в контексте Fusion



Vue SFC (Single File Components) — это подход, при котором шаблон, логика и стили компонента объединяются в одном файле с расширением .vue. Это делает компоненты более самодостаточными и реализует принцип инкапсуляции на уровне файловой системы. Традиционно Vue SFC включает три секции:
<template> — HTML-разметка компонента,
<script> — JavaScript-логика,
<style> — CSS-стили.

Fusion расширяет эту концепцию, добавляя новую секцию <php>, в которой можно писать PHP-код. Этот код выполняется на сервере, а результаты передаются на фронтенд как пропсы компонента. Такое расширение позволяет использовать все возможности PHP-экосистемы (ORM, аутентификацию, валидацию и т.д.) непосредственно в контексте Vue-компонента. Это не просто синтаксический сахар, а фундаментальное изменение способа взаимодействия фронтенда и бэкенда. По сути, Fusion делает для Laravel то же, что современные метафреймворки вроде Next.js или Nuxt.js делают для React и Vue соответственно — сближает серверную и клиентскую разработку, делая процесс более интегрированным и интуитивным.

Технический обзор Fusion



Fusion представляет собой технологическую связку между PHP-бэкендом и Vue-фронтендом, функционирующую на принципах единого источника истины для данных и автоматической синхронизации состояний. Вместо создания дополнительных слоев абстракции, Fusion органично вписывается в существующий стек технологий, расширяя возможности стандартных компонентов Vue SFC.

Принципы работы и архитектура



В основе архитектуры Fusion лежит плагин для Vite, который обрабатывает файлы .vue и извлекает из них PHP-код. Этот код затем валидируется, трансформируется в формат, совместимый с Laravel, и сохраняется в специально выделенной директории. При обращении к соответствующему маршруту сгенерированный PHP-класс выполняется, а результаты передаются обратно во фронтенд как пропсы Vue-компонента. Схематически процесс работы Fusion можно представить так:

1. Vue SFC с PHP-блоком → Плагин Vite → Извлечение PHP-кода.
2. Валидация и трансформация PHP → Генерация PHP-класса.
3. Создание JavaScript-шима для связи с PHP.
4. Регистрация маршрута в Laravel.
5. Выполнение PHP-кода при обращении к маршруту.
6. Передача данных в Vue через Inertia.
7. Двусторонняя синхронизация состояний.

Для хранения метаданных о компонентах Fusion использует локальную SQLite базу данных, что позволяет эффективно связывать фронтенд и бэкенд без влияния на основную базу данных приложения.

Инструменты и зависимости, необходимые для работы с Fusion



Для полноценной работы с Fusion требуется следующий набор технологий:
Laravel 10+ – основной фреймворк для бэкенда,
Vue 3 – фронтенд-фреймворк для создания компонентов,
Vite – современный сборщик фронтенда,
Inertia.js – библиотека, связывающая Laravel и Vue,
Node.js – среда выполнения JavaScript,
SQLite – для внутреннего хранения метаданных.

Важно понимать, что Fusion не заменяет Inertia.js, а надстраивается над ним, существенно расширяя его возможности. Это позволяет использовать привычные подходы к разработке на Inertia, добавляя к ним новые возможности Fusion.

Жизненный цикл Fusion-компонента от написания до рендеринга



Жизненный цикл компонента начинается с создания файла .vue, содержащего блок <php>:

TypeScript
1
2
3
4
5
6
7
8
9
<php>
use function \Fusion\{prop};
$name = prop('Мир');
$name = strtoupper($name);
</php>
 
<template>
  Привет, {{ name }}!
</template>
При запуске сервера разработки или изменении файла плагин Vite обнаруживает PHP-блок, извлекает его и запускает процесс валидации. Если код корректен, он трансформируется в PHP-класс:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
namespace Fusion\Generated\Pages;
 
class IndexGenerated extends \Fusion\FusionPage
{
    #[\Fusion\Attributes\ServerOnly]
    public array $discoveredProps = ['name'];
 
    use \Fusion\Concerns\IsProceduralPage;
 
    public function runProceduralCode()
    {
        $name = $this->prop(name: 'name', default: 'Мир')->value();
        $name = strtoupper($name);
        $this->syncProps(get_defined_vars());
    }
}
Одновременно генерируется JavaScript-шим для связи с этим классом, а в исходный Vue-компонент добавляется скрипт для использования этого шима:

TypeScript
1
2
3
4
5
6
7
<script setup>
import { useFusion } from '$fusion/Pages/Index.js'
import useHotFusion from '@fusion/vue/hmr'
 
const __fusionData = useFusion(['name', 'fusion'], __props.fusion)
const { name, fusion } = __fusionData
</script>
При обращении к маршруту Laravel выполняет соответствующий PHP-класс, подготавливает данные и передает их через Inertia в Vue-компонент, который использует их для рендеринга.

Механизмы обработки состояний и реактивных данных в Fusion



Fusion предлагает два основных механизма для работы с данными: prop() для определения свойств и expose() для экспорта серверных методов. Функция prop() создает реактивное свойство, которое автоматически синхронизируется между сервером и клиентом. Свойства могут быть простыми значениями или функциями, возвращающими данные:

TypeScript
1
2
3
4
5
6
<php>
use function \Fusion\{prop};
use \App\Models\Podcast;
 
$podcasts = prop(fn () => Podcast::all())->readonly();
</php>
Функция expose() открывает серверные методы для вызова с клиента, формируя RPC-интерфейс:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
<php>
use function \Fusion\{expose};
use \App\Models\Podcast;
 
expose(favorite: fn (Podcast $podcast) => response()->json($podcast->toggleFavorite()));
</php>
 
<template>
  <button @click="favorite({ podcast }).then(fav => podcast.favorited = fav)">
    Избранное
  </button>
</template>
Это позволяет вызывать PHP-методы прямо из шаблона или JavaScript-кода, как если бы они были обычными клиентскими функциями. Дополнительно Fusion предоставляет функцию sync() для двунаправленной синхронизации состояния, что особенно удобно для форм и других интерактивных элементов.

Внутреннее устройство компилятора Fusion



При трансформации PHP-кода Fusion использует серию специализированных трансформеров, каждый из которых отвечает за определенный аспект преобразования. Среди них:

ProceduralTransformer – преобразует процедурный код в метод класса.
PropsTransformer – обрабатывает вызовы функции prop().
ExposeTransformer – преобразует вызовы функции expose().
PropDiscoveryTransformer – обнаруживает имена свойств.
ActionDiscoveryTransformer – находит и создает методы для обработчиков действий.

Процесс трансформации использует библиотеку PHP Parser для анализа и модификации PHP-кода, что позволяет создавать корректные PHP-классы из произвольного кода в блоке <php>. На стороне JavaScript Fusion генерирует композабл useFusion, который извлекает данные из пропсов и предоставляет доступ к серверным методам. Это создает единый интерфейс для работы с данными, независимо от их источника.

Анализ производительности: сравнение нативного Vue и Fusion



В плане производительности Fusion находится на уровне нативных Vue-приложений на Inertia, поскольку фактический рендеринг все равно происходит на стороне клиента с использованием Vue. Основные отличия касаются начальной загрузки (где Fusion может добавить небольшую задержку из-за дополнительных преобразований) и последующи взаимодействий (где Fusion потенциально может быть эффективнее за счет оптимизированного обмена данными). Для приложений средней сложности эти различия практически незаметны, а преимущества в скорости разработки и поддержки могут значительно перевешивать любые теоретические потери в производительности.

Сравнение с традиционными подходами к интеграции PHP и Vue



По сравнению с традиционными подходами, Fusion предлагает несколько существенных преимуществ:
1. Единый контекст разработки – вместо создания отдельных контроллеров и компонентов, вся логика находится в одном файле.
2. Отсутствие дублирования кода – нет необходимости дублировать валидацию и бизнес-логику.
3. Прозрачный RPC – вызов серверных методов выглядит как обычный вызов JavaScript-функций.
4. Файловая маршрутизация – маршруты автоматически создаются на основе структуры файлов, как в Next.js или Nuxt.js.

В традиционном подходе с REST API разработчику приходится создавать контроллеры, определять маршруты, создавать JavaScript-код для взаимодействия с API и т.д. Fusion автоматизирует большую часть этого процесса, делая разработку более быстрой и интуитивной.

Процесс компиляции компонентов



Углубимся в технические аспекты преобразования PHP-кода в Fusion. Процесс компиляции компонентов является комплексным и включает несколько ключевых этапов:
1. Обнаружение и извлечение – с помощью парсера @vue/compiler-sfc плагин Fusion извлекает блок <php> из Vue SFC.
2. Валидация синтаксиса – извлеченный PHP-код проверяется на корректность с использованием PHP Parser.
3. Конформация – код преобразуется в структуру, совместимую с Laravel, с применением цепочки трансформеров.
4. Генерация класса – создается PHP-класс с необходимыми методами и свойствами.
5. Генерация шима – создается JavaScript-файл для связи с серверным кодом.

Каждый из этих этапов имеет свои особенности и технические нюансы, которые делают Fusion таким мощным инструментом.

Детальный анализ Vite-плагина



Работа Fusion начинается с Vite-плагина, который интегрируется в процесс сборки фронтенда. Этот плагин выполняет две ключевые функции:

1. Трансформация – обрабатывает Vue SFC при запросе через функцию transform:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
function transform(code, filename) {
  if (!filename.endsWith('.vue')) {
    return code
  }
  
  return new Transformer({
    config: fusionConfig,
    code,
    filename,
    isProd
  }).transform()
}
2. Наблюдение за файловой системой – мониторит изменения во всём проекте:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function configureServer(server) {
  server.watcher.on('all', async (event, filename) => {
    if (filename.endsWith('.vue')) {
      const fileContent = fs.readFileSync(filename, 'utf-8')
      
      await new Transformer({
        config: fusionConfig,
        code: fileContent,
        filename,
        isProd
      }).transform()
    }
  })
}
Наблюдение за файловой системой позволяет Fusion оперативно реагировать на изменения Vue SFC и автоматически обновлять сгенерированный PHP-код. Это особенно важно при первом обращении к маршруту, когда PHP-файл еще не создан.

Роль SQLite в архитектуре Fusion



Для связи между PHP и JavaScript процессами Fusion использует локальную SQLite базу данных. В ней хранятся метаданные о компонентах и связях между Vue SFC и сгенерированными PHP-классами. Структура основной таблицы базы данных выглядит примерно так:
id – уникальный идентификатор,
src – путь к Vue SFC файлу,
php_class – имя сгенерированного PHP-класса,
php_path – путь к сгенерированному PHP-файлу,
shim_path – путь к JavaScript-шиму,
php_hash – хеш PHP-кода для определения изменений.

Хеш PHP-кода используется для оптимизации – если код не изменился, повторная трансформация не выполняется.
При этом база данных никак не влияет на основное приложение Laravel. Для неё создается отдельное соединение:

PHP
1
2
3
4
5
6
7
ConfigFacade::set('database.connections.__fusion', [
  'name' => '__fusion',
  'driver' => 'sqlite',
  'database' => Fusion::storage('fusion.sqlite'),
  'foreign_key_constraints' => true,
  'journal_mode' => 'OFF',
])
Это изящное решение позволяет Fusion функционировать не затрагивая основную базу данных проекта.

Файловая маршрутизация в Fusion



Одним из главных преимуществ Fusion является автоматическая файловая маршрутизация, аналогичная той, что используется в Next.js или Nuxt.js. Vue SFC, размещенные в директории resources/js/Pages, автоматически регистрируются как маршруты Laravel. Регистрация происходит при запуске приложения через вызов Fusion::pages() в файле routes/web.php:

PHP
1
2
3
4
<?php
use Fusion\Fusion;
 
Fusion::pages();
При этом Fusion анализирует структуру директории, находит все Vue SFC и регистрирует маршруты, используя стандартный Laravel Router:

PHP
1
2
3
Route::any($uri, [FusionController::class, 'handle'])
  ->defaults('__component', $component)
  ->defaults('__class', Component::where('src', $src)->first()?->php_class);
Это позволяет использовать привычные соглашения об именовании файлов для определения маршрутов. Например:

resources/js/Pages/Index.vue/
resources/js/Pages/About.vue/about
resources/js/Pages/Users/[Id].vue/users/{id}

Интеграция с Inertia.js



Важно понимать, что Fusion не является полной заменой Inertia.js, а скорее его расширением. Fusion все еще использует Inertia для рендеринга компонентов и передачи данных. Это видно по атрибуту data-page в HTML-ответе:

JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "component": "Index",
  "props": {
    "errors": {},
    "auth": {
      "user": null
    },
    "fusion": {
      "meta": [],
      "state": {
        "name": "AARON"
      },
      "actions": [
        {
          "handler": "applyServerState",
          "priority": 40,
          "_handler": "Fusion\\Http\\Response\\Actions\\ApplyServerState"
        }
      ]
    }
  },
  "url": "/",
  "version": "f7e418d032bf1c4cc3cc55d947cab622",
  "clearHistory": false,
  "encryptHistory": false
}
Fusion добавляет специальный объект fusion в пропсы, который содержит состояние и действия, необходимые для работы компонента. Это позволяет сохранить совместимость с существующими Inertia-приложениями и постепенно переходить на Fusion там, где это нужно.

Обработка запросов к серверу



Когда клиент совершает действие, требующее взаимодействия с сервером (например, нажатие на кнопку, которая вызывает метод, экспортированный через expose()), Fusion генерирует POST-запрос с специальными заголовками:
X-Fusion-Action-Handler – имя обработчика,
X-Fusion-Action-Request – флаг, указывающий, что это запрос действия.

Тело запроса содержит аргументы для метода и текущее состояние:

JSON
1
2
3
4
5
6
7
8
9
10
11
{
  "fusion": {
    "args": {
      "podcast": { /* данные подкаста */ }
    },
    "state": {
      "search": "",
      "podcasts": [ /* список подкастов */ ]
    }
  }
}
Сервер обрабатывает запрос, выполняет соответствующий метод и возвращает результат, который автоматически применяется к состоянию на клиенте. Такой подход позволяет создавать интерактивные приложения, сохраняя при этом простоту разработки и поддержки кода.

Практическое применение



Теперь, когда мы разобрались с теоретическими основами и внутренним устройством Fusion, пришло время рассмотреть практические аспекты использования этой технологии. Познакомимся с настройкой окружения, созданием компонентов и типичными сценариями применения Fusion в реальных проектах.

Настройка окружения для работы с Fusion



Для начала работы с Fusion требуется выполнить несколько шагов по настройке проекта. Процесс установки достаточно прямолинеен, поскольку Fusion интегрируется с существующей инфраструктурой Laravel и Inertia.js.

1. Создаем новый проект Laravel или используем существующий:
Bash
1
2
composer create-project laravel/laravel fusion-app
cd fusion-app
2. Устанавливаем и настраиваем Inertia.js с Vue 3:
Bash
1
2
composer require inertiajs/inertia-laravel
npm install @inertiajs/vue3
3. Добавляем пакет Fusion:
Bash
1
2
composer require fusion-php/fusion
npm install @fusion-php/vue
4. Регистрируем сервис-провайдер Fusion в конфигурации приложения (config/app.php):
PHP
1
2
3
4
'providers' => [
    // Другие провайдеры
    Fusion\Providers\FusionServiceProvider::class,
],
5. Настраиваем Vite для работы с Fusion, добавляя соответствующий плагин в vite.config.js:
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
import fusion from '@fusion-php/vue/vite';
 
export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
        vue(),
        fusion(),
    ],
});
6. Создаем структуру каталогов для Vue-компонентов:
Bash
1
mkdir -p resources/js/Pages
7. Настраиваем файловую маршрутизацию в routes/web.php:
PHP
1
2
3
4
<?php
use Fusion\Fusion;
 
Fusion::pages();
После выполнения этих шагов окружение готово к созданию компонентов с использованием Fusion. Важно запустить сервер разработки Vite, который будет обрабатывать Vue SFC с PHP-блоками:

Bash
1
npm run dev

Пошаговое создание первого PHP-Vue компонента



Создадим простой компонент для управления списком задач, демонстрирующий основные возможности Fusion.

1. Сначала создадим модель Task и миграцию для неё:
Bash
1
php artisan make:model Task -m
2. Определим структуру таблицы в миграции:
PHP
1
2
3
4
5
6
Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->boolean('completed')->default(false);
    $table->timestamps();
});
3. Настроим модель Task:
PHP
1
2
3
4
5
6
7
class Task extends Model
{
    protected $fillable = ['title', 'completed'];
    protected $casts = [
        'completed' => 'boolean',
    ];
}
4. Теперь создадим компонент resources/js/Pages/Tasks.vue:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<php>
use function \Fusion\{prop, expose};
use \App\Models\Task;
 
// Получаем список задач из базы данных
$tasks = prop(fn () => Task::all());
 
// Метод для создания новой задачи
expose(createTask: function (string $title) {
    $task = Task::create([
        'title' => $title,
        'completed' => false,
    ]);
    
    return $task;
});
 
// Метод для изменения статуса задачи
expose(toggleTask: function (Task $task) {
    $task->completed = !$task->completed;
    $task->save();
    
    return $task;
});
 
// Метод для удаления задачи
expose(deleteTask: function (Task $task) {
    $task->delete();
    return true;
});
</php>
 
<script setup>
import { ref } from 'vue';
 
const newTaskTitle = ref('');
 
function handleCreateTask() {
    if (!newTaskTitle.value.trim()) return;
    
    createTask({ title: newTaskTitle.value })
        .then(task => {
            tasks.push(task);
            newTaskTitle.value = '';
        });
}
</script>
 
<template>
    <div class="tasks-container">
        <h1>Список задач</h1>
        
        <div class="task-form">
            <input 
                v-model="newTaskTitle" 
                placeholder="Что нужно сделать?"
                @keyup.enter="handleCreateTask"
            />
            <button @click="handleCreateTask">Добавить</button>
        </div>
        
        <ul class="task-list">
            <li v-for="task in tasks" :key="task.id" class="task-item">
                <input 
                    type="checkbox" 
                    :checked="task.completed"
                    @change="toggleTask({ task }).then(updated => Object.assign(task, updated))"
                />
                <span :class="{ 'completed': task.completed }">
                    {{ task.title }}
                </span>
                <button @click="deleteTask({ task }).then(() => {
                    const index = tasks.findIndex(t => t.id === task.id);
                    if (index !== -1) tasks.splice(index, 1);
                })">
                    Удалить
                </button>
            </li>
        </ul>
    </div>
</template>
 
<style scoped>
.tasks-container {
    max-width: 600px;
    margin: 0 auto;
    padding: 20px;
}
.task-form {
    display: flex;
    margin-bottom: 20px;
}
.task-form input {
    flex-grow: 1;
    padding: 8px;
    margin-right: 10px;
}
.task-list {
    list-style: none;
    padding: 0;
}
.task-item {
    display: flex;
    align-items: center;
    padding: 10px 0;
    border-bottom: 1px solid #eee;
}
.task-item input {
    margin-right: 10px;
}
.completed {
    text-decoration: line-through;
    color: #888;
}
.task-item button {
    margin-left: auto;
    background: #ff4757;
    color: white;
    border: none;
    padding: 5px 10px;
    border-radius: 4px;
    cursor: pointer;
}
</style>
В этом примере демонстрируются основные концепции Fusion:
  • Использование prop() для получения данных из базы данных,
  • Функция expose() для экспорта методов, которые могут вызываться с клиента,
  • Автоматическая типизация аргументов в PHP (параметр Task $task),
  • Двусторонняя связь между Vue-компонентом и серверным кодом.

При обращении к маршруту /tasks будет отображён этот компонент, позволяющий управлять списком задач без необходимости создания отдельного API.

Интеграция Fusion с существующими PHP-фреймворками



Хотя Fusion создавался для Laravel, принципы работы с ним можно адаптировать и для других PHP-фреймворков. Основная задача — обеспечить трансформацию PHP-кода из Vue SFC в исполняемый код на сервере. В случае с Laravel Fusion использует:
  1. Сервис-провайдеры для регистрации и инициализации.
  2. Eloquent ORM для работы с данными.
  3. Маршрутизацию Laravel для обработки запросов.

При работе с другими фреймворками нужно реализовать аналогичные механизмы. Например, для Symfony потребуется:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/Bundle/FusionBundle.php
namespace App\Bundle;
 
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use App\DependencyInjection\FusionExtension;
 
class FusionBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->registerExtension(new FusionExtension());
    }
}
С точки зрения фронтенда, интеграция будет аналогичной, поскольку Vite и Vue работают независимо от серверного фреймворка.

Работа с асинхронными запросами в PHP-Vue компонентах



Одно из главных преимуществ Fusion — прозрачная работа с асинхронными запросами. Рассмотрим пример компонента поиска, который выполняет запросы к базе данных в реальном времени:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<php>
use function \Fusion\{prop, sync};
use \App\Models\Product;
 
// Инициализация состояния
$search = prop('');
$products = prop([]);
 
// Синхронизируем поиск с результатами
sync(function () use (&$search, &$products) {
    if (strlen($search) >= 2) {
        $products = Product::where('name', 'like', "%{$search}%")
                          ->orWhere('description', 'like', "%{$search}%")
                          ->limit(20)
                          ->get();
    } else {
        $products = [];
    }
});
</php>
 
<template>
    <div class="search-container">
        <input 
            v-model="search" 
            placeholder="Поиск продуктов..." 
            class="search-input"
        />
        
        <div v-if="search.length >= 2 && products.length === 0" class="no-results">
            По запросу "{{ search }}" ничего не найдено
        </div>
        
        <div v-if="products.length > 0" class="results">
            <div v-for="product in products" :key="product.id" class="product-item">
                <h3>{{ product.name }}</h3>
                <p>{{ product.description }}</p>
                <span class="price">{{ product.price }}</span>
            </div>
        </div>
    </div>
</template>
Функция sync() обеспечивает автоматическую синхронизацию между клиентом и сервером. Когда пользователь вводит текст в поле поиска, значение search изменяется, что автоматически инициирует запрос к серверу. Сервер выполняет функцию в sync(), обновляет переменную $products и возвращает новые данные клиенту. Всё это происходит без необходимости вручную писать код для отправки запросов, обработки ответов и обновления состояния компонента.

Оптимизация структуры проекта при использовании Fusion



При работе с Fusion рекомендуется следовать определенным принципам организации проекта:

1. Структура директорий

Fusion базируется на файловой маршрутизации, поэтому структура каталогов имеет важное значение:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
resources/
└── js/
    ├── Pages/         # Компоненты, соответствующие маршрутам
    │   ├── Index.vue  # Соответствует '/'
    │   ├── About.vue  # Соответствует '/about'
    │   └── Users/
    │       ├── Index.vue    # Соответствует '/users'
    │       └── [Id].vue     # Соответствует '/users/{id}'
    ├── Components/    # Переиспользуемые компоненты
    │   ├── Navbar.vue
    │   └── Button.vue
    └── Layouts/       # Шаблоны страниц
        └── Default.vue
2. Разделение ответственности

Несмотря на возможность писать PHP и Vue в одном файле, рекомендуется:
  1. Выносить сложную бизнес-логику в отдельные сервисы.
  2. Использовать репозитории для сложных запросов к данным.
  3. Создавать чистые Vue-компоненты для переиспользуемых UI-элементов.

3. Кеширование и производительность

Для улучшения производительности:
  • Ограничивайте объём данных, передаваемых через prop().
  • Используйте метод readonly() для неизменяемых данных: $config = prop($appConfig)->readonly().
  • Применяйте пагинацию для больших наборов данных: $users = prop(fn () => User::paginate(15)).

4. Типизация

Используйте типизацию для обеспечения надежности:
  • В PHP применяйте строгую типизацию параметров и возвращаемых значений.
  • В Vue рассмотрите возможность использования TypeScript.

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<php>
expose(getUserById: function (int $id): ?\App\Models\User {
    return User::find($id);
});
</php>
 
<script setup lang="ts">
interface User {
  id: number;
  name: string;
  email: string;
}
 
const selectedUser = ref<User | null>(null);
</script>

Паттерны обмена данными между серверной и клиентской частями



Fusion предоставляет несколько паттернов для организации взаимодействия между сервером и клиентом:

1. Пропсы (prop)

Основной механизм передачи данных с сервера на клиент:

PHP
1
2
3
4
5
6
7
8
// Статический проп
$title = prop('Заголовок страницы');
 
// Динамический проп
$users = prop(fn () => User::all());
 
// Только для чтения
$settings = prop($appSettings)->readonly();
2. Экспорт методов (expose)

Позволяет вызывать серверные методы с клиента:

PHP
1
2
3
4
expose(saveUser: function (User $user, array $data) {
    $user->fill($data)->save();
    return $user;
});
На клиенте:

HTML5
1
2
3
<button @click="saveUser({ user, data }).then(updatedUser => ...)">
    Сохранить
</button>
3. Синхронизация (sync)

Обеспечивает двунаправленную синхронизацию:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$filter = prop('all');
$tasks = prop([]);
 
sync(function () use (&$filter, &$tasks) {
    $query = Task::query();
    
    if ($filter === 'completed') {
        $query->where('completed', true);
    } elseif ($filter === 'active') {
        $query->where('completed', false);
    }
    
    $tasks = $query->get();
});
На клиенте:

HTML5
1
2
3
4
5
<select v-model="filter">
    <option value="all">Все задачи</option>
    <option value="active">Активные</option>
    <option value="completed">Завершённые</option>
</select>
Каждый из этих паттернов имеет свои преимущества и ограничения, выбор зависит от конкретного сценария использования.

Типичные сценарии использования



Fusion особенно эффективен в следующих сценариях:

1. Админ-панели и дашборды

Возможность напрямую использовать модели Eloquent в компонентах и автоматизировать работу с данными делает Fusion идеальным инструментом для создания админ-панелей.

2. Интерактивные формы с валидацией

Fusion позволяет использовать валидацию Laravel прямо в компонентах:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<php>
use function \Fusion\{prop, expose};
use Illuminate\Support\Facades\Validator;
 
$form = prop([
    'name' => '',
    'email' => '',
    'password' => '',
]);
 
$errors = prop([]);
 
expose(register: function (array $formData) {
    $validator = Validator::make($formData, [
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users',
        'password' => 'required|min:8',
    ]);
    
    if ($validator->fails()) {
        return ['success' => false, 'errors' => $validator->errors()->toArray()];
    }
    
    // Создание пользователя...
    
    return ['success' => true];
});
</php>
3. Приложения с быстрым поиском и фильтрацией

Как мы видели в примере с асинхронными запросами, Fusion отлично подходит для реализации поиска в режиме реального времени.

4. Многошаговые процессы

Fusion упрощает реализацию сложных многошаговых процессов, сохраняя состояние между шагами:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<php>
use function \Fusion\{prop, expose};
 
$step = prop(1);
$formData = prop([
    'personal' => ['name' => '', 'email' => ''],
    'address' => ['city' => '', 'street' => ''],
    'payment' => ['method' => '', 'details' => ''],
]);
 
expose(submitStep: function (int $currentStep, array $stepData) {
    // Валидация текущего шага...
    
    if ($currentStep < 3) {
        return ['success' => true, 'nextStep' => $currentStep + 1];
    } else {
        // Финальная обработка данных...
        return ['success' => true, 'completed' => true];
    }
});
</php>
В каждом из этих сценариев Fusion помогает значительно сократить количество кода и упростить разработку, объединяя клиентскую и серверную логику в рамках единого компонента.

Работа с аутентификацией и авторизацией в Fusion



При создании веб-приложений аутентификация и авторизация – критически важные аспекты. Fusion позволяет использовать стандартные механизмы Laravel для управления доступом прямо в компонентах:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<php>
use function \Fusion\{prop, expose};
use Illuminate\Support\Facades\Auth;
 
// Проверяем авторизацию пользователя
$user = prop(fn () => Auth::user());
$isAdmin = prop(fn () => Auth::check() && Auth::user()->hasRole('admin'));
 
// Метод для входа пользователя
expose(login: function (string $email, string $password, bool $remember = false) {
    $credentials = ['email' => $email, 'password' => $password];
    
    if (Auth::attempt($credentials, $remember)) {
        request()->session()->regenerate();
        return ['success' => true, 'user' => Auth::user()];
    }
    
    return ['success' => false, 'message' => 'Неверные учетные данные'];
});
 
// Метод для выхода
expose(logout: function () {
    Auth::logout();
    request()->session()->invalidate();
    request()->session()->regenerateToken();
    
    return ['success' => true];
});
</php>
 
<template>
  <div v-if="!user">
    <!-- Форма входа -->
  </div>
  <div v-else>
    <p>Привет, {{ user.name }}!</p>
    <button @click="logout().then(() => window.location.reload())">Выйти</button>
    
    <div v-if="isAdmin" class="admin-panel">
      <!-- Админ-панель -->
    </div>
  </div>
</template>
Аналогично можно реализовать политики доступа, используя возможности Gate и Policy в Laravel:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<php>
use function \Fusion\{prop};
use Illuminate\Support\Facades\Gate;
use \App\Models\Post;
 
$post = prop(fn () => Post::find(request()->route('id')));
$canEdit = prop(fn () => Gate::allows('update', $post));
</php>
 
<template>
  <div v-if="post">
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
    
    <div v-if="canEdit" class="edit-controls">
      <button>Редактировать</button>
    </div>
  </div>
</template>

Интеграция с внешними API



Fusion позволяет легко интегрироваться с внешними API, инкапсулируя логику взаимодействия с ними в серверной части:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<php>
use function \Fusion\{prop, expose};
use Illuminate\Support\Facades\Http;
 
// Инициализация состояния
$query = prop('');
$results = prop([]);
$isLoading = prop(false);
 
// Метод для поиска через внешний API
expose(search: function (string $searchQuery) {
    $isLoading = true;
    
    try {
        $response = Http::get('https://api.example.com/search', [
            'q' => $searchQuery,
            'limit' => 10
        ]);
        
        if ($response->successful()) {
            $results = $response->json('results', []);
            return ['success' => true, 'results' => $results];
        }
        
        return ['success' => false, 'error' => 'API вернул ошибку'];
    } catch (\Exception $e) {
        return ['success' => false, 'error' => $e->getMessage()];
    } finally {
        $isLoading = false;
    }
});
</php>
 
<template>
  <div class="search-container">
    <input v-model="query" placeholder="Поиск..." />
    <button @click="search({ searchQuery: query })
      .then(response => {
        if (response.success) {
          results = response.results;
        } else {
          alert(response.error);
        }
        isLoading = false;
      })">Искать</button>
    
    <div v-if="isLoading" class="loading">Загрузка...</div>
    
    <div v-else-if="results.length > 0" class="results">
      <!-- Результаты поиска -->
    </div>
  </div>
</template>

Обработка ошибок и дебаггинг в Fusion



При разработке с использованием Fusion важно понимать, как обрабатывать ошибки и отлаживать приложение. Существует несколько подходов:

1. Серверные ошибки

PHP-ошибки в блоке <php> отображаются в оверлее Vite, что упрощает их обнаружение:

TypeScript
1
2
3
4
<php>
// Ошибка будет показана в оверлее Vite
$undefined_variable;
</php>
2. Ошибки JavaScript

Для клиентской части используются стандартные инструменты отладки Vue:

Vue DevTools для инспекции компонентов и состояния
Консоль браузера для отлова ошибок JavaScript

3. Логирование

Fusion предлагает действие log для отладки, которое позволяет выводить информацию в консоль:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<php>
use function \Fusion\{prop};
 
$data = prop(['value' => 'test']);
</php>
 
<script setup>
import { useFusion } from '$fusion/Pages/Debug.js'
import { log } from '@fusion/vue/actions'
 
const { data, fusion } = useFusion(['data', 'fusion'])
 
// Добавляем действие log
fusion.addAction(log, { priority: 10 })
 
// Теперь все изменения будут логироваться
</script>
4. Трассировка запросов

Для отслеживания всех запросов можно использовать действие logStack:

TypeScript
1
2
3
4
5
6
7
<script setup>
import { useFusion } from '$fusion/Pages/Debug.js'
import { logStack } from '@fusion/vue/actions'
 
const { fusion } = useFusion(['fusion'])
fusion.addAction(logStack, { priority: 20 })
</script>

Развертывание приложений на Fusion



Процесс развертывания Fusion-приложения во многом схож с развертыванием стандартного Laravel-приложения с Inertia.js, но есть некоторые особенности:

1. Генерация PHP-файлов

При разработке PHP-файлы генерируются автоматически плагином Vite. В production это нужно выполнить вручную:

Bash
1
php artisan fusion:build
Эта команда анализирует все Vue SFC с PHP-блоками и генерирует соответствующие PHP-классы.

2. Настройка хостинга

Убедитесь, что директория storage/fusion доступна для записи веб-сервером, так как там хранятся сгенерированные файлы и SQLite база данных.

3. Оптимизация для production

Для улучшения производительности в production рекомендуется:
  • Кешировать маршруты: php artisan route:cache.
  • Оптимизировать автозагрузчик: composer install --optimize-autoloader --no-dev.
  • Собрать фронтенд для production: npm run build.

4. Миграция базы данных

Не забудьте выполнить миграции базы данных:
Bash
1
php artisan migrate --force
5. Конфигурация веб-сервера

Настройте веб-сервер так, чтобы все запросы, кроме запросов к существующим файлам, перенаправлялись на public/index.php, как в стандартном Laravel-приложении.

С этими настройками ваше Fusion-приложение будет готово к использованию в production-среде, обеспечивая плавное взаимодействие между PHP и Vue компонентами.

Преимущества и ограничения



Отделяя плюсы от минусов любой новой технологии, важно сохранять объективность. Fusion, как и любой инструмент, имеет свои сильные стороны и ограничения, которые нужно учитывать при выборе технологического стека. Давайте рассмотрим, в каких случаях Fusion блистает, а где может создать дополнительные сложности.

Производительность и оптимизация



В плане производительности Fusion находится примерно на том же уровне, что и стандартные Inertia-приложения. Поскольку Fusion фактически надстраивается над Inertia, базовая модель рендеринга остаётся прежней – серверный код генерирует данные, которые передаются на клиент для отрисовки с помощью Vue. Однако есть несколько нюансов:
1. Начальная загрузка – первый визит на страницу с Fusion может занимать чуть больше времени из-за дополнительной обработки PHP-блоков и генерации соответствующих классов. Этот эффект особенно заметен в режиме разработки, когда трансформация происходит на лету.
2. Кеширование трансформаций – Fusion использует хеширование PHP-кода для определения изменений, что позволяет избежать повторной трансформации неизменившихся компонентов. В production все трансформации выполняются заранее, что минимизирует задержки.
3. Размер бандла – JavaScript-шимы, генерируемые Fusion, добавляют некоторый объём к итоговому бандлу. Хотя это увеличение обычно несущественно, при большом количестве компонентов с PHP-блоками оно может стать заметным.
4. Сетевые запросы – Fusion оптимизирует взаимодействие клиента с сервером, особенно при использовании функции sync(). Вместо традиционного API с множеством эндпоинтов, Fusion использует унифицированный подход к обмену данными, что может сократить количество запросов.

При правильной реализации, Fusion-приложения могут демонстрировать отличную производительность. Особенно заметны преимущества при работе с формами и интерактивными элементами, где Fusion избавляет от необходимости создавать отдельные API-эндпоинты и дублировать логику валидации.

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Оптимизация запросов с использованием Fusion
$search = prop('');
$results = prop([]);
 
sync(function () use (&$search, &$results) {
    // Запрос выполняется только когда меняется значение $search
    if (strlen($search) >= 3) {
        $results = DB::table('products')
            ->where('name', 'like', "%{$search}%")
            ->limit(10)
            ->get();
    } else {
        $results = collect();
    }
});
В этом примере запрос к базе данных выполняется только при изменении поисковой строки, а не при каждом рендеринге компонента, что экономит ресурсы сервера.

Типизация и возможные проблемы



Одним из технических вызовов при работе с Fusion является обеспечение согласованной типизации между PHP и JavaScript частями приложения. В отличие от чистого TypeScript или PHP, где системы типов самодостаточны, Fusion требует координации между двумя разными мирами. Основные проблемы с типизацией:

1. Несоответствие типов – PHP и JavaScript имеют разные системы типов. Например, PHP различает целые числа и числа с плавающей точкой, тогда как в JavaScript есть только тип Number.
2. Сериализация объектов – При передаче сложных объектов из PHP в JavaScript может происходить потеря методов и прототипов. Объекты Eloquent превращаются в простые JSON-структуры.
3. Отсутствие статического анализа – Стандартные инструменты типизации не могут проверять связи между PHP и JavaScript кодом внутри компонентов Fusion.

Для смягчения этих проблем можно:
  • Использовать PHP-докблоки и JSDoc для документирования типов.
  • Явно указывать типы в PHP-функциях, экспортированных через expose().
  • Применять TypeScript в Vue-компонентах для строгой типизации клиентской части.
  • Создавать интерфейсы TypeScript, соответствующие структурам данных в PHP.

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<php>
/**
 * @return \App\Models\User[]
 */
$users = prop(fn () => User::all()->makeVisible(['api_token']));
 
/**
 * @param int $id ID пользователя
 * @return \App\Models\User|null
 */
expose(getUser: function (int $id) {
    return User::find($id);
});
</php>
 
<script setup lang="ts">
interface User {
  id: number;
  name: string;
  email: string;
  api_token: string;
}
 
// TypeScript понимает, что users — это массив объектов User
const selectedUser = ref<User | null>(null);
 
// Функция для выбора пользователя
function selectUser(id: number) {
  getUser({ id }).then(user => {
    selectedUser.value = user as User;
  });
}
</script>
Такой подход позволяет получить некоторые преимущества строгой типизации, хотя и не может полностью устранить разрыв между двумя языками.

Тестирование Fusion-компонентов: стратегии и инструменты



Тестирование приложений, использующих Fusion, представляет определённые сложности из-за гибридной природы компонентов. Традиционные подходы к тестированию не всегда работают, поскольку они ориентированы либо на PHP, либо на JavaScript, но не на их комбинацию. Эффективная стратегия тестирования Fusion-приложений включает несколько уровней:

1. Модульное тестирование PHP-кода

Серверную часть компонентов можно тестировать, используя стандартные инструменты Laravel, такие как PHPUnit. Для этого нужно изолировать бизнес-логику от кода, специфичного для Fusion:

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
// Вместо непосредственного размещения логики в блоке <php>
// мы создаём отдельный сервис
class ProductService
{
    public function searchProducts(string $query)
    {
        return Product::where('name', 'like', "%{$query}%")->get();
    }
}
 
// В блоке <php>
$productService = new ProductService();
$results = prop(fn () => $productService->searchProducts($search));
Теперь ProductService можно тестировать независимо от Fusion:

PHP
1
2
3
4
5
6
7
8
9
10
11
public function test_search_products()
{
    // Создаём тестовые продукты
    Product::factory()->create(['name' => 'Test Product']);
    
    $service = new ProductService();
    $results = $service->searchProducts('Test');
    
    $this->assertCount(1, $results);
    $this->assertEquals('Test Product', $results[0]->name);
}
2. Модульное тестирование Vue-компонентов

Клиентскую часть можно тестировать с помощью Vue Test Utils или Testing Library, имитируя данные и функции, которые обычно предоставляются Fusion:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { mount } from '@vue/test-utils'
import ProductSearch from './ProductSearch.vue'
 
test('отображает результаты поиска', async () => {
  // Мокаем функцию searchProducts, которая обычно предоставляется Fusion
  const searchProducts = vi.fn().mockResolvedValue([
    { id: 1, name: 'Test Product' }
  ]);
  
  const wrapper = mount(ProductSearch, {
    global: {
      provide: {
        // Имитируем данные, которые бы предоставил Fusion
        fusion: {
          state: { search: '', results: [] },
          actions: { searchProducts }
        }
      }
    }
  });
  
  // Вводим поисковый запрос
  await wrapper.find('input').setValue('Test');
  await wrapper.find('button').trigger('click');
  
  // Проверяем, что функция была вызвана
  expect(searchProducts).toHaveBeenCalledWith({ query: 'Test' });
  
  // Дожидаемся обновления компонента
  await wrapper.vm.$nextTick();
  
  // Проверяем отображение результатов
  expect(wrapper.text()).toContain('Test Product');
});
3. Интеграционное тестирование

Интеграционные тесты проверяют взаимодействие между PHP и JavaScript частями. Для этого можно использовать Laravel Dusk или Cypress, которые позволяют тестировать приложение в реальном браузере:

PHP
1
2
3
4
5
6
7
8
9
10
public function test_search_functionality()
{
    $this->browse(function (Browser $browser) {
        $browser->visit('/products')
                ->type('search', 'Test')
                ->press('Search')
                ->waitForText('Test Product')
                ->assertSee('Test Product');
    });
}
4. Снимки (snapshots)

Для компонентов с стабильным UI полезно использовать тесты на основе снимков, которые отлавливают неожиданные изменения в рендеринге:

JavaScript
1
2
3
4
5
6
7
8
9
test('рендерит корректно', () => {
  const wrapper = mount(ProductItem, {
    props: {
      product: { id: 1, name: 'Test Product', price: 100 }
    }
  });
  
  expect(wrapper.html()).toMatchSnapshot();
});

Кейс-стади: миграция существующего проекта на Fusion



Переход на новую технологию всегда сопряжён с определёнными рисками и затратами. Рассмотрим практический пример миграции существующего Laravel+Inertia проекта на Fusion, чтобы лучше понять реальные преимущества и сложности.

Компания "ФудТек" разрабатывала систему управления рестораном на основе Laravel и Inertia.js с Vue 3. Система включала модули для управления заказами, инвентаризации, бронирования столиков и аналитики. После выхода Fusion разработчики решили постепенно мигрировать наиболее проблемные части приложения.

Этап 1: Подготовка инфраструктуры

Первым шагом была интеграция Fusion в существующий проект без изменения функциональности:

Bash
1
2
composer require fusion-php/fusion
npm install @fusion-php/vue
Настройка Vite и регистрация сервис-провайдера позволили использовать Fusion параллельно с существующим кодом.

Этап 2: Пилотная миграция

Для начала выбрали модуль бронирования столиков — компонент с интенсивным взаимодействием между клиентом и сервером, требующий множества проверок и валидаций. До миграции архитектура включала:
  • Контроллер (300+ строк).
  • API-ресурсы (150+ строк).
  • Запросы валидации (100+ строк).
  • Vue-компоненты (500+ строк).

После миграции на Fusion вся эта функциональность уместилась в двух компонентах общим объёмом около 400 строк. Особенно заметно сократился код, связанный с валидацией и обменом данными.

Этап 3: Измерение результатов

Основные результаты:
  • Сокращение кодовой базы на 42% для мигрированных модулей.
  • Уменьшение времени на разработку новых функций на 30%.
  • Сокращение количества ошибок при валидации на 75%.
  • Производительность осталась на прежнем уровне.

Этап 4: Постепенная миграция

На основе успешного пилота компания решила постепенно мигрировать остальные модули, начиная с тех, где преимущества были бы наиболее заметны:
1. Формы создания и редактирования заказов.
2. Функциональность поиска и фильтрации.
3. Панель аналитики с интерактивными фильтрами.

Для внедрения разработали паттерн "Адаптер", который позволял постепенно переводить функциональность без нарушения работы системы.

Безопасность при использовании Fusion



При использовании Fusion возникает ряд специфических вопросов безопасности:

1. Прямой доступ к моделям Eloquent

Fusion позволяет напрямую работать с моделями в PHP-блоке, что потенциально может привести к уязвимостям:

TypeScript
1
2
3
4
5
<php>
expose(deleteUser: function (User $user) {
   $user->delete();  // Возможно нарушение авторизации!
});
</php>
Решение: Всегда проверяйте права доступа перед выполнением операций:

TypeScript
1
2
3
4
5
6
7
8
9
10
<php>
expose(deleteUser: function (User $user) {
   if (Gate::denies('delete', $user)) {
       abort(403);
   }
   
   $user->delete();
   return true;
});
</php>
2. Утечка конфиденциальных данных

При неправильном использовании prop() может произойти утечка конфиденциальных данных:

TypeScript
1
2
3
4
<php>
// Опасно! Передаём всю модель User
$user = prop(fn () => Auth::user());
</php>
Решение: Используйте явное указание атрибутов или ресурсы API:

TypeScript
1
2
3
4
5
6
7
<php>
// Безопасно - передаём только необходимые атрибуты
$user = prop(fn () => Auth::user()->only(['id', 'name', 'email']));
 
// Или используем API-ресурс
$user = prop(fn () => new UserResource(Auth::user()));
</php>
3. Валидация входных данных

Не забывайте валидировать все входные данные, даже если они приходят через безопасный интерфейс Fusion:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<php>
expose(updateSettings: function (array $settings) {
   // Обязательно валидируйте входные данные!
   $validator = Validator::make($settings, [
       'email' => 'email|nullable',
       'notification_type' => 'in:email,sms,none',
       // прочие правила
   ]);
   
   if ($validator->fails()) {
       return ['success' => false, 'errors' => $validator->errors()];
   }
   
   // дальнейшая обработка
});
</php>

Альтернативные решения



Fusion — не единственное решение для интеграции PHP и JavaScript. Альтернативами являются:

Заключение



Рассмотрев возможности и технические аспекты Fusion, можно сделать вывод, что эта технология представляет собой значительный шаг вперёд в интеграции PHP и JavaScript. Сравнивая Fusion с другими подходами к объединению фронтенда и бэкенда, отчётливо видны его преимущества в упрощении разработки и сокращении количества кода. Если говорить о производительности, Fusion демонстрирует результаты на уровне стандартных Inertia-приложений с незначительными отличиями. Первоначальная загрузка может занимать несколько больше времени из-за дополнительных преобразований, но последующие взаимодействия выигрывают от оптимизированного обмена данными. Исследования реальных внедрений показывают сокращение кодовой базы до 40% и ускорение разработки новой функциональности примерно на треть.

В сравнении с Livewire, который полностью основан на серверном рендеринге, Fusion сохраняет все преимущества клиентского рендеринга Vue, что делает его более гибким для создания сложных интерфейсов. По сравнению с чистым Inertia.js, Fusion устраняет необходимость создания отдельных контроллеров и API-эндпоинтов, упрощая архитектуру приложения.

Перспективы развития Fusion выглядят многообещающе. Судя по планам разработчиков, в будущих версиях можно ожидать:
  • Поддержку React наряду с Vue.
  • Расширение возможностей файловой маршрутизации.
  • Улучшение типизации между PHP и JavaScript.
  • Оптимизацию производительности для крупных приложений.
  • Интеграцию с другими PHP-фреймворками помимо Laravel.

Для разработчиков, рассматривающих внедрение Fusion, рекомендуется следующий подход:

1. Начинать с небольших, изолированных компонентов, чтобы освоить концепции и особенности технологии.
2. Сконцентрироваться на частях приложения с интенсивным взаимодействием между клиентом и сервером.
3. Выносить сложную бизнес-логику в отдельные сервисы для упрощения тестирования.
4. Уделять особое внимание вопросам безопасности, особенно при работе с моделями данных.
5. Применять инкрементальный подход при миграции существующих проектов.

Fusion вероятно найдёт свою нишу среди команд, разрабатывающих сложные веб-приложения на Laravel с современным интерактивным интерфейсом. При правильном применении эта технология способна существенно повысить продуктивность разработки, уменьшить когнитивную нагрузку на программистов и создать более целостный процесс разработки, объединяющий серверную и клиентскую части приложения.

VUE CLI (CLI-VUE-BABEL) не собирается в работающий проект
После сборки проекта, сам проект не работает (RAGE MP сервер не открывает браузер). Проблемы в...

PHP-Fusion 7
Отличная CMS система, но как добавить нового администратора? Зарегистрировался человек, но в...

Есть flash меню, как его подключить к PHP-Fusion
нужен скрипт подключения.... помогите пожалуйста, чет у меня не получается нифига...

Работа с движком Php Fusion 7
Здравствуйте. Интересует небольшой вопрос касательно движка php fusion 7. Сразу описывать всю...

Vue-router не отображает компонент по ссылке
Здравствуйте, столкнулся с определенной проблемой. Для перехода использую vue-router. Страницы...

Компонент Vue.js, который выводит две кнопки разного цвета с разными текстами, например, “Да”, “Нет”
Подскажите как правильно сделайте компонент Vue.js, который выводит две кнопки разного цвета с...

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

Не выводится тестовый компонент Vue
Я пытаюсь начать использовать Vue в проекте Laravel, но что-то идёт не так. app.blade.php ...

Как подключить во VUE компонент СВОЙ ! UI?
Привет. Хочу в компоненте VUE работать с привычными js файлами, где могу писать...

Vue компонент срабатывает сам по себе?
Доброго всем времени суток! Заметил что если использовать Option API и в теге script компонента,...

Vue-router загружает компонент другого роута при использовании Memory mode
Здравствуйте. Создал сборку vite + vue3 + vuetify3 + nodejs, для реализации SSR. При настройке...

SFC: гарантированный выход за границу родительского компонента
Есть некий vue-компонент, который может применяться в любом непредсказуемом окружении (т.е. внутри...

Метки fusion, php, vue
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Символьное дифференцирование
igorrr37 13.02.2026
/ * Логарифм записывается как: (x-2)log(x^2+2) - означает логарифм (x^2+2) по основанию (x-2). Унарный минус обозначается как ! */ #include <iostream> #include <stack> #include <cctype>. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru