Some checks failed
Deploy Application / deploy (push) Has been cancelled
309 lines
8.0 KiB
Markdown
309 lines
8.0 KiB
Markdown
# Island Directive
|
|
|
|
**Isolated Component Rendering** - Render heavy components separately for better performance.
|
|
|
|
## Overview
|
|
|
|
The `#[Island]` directive enables isolated rendering of LiveComponents, allowing them to be rendered separately from the main template flow. This is particularly useful for resource-intensive components that can slow down page rendering.
|
|
|
|
## Features
|
|
|
|
- **Isolated Rendering**: Components are rendered separately without template wrapper
|
|
- **Lazy Loading**: Optional lazy loading when component enters viewport
|
|
- **Independent Caching**: Islands can have their own caching strategies
|
|
- **Isolated Events**: Islands don't receive parent component events
|
|
|
|
## Basic Usage
|
|
|
|
### Simple Island Component
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\Attributes\LiveComponent;
|
|
use App\Framework\LiveComponents\Attributes\Island;
|
|
|
|
#[LiveComponent('heavy-widget')]
|
|
#[Island]
|
|
final readonly class HeavyWidgetComponent implements LiveComponentContract
|
|
{
|
|
// Component implementation
|
|
}
|
|
```
|
|
|
|
### Lazy Island Component
|
|
|
|
```php
|
|
#[LiveComponent('metrics-dashboard')]
|
|
#[Island(isolated: true, lazy: true, placeholder: 'Loading dashboard...')]
|
|
final readonly class MetricsDashboardComponent implements LiveComponentContract
|
|
{
|
|
// Component implementation
|
|
}
|
|
```
|
|
|
|
## Configuration Options
|
|
|
|
### `isolated` (bool, default: `true`)
|
|
|
|
Whether the component should be rendered in isolation. When `true`, the component is rendered via the `/island` endpoint without template wrapper.
|
|
|
|
```php
|
|
#[Island(isolated: true)] // Isolated rendering (default)
|
|
#[Island(isolated: false)] // Normal rendering (not recommended)
|
|
```
|
|
|
|
### `lazy` (bool, default: `false`)
|
|
|
|
Whether the component should be lazy-loaded when entering the viewport. When `true`, a placeholder is generated and the component is loaded via IntersectionObserver.
|
|
|
|
```php
|
|
#[Island(lazy: true)] // Lazy load on viewport entry
|
|
#[Island(lazy: false)] // Load immediately (default)
|
|
```
|
|
|
|
### `placeholder` (string|null, default: `null`)
|
|
|
|
Custom placeholder text shown while the component is loading (only used when `lazy: true`).
|
|
|
|
```php
|
|
#[Island(lazy: true, placeholder: 'Loading widget...')]
|
|
```
|
|
|
|
## Template Usage
|
|
|
|
### In Templates
|
|
|
|
Island components are used the same way as regular LiveComponents:
|
|
|
|
```html
|
|
<x-heavy-widget id="user-123" />
|
|
```
|
|
|
|
When processed, lazy Islands generate a placeholder:
|
|
|
|
```html
|
|
<div data-island-component="true"
|
|
data-live-component-lazy="heavy-widget:user-123"
|
|
data-lazy-priority="normal"
|
|
data-lazy-threshold="0.1">
|
|
<div class="island-placeholder">Loading widget...</div>
|
|
</div>
|
|
```
|
|
|
|
## Island vs. Lazy Component
|
|
|
|
### Lazy Component (`data-live-component-lazy`)
|
|
|
|
- Loaded when entering viewport
|
|
- Part of normal template flow
|
|
- Shares state/events with parent components
|
|
- Uses `/lazy-load` endpoint
|
|
|
|
### Island Component (`data-island-component`)
|
|
|
|
- Rendered in isolation (separate request)
|
|
- No template wrapper (no layout/meta)
|
|
- Isolated event context (no parent events)
|
|
- Optional lazy loading on viewport entry
|
|
- Uses `/island` endpoint
|
|
|
|
## Performance Benefits
|
|
|
|
### Isolation
|
|
|
|
Islands reduce template processing overhead for heavy components by rendering them separately. This means:
|
|
|
|
- Heavy components don't block main page rendering
|
|
- Independent error handling (one island failure doesn't affect others)
|
|
- Separate caching strategies per island
|
|
|
|
### Lazy Loading
|
|
|
|
When combined with lazy loading, Islands provide:
|
|
|
|
- Reduced initial page load time
|
|
- Faster Time to Interactive (TTI)
|
|
- Better Core Web Vitals scores
|
|
|
|
### Example Performance Impact
|
|
|
|
```php
|
|
// Without Island: 500ms page load (heavy component blocks rendering)
|
|
// With Island: 200ms page load + 300ms island load (non-blocking)
|
|
```
|
|
|
|
## Use Cases
|
|
|
|
### Heavy Widgets
|
|
|
|
```php
|
|
#[LiveComponent('analytics-dashboard')]
|
|
#[Island(isolated: true, lazy: true, placeholder: 'Loading analytics...')]
|
|
final readonly class AnalyticsDashboardComponent implements LiveComponentContract
|
|
{
|
|
// Heavy component with complex data processing
|
|
}
|
|
```
|
|
|
|
### Third-Party Integrations
|
|
|
|
```php
|
|
#[LiveComponent('external-map')]
|
|
#[Island(isolated: true, lazy: true)]
|
|
final readonly class ExternalMapComponent implements LiveComponentContract
|
|
{
|
|
// Component that loads external resources
|
|
}
|
|
```
|
|
|
|
### Conditional Components
|
|
|
|
```php
|
|
#[LiveComponent('admin-panel')]
|
|
#[Island(isolated: true)]
|
|
final readonly class AdminPanelComponent implements LiveComponentContract
|
|
{
|
|
// Component only visible to admins
|
|
// Isolated to prevent template processing for non-admin users
|
|
}
|
|
```
|
|
|
|
## Technical Details
|
|
|
|
### Endpoint
|
|
|
|
Islands are rendered via:
|
|
|
|
```
|
|
GET /live-component/{id}/island
|
|
```
|
|
|
|
This endpoint:
|
|
- Renders component HTML without template wrapper
|
|
- Returns JSON with `html`, `state`, `csrf_token`
|
|
- Uses `ComponentRegistry.render()` directly (no wrapper)
|
|
|
|
### Frontend Integration
|
|
|
|
Islands are automatically detected and handled by `LazyComponentLoader`:
|
|
|
|
- Lazy islands: Loaded when entering viewport
|
|
- Non-lazy islands: Loaded immediately
|
|
- Isolated initialization: Islands don't receive parent events
|
|
|
|
### Event Isolation
|
|
|
|
Islands have isolated event contexts:
|
|
|
|
- No parent component events
|
|
- No shared state with parent components
|
|
- Independent SSE channels (if configured)
|
|
|
|
## Best Practices
|
|
|
|
1. **Use Islands for Heavy Components**: Only use `#[Island]` for components that significantly impact page load time
|
|
|
|
2. **Combine with Lazy Loading**: Use `lazy: true` for below-the-fold content
|
|
|
|
3. **Provide Placeholders**: Always provide meaningful placeholder text for better UX
|
|
|
|
4. **Test Isolation**: Verify that islands work correctly in isolation
|
|
|
|
5. **Monitor Performance**: Track island load times and optimize as needed
|
|
|
|
## Examples
|
|
|
|
### Complete Example
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Application\LiveComponents\Dashboard;
|
|
|
|
use App\Framework\LiveComponents\Attributes\LiveComponent;
|
|
use App\Framework\LiveComponents\Attributes\Island;
|
|
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentState;
|
|
|
|
#[LiveComponent('metrics-dashboard')]
|
|
#[Island(isolated: true, lazy: true, placeholder: 'Loading metrics...')]
|
|
final readonly class MetricsDashboardComponent implements LiveComponentContract
|
|
{
|
|
public function __construct(
|
|
public ComponentId $id,
|
|
public ComponentState $state
|
|
) {
|
|
}
|
|
|
|
public function getRenderData(): ComponentRenderData
|
|
{
|
|
return new ComponentRenderData(
|
|
templatePath: 'livecomponent-metrics-dashboard',
|
|
data: [
|
|
'componentId' => $this->id->toString(),
|
|
'metrics' => $this->loadMetrics(),
|
|
]
|
|
);
|
|
}
|
|
|
|
private function loadMetrics(): array
|
|
{
|
|
// Heavy operation - isolated rendering prevents blocking
|
|
return [
|
|
'users' => 12345,
|
|
'orders' => 6789,
|
|
'revenue' => 123456.78,
|
|
];
|
|
}
|
|
}
|
|
```
|
|
|
|
## Migration Guide
|
|
|
|
### Converting Regular Component to Island
|
|
|
|
1. Add `#[Island]` attribute to component class
|
|
2. Test component rendering (should work identically)
|
|
3. Optionally enable lazy loading with `lazy: true`
|
|
4. Monitor performance improvements
|
|
|
|
### Backward Compatibility
|
|
|
|
- Components without `#[Island]` work exactly as before
|
|
- `#[Island]` is opt-in (no breaking changes)
|
|
- Existing lazy components continue to work
|
|
|
|
## Troubleshooting
|
|
|
|
### Island Not Loading
|
|
|
|
- Check browser console for errors
|
|
- Verify component is registered in ComponentRegistry
|
|
- Ensure `/island` endpoint is accessible
|
|
|
|
### Placeholder Not Showing
|
|
|
|
- Verify `lazy: true` is set
|
|
- Check `placeholder` parameter is provided
|
|
- Inspect generated HTML for `data-island-component` attribute
|
|
|
|
### Events Not Working
|
|
|
|
- Islands have isolated event contexts
|
|
- Use component-specific events, not parent events
|
|
- Check SSE channel configuration if using real-time updates
|
|
|
|
## See Also
|
|
|
|
- [Lazy Loading Guide](livecomponents-lazy-loading.md)
|
|
- [Component Caching](livecomponents-caching.md)
|
|
- [Performance Optimization](livecomponents-performance.md)
|
|
|
|
|
|
|
|
|
|
|