NativePHP тепер рендерить Blade-компоненти як справжні SwiftUI елементи на iOS та Jetpack Compose на Android, без використання web view чи HTML. NativePHP називає це SuperNative, і Simon Hamp та Shane Rosenthal презентували цю технологію на The Vibes - заході на 100 осіб у Бостоні 30 липня, наступного дня після завершення Laracon US.
Якщо ви оцінювали NativePHP рік тому і вважали його просто Laravel у web view, версія 4 варта повторного огляду. Web view все ще працюють і не потребують міграції, але вони більше не є єдиним способом побудови екранів. SuperNative є типовим підходом у v4 і станом на версію 4.1 все ще перебуває в бета-стадії, тому документація застерігає про швидкі зміни.
Ключові можливості
Нативний UI з Blade. Набір компонентів називається EDGE (Element Definition and Generation Engine) і компілюється у SwiftUI та Jetpack Compose замість HTML.
Без мережевих запитів. PHP та нативний шар ділять пам'ять безпосередньо, тому немає круговороту запитів та web view bridge між компонентом та екраном.
Компоненти у стилі Livewire. Публічні властивості, метод mount(), екшн-методи та атрибути включно з #[Poll], #[Computed], #[Lazy] та #[Locked].
Тестовий набір Pest для нативних екранів. Тести монтують компонент у процесі та перевіряють опубліковані дані, тому вони працюють у CI без симулятора.
Чотири плагіни інтегровані у ядро. Device, Dialog, File та System тепер постачаються з nativephp/mobile, що є єдиною критичною зміною у релізі.
Як працює SuperNative
SuperNative не є повністю кастомним рендерером як Skia чи Impeller, і це не ще одна віртуальна машина поверх або поруч з PHP. Це також не транспілер чи конвертер HTML-у-нативний код.
З архітектурної документації NativePHP:
Ми створили власний Blade-движок, який перетворює справжні Blade-компоненти у просте бінарне представлення замість HTML.
Це представлення є масивом байтів фіксованої довжини, і інтерпретатор на нативній стороні читає його та будує SwiftUI і Compose елементи.
SuperNative екран запускається швидше за web view екран. Немає web view для завантаження, немає bundle для парсингу та немає серіалізації через bridge при кожному натисканні.
Доступність зазвичай є слабким місцем у web view додатках. Оскільки це справжні SwiftUI та Compose елементи, VoiceOver, TalkBack, динамічний шрифт та платформні засоби доступності працюють за замовчуванням. Контроли лише з іконками все ще потребують явного a11y-label.
Написання SuperNative екрану
У v3 екран був web-маршрутом. Ви писали Blade, Livewire або Inertia; це рендерилось як HTML у web view, а EDGE обгортав це у нативне хром: справжню верхню панель, нижню навігацію або floating action button, оголошені у Blade. Не було PHP-класу для самого екрану.
У v4 екран - це компонент. Це PHP-клас, що розширює NativeComponent, та Blade-view. Публічні властивості зберігають стан, публічні методи - це дії, які викликає ваш view, а атрибути як #[Poll] обробляють повторювані завдання. Цей трекер доставки опитує зміни статусу:
<?php
namespace App\NativeComponents;
use App\Models\Delivery;
use Illuminate\View\View;
use Native\Mobile\Attributes\Locked;
use Native\Mobile\Attributes\Poll;
use Native\Mobile\Edge\NativeComponent;
class DeliveryTracker extends NativeComponent
{
#[Locked]
public int $deliveryId;
public string $status = 'awaiting_pickup';
public ?string $courier = null;
public function mount(): void
{
$this->syncFromDatabase();
}
#[Poll(5000)]
public function syncFromDatabase(): void
{
$delivery = Delivery::findOrFail($this->deliveryId);
$this->status = $delivery->status;
$this->courier = $delivery->courier_name;
}
public function confirmReceipt(): void
{
Delivery::findOrFail($this->deliveryId)->markReceived();
$this->syncFromDatabase();
}
public function render(): View
{
return view('native.delivery-tracker');
}
}
Маршрути знаходяться у routes/mobile.php, зареєстровані з макросом Route::native, який приймає клас компонента замість контролера. Нативне хром походить від класу layout, який ви пишете, розширюючи NativeLayout, прикріплений через ->layout(), а Route::nativeGroup застосовує один layout до кількох маршрутів:
Route::native('/deliveries/{deliveryId}', DeliveryTracker::class)
->layout(DeliveryLayout::class)
->name('deliveries.show');
View - це Blade, але елементи є нативними примітивами замість HTML-тегів, і вони стилізовані класами Tailwind, які парсер зіставляє з платформним layout:
<column class="flex-1 p-6 gap-4 bg-theme-background safe-area">
<text class="text-2xl font-bold text-theme-on-background">
{{ str($status)->headline() }}
</text>
@if ($courier)
<text class="text-sm text-gray-500">Courier: {{ $courier }}</text>
@endif
<pressable @tap="confirmReceipt" class="px-6 py-4 rounded bg-theme-primary items-center">
<text class="text-theme-on-primary font-semibold">Confirm receipt</text>
</pressable>
</column>
<column> стає справжнім SwiftUI layout на iOS та Compose Column на Android. Обробник @tap викликає метод вашого PHP-класу безпосередньо. v4 також додав @pressDown та @pressUp для подій натискання та відпускання, що необхідно для поведінки утримування натискання замість одиночного тапу.
Запуск цього екрану на телефоні не вимагає Xcode чи Android Studio. php artisan native:jump запускає dev-сервер та виводить QR-код, і сканування його за допомогою Jump - безплатного додатка-компаньйона в App Store та Google Play - завантажує ваш додаток на пристрій через Wi-Fi. Нативні виклики ретранслюються назад до PHP, що працює на вашій машині, тому камера та біометрія поводяться так само, як у упакованій збірці. Коли NATIVEPHP_START_URL вказує на Route::native екран, Jump рендерить нативний UI замість web view, що робить його найшвидшим способом побачити SuperNative на реальному обладнанні.
Збереження Web View
Існуючий додаток v3 не потребує переписування. Web view тепер є компонентом, який ви розміщуєте всередині нативного екрану замість усього додатку:
<webview php url="/" fullscreen />
Вкажіть нативний маршрут на екран, що містить цей елемент, встановіть NATIVEPHP_START_URL=/home у вашому .env, і ваші routes/web.php view продовжать рендеритись як раніше. Ви можете поєднувати обидва підходи: використовувати нативну навігацію навколо web view для екрану, який ви ще не конвертували. Кожен вбудований php-режим web view отримує власний виділений PHP runtime, тому він ніколи не конкурує з runtime нативного екрану.
v4 також більше не завантажує web view, поки web-маршрут фактично не рендериться.
Тестування нативних екранів у Pest
Оскільки екран є PHP-об'єктом, що публікує дерево, ви можете тестувати його без пристрою. Набір надає FakeBridge, який захоплює кожне дерево, опубліковане компонентом, та кожен нативний виклик, а тести - це звичайний Pest:
use App\NativeComponents\DeliveryTracker;
use Native\Mobile\Testing\Native;
it('confirms receipt of a delivery', function () {
$delivery = Delivery::factory()->create(['status' => 'out_for_delivery']);
Native::visit("/deliveries/{$delivery->id}")
->assertSee('Out For Delivery')
->tap('Confirm receipt')
->assertSet('status', 'received');
});
Native::test() монтує клас компонента безпосередньо, Native::visit() проходить через зареєстровані нативні маршрути та розв'язує параметри маршруту, а tap() натискає pressable, що відповідає заданому ref або видимому тексту, перед повторним рендерингом. php artisan native:make-test DeliveryTracker створює каркас файлу. Ви можете додати власні перевірки з макросами FakeBridge для тестування нативних викликів плагіна.
Версія 4.1
Версія 4.1 вийшла 7 серпня. #[Locked] позначає властивість, до якої двостороннє зв'язування не може писати. Помилка native:model="deliveryId" викине виняток замість того, щоб дозволити текстовому полю перезаписати запис на екрані.
TreeObservers дозволяють спостерігати за деревами елементів, які публікує runtime, що потрібно для інструментів запису сесій та налагодження. Той самий реліз постачає утиліту тестування TreeSpy, побудовану на цьому хуку, щоб тест міг перевіряти проміжні фрейми, а не лише фінальний стан.
Два менші доповнення: NativeRouteFallback встановлює, що бачить браузер при переході на маршрут лише для нативного режиму, а парсер Tailwind тепер попереджає про класи, які він не підтримує, замість того, щоб ігнорувати їх.
Оновлення з v3
Більша частина v4 є адитивною, і керівництво з оновлення каже, що зміни коду додатку не потрібні. Єдина критична зміна стосується залежностей. Device, Dialog, File та System тепер є основними компонентами, і nativephp/mobile оголошує конфлікти Composer з чотирма окремими пакетами плагінів, тому Composer не розв'яже залежності, поки вони не будуть видалені:
php artisan native:plugin:uninstall --core-v4
Це видаляє всі чотири та скасовує їх реєстрацію у вашому NativeServiceProvider. Додайте --force, щоб пропустити підтвердження. Потім оновіть обмеження до ~4.0.0 та перебудуйте файли нативного проєкту:
composer update
php artisan native:install --force
Facades та події залишаються незмінними, тому ваші існуючі виклики Dialog::alert() продовжують працювати. Vite dev-сервер тепер є опціональним: передайте --vite до native:watch або native:run, якщо ви хочете його, і видаліть прапорці --no-vite зі своїх скриптів. Автори плагінів повинні розширити своє обмеження до ^3.0|^4.0. Пакет також постачає навичку оновлення v3-до-v4 серед своїх вбудованих навичок Boost, якщо ви віддаєте перевагу, щоб агент виконав оновлення за вас.
Висновок
Звичайне заперечення проти NativePHP полягає в тому, що web view у нативній оболонці - це не нативний додаток. Для екранів, які ви вирішите конвертувати, v4 відповідає на це, і робить це без необхідності вивчати Swift чи Kotlin. Ваш компонент - це PHP-клас, ваш шаблон - це Blade, а ваші тести - це Pest.
Ви також можете конвертувати по одному екрану за раз. Оскільки <webview> є компонентом, а не всім додатком, команда може перемістити екран, де продуктивність прокрутки чи доступність є проблемою, і залишити решту як є.
Повний changelog та довідник компонентів EDGE доступні на GitHub та у документації v4.