|
| 1 | +# Advanced Optimization |
| 2 | + |
| 3 | +These optimizations (memoization and folding) are more powerful but carry risk — they can affect component behavior if applied incorrectly and should be thoroughly tested. |
| 4 | + |
| 5 | +## Option A: Trace-Based (preferred) |
| 6 | + |
| 7 | +### 1. Collect trace data |
| 8 | + |
| 9 | +Ask the user to visit pages in their browser, making sure to include the slow pages they want to optimize. `BLAZE_DEBUG=true` is already enabled from Phase 1, so trace data will be collected automatically. |
| 10 | + |
| 11 | +Wait for the user to confirm they are done browsing before proceeding. |
| 12 | + |
| 13 | +### 2. Analyze traces |
| 14 | + |
| 15 | +Run: |
| 16 | + |
| 17 | +```bash |
| 18 | +php artisan blaze:trace:list |
| 19 | +``` |
| 20 | + |
| 21 | +This shows pages the user visited with their load times. Identify the slowest pages, then drill into them: |
| 22 | + |
| 23 | +```bash |
| 24 | +php artisan blaze:trace:show [id] |
| 25 | +``` |
| 26 | + |
| 27 | +This shows the slowest components for that page and how many were rendered. |
| 28 | + |
| 29 | +### 3. Optimize slow components |
| 30 | + |
| 31 | +Be THOROUGH and PROACTIVE. Analyze EVERY high-volume component from the trace data — do NOT dismiss components without reading them first. If a component is slow and has high render count, read it and determine what optimization applies. Apply optimizations immediately — do not ask the user for permission on each one. |
| 32 | + |
| 33 | +For each slow component identified in the trace data: |
| 34 | + |
| 35 | +1. Read the component file |
| 36 | +2. Evaluate it against BOTH memoization and folding — they are not mutually exclusive buckets. A component may fail one but qualify for the other. |
| 37 | +3. When applying folding, ALWAYS evaluate every prop, slot, and attribute for safe/unsafe. Never apply bare `@blaze(fold: true)` without checking. |
| 38 | + |
| 39 | +**Memoization** — the MOST effective optimization for high-volume components. Use when: |
| 40 | +- Has no slots |
| 41 | +- Has no global state access |
| 42 | +- Is rendered many times with the same props (icons, avatars, badges) |
| 43 | + |
| 44 | +IMPORTANT: If a high-volume component has a simple slot that only outputs text (e.g. `{{ $slot }}`), it is almost certainly better as a prop. Flag it for refactoring — converting the slot to a prop (like `label` or `text`) makes it memoizable, which is far more effective than folding at high render counts. See the "Suggest Refactors" section below. |
| 45 | + |
| 46 | +Apply via service provider (preferred for entire directories like `icons/`): |
| 47 | + |
| 48 | +```php |
| 49 | +Blaze::optimize() |
| 50 | + ->in(resource_path('views/components')) |
| 51 | + ->in(resource_path('views/components/icons'), memo: true); |
| 52 | +``` |
| 53 | + |
| 54 | +Or via directive (individual components): |
| 55 | + |
| 56 | +```blade |
| 57 | +@blaze(memo: true) |
| 58 | +``` |
| 59 | + |
| 60 | +**Folding** — use when the component: |
| 61 | +- Has no global state access (or state is isolated with `@unblaze`) |
| 62 | +- Is rendered frequently, especially in loops |
| 63 | + |
| 64 | +Components with slots CAN be folded — slots are pass-through by default. Only mark a slot `unsafe` if the component inspects it. |
| 65 | + |
| 66 | +For each folding candidate: |
| 67 | + |
| 68 | +1. Check for global state patterns — if ANY are found inside the component, do NOT fold. See [folding-safety.md](folding-safety.md) for the complete list. |
| 69 | +2. Classify EVERY `@props` value individually: |
| 70 | + - Pass-through (output directly in HTML, class strings, attributes) → mark `safe` |
| 71 | + - Used in logic (match, if/else, switch, ternary with multiple branches) → leave as default (auto-abort when dynamic) |
| 72 | +3. Check if slots are inspected (`$slot->hasActualContent()`, `$slot->isEmpty()`, `$slot->isNotEmpty()`) → mark as `unsafe`. If slots are just output (`{{ $slot }}`), they are pass-through and need no annotation. |
| 73 | +4. Check if `$attributes->get()` is used in logic → mark `attributes` as `unsafe` |
| 74 | +5. Add the directive with the CORRECT safe/unsafe parameters |
| 75 | + |
| 76 | +```blade |
| 77 | +@blaze(fold: true) |
| 78 | +@blaze(fold: true, safe: ['level']) |
| 79 | +@blaze(fold: true, unsafe: ['slot']) |
| 80 | +@blaze(fold: true, unsafe: ['attributes']) |
| 81 | +``` |
| 82 | + |
| 83 | +For components that are mostly foldable but have a small section using global state, use `@unblaze`: |
| 84 | + |
| 85 | +```blade |
| 86 | +@unblaze(scope: ['name' => $name]) |
| 87 | + @error($scope['name']) |
| 88 | + <p class="text-red-500">{{ $message }}</p> |
| 89 | + @enderror |
| 90 | +@endunblaze |
| 91 | +``` |
| 92 | + |
| 93 | +Note: Variables from the component scope must be passed explicitly via the `scope` parameter. |
| 94 | + |
| 95 | +## Option B: Code Analysis (fallback) |
| 96 | + |
| 97 | +Warn the user that this approach is token-intensive and the optimizations are speculative compared to trace-based analysis. |
| 98 | + |
| 99 | +Use a subagent to perform a full component audit. The subagent should use Glob and Grep extensively — do not read files line by line. For projects with 200+ components, use grep-based sampling. |
| 100 | + |
| 101 | +The subagent should return a structured report covering: |
| 102 | + |
| 103 | +1. **Component inventory** — For each component under `resources/views/components/`, grep for: `@props`, `{{ $slot }}` / named slots, slot inspection patterns (`$slot->hasActualContent()`, `$slot->isEmpty()`, `$slot->isNotEmpty()`), global state patterns (`auth()`, `@auth`, `@guest`, `Auth::`, `session(`, `Session::`, `request()`, `Request::`, `$errors`, `@error`, `now()`, `Carbon::`, `@csrf`, `csrf_`, `url()`, `route(`, `DB::`, `::where(`, `::find(`, `::first(`, `::get()`, `::all()`, `::count(`, `config(`, `cache(`) |
| 104 | + |
| 105 | +2. **Usage analysis** — Grep all views for `<x-` tags. Count per component. List top 20 most-used. |
| 106 | + |
| 107 | +3. **Loop detection** — Find `<x-` tags inside `@foreach`, `@for`, `@forelse`, `@while` blocks. |
| 108 | + |
| 109 | +4. **Nesting analysis** — Which components use other `<x-` components internally. |
| 110 | + |
| 111 | +Report format: |
| 112 | + |
| 113 | +``` |
| 114 | +COMPONENT AUDIT |
| 115 | +=============== |
| 116 | +Total components: [N] |
| 117 | +
|
| 118 | +ALL COMPONENTS: |
| 119 | +- [path] — uses: [N], slots: [y/n], inspects-slots: [y/n], global-state: [y/n], loop: [y/n], props: [list] |
| 120 | +
|
| 121 | +MEMOIZATION CANDIDATES: |
| 122 | +- [name] — [count] uses, no slots, no global state |
| 123 | +
|
| 124 | +FOLDING CANDIDATES: |
| 125 | +- [name] — [count] uses, in [N] loops, slots: [y/n], inspects-slots: [y/n], props: [list], global-state: none |
| 126 | +
|
| 127 | +GLOBAL STATE (cannot fold): |
| 128 | +- [name] — uses: [patterns found] |
| 129 | +
|
| 130 | +NESTING CHAINS: |
| 131 | +- [parent] -> [child] -> [grandchild] |
| 132 | +``` |
| 133 | + |
| 134 | +Apply memoization and folding using the same rules as Option A. |
| 135 | + |
| 136 | +## Suggest Refactors |
| 137 | + |
| 138 | +After applying optimizations, identify components that are close to qualifying for a higher optimization level but are blocked by a small issue. Present these refactors to the user and APPLY them unless the user declines. Do not just list them and wait — tell the user what you're going to do and do it. |
| 139 | + |
| 140 | +Only suggest refactors that are straightforward and have a clear payoff. Do not refactor components that are rarely used or where compile is already sufficient. |
| 141 | + |
| 142 | +**Slots blocking memoization** — High-volume components rendered many times with the same props but using a simple slot that could be a prop instead. Refactor the component to accept a `label`/`text` prop, update ALL callers, then switch from fold/compile to memo: |
| 143 | + |
| 144 | +```blade |
| 145 | +{{-- Before: has slot, cannot memoize --}} |
| 146 | +<x-badge color="green">Active</x-badge> |
| 147 | +
|
| 148 | +{{-- After: slot replaced with prop, can memoize --}} |
| 149 | +<x-badge color="green" label="Active" /> |
| 150 | +``` |
| 151 | + |
| 152 | +When applying this refactor: grep for ALL callers, update every one, update the component, and change the strategy to `memo: true`. |
| 153 | + |
| 154 | +**Global state blocking folding** — Components used in loops that would be good folding candidates, but contain a section that accesses global state. Extract the stateful part with `@unblaze`: |
| 155 | + |
| 156 | +```blade |
| 157 | +{{-- Before: $errors blocks folding --}} |
| 158 | +<div> |
| 159 | + <label>{{ $label }}</label> |
| 160 | + <input name="{{ $name }}"> |
| 161 | + @error($name) <p>{{ $message }}</p> @enderror |
| 162 | +</div> |
| 163 | +
|
| 164 | +{{-- After: stateful section extracted with @unblaze --}} |
| 165 | +@blaze(fold: true) |
| 166 | +<div> |
| 167 | + <label>{{ $label }}</label> |
| 168 | + <input name="{{ $name }}"> |
| 169 | + @unblaze(scope: ['name' => $name]) |
| 170 | + @error($scope['name']) <p>{{ $message }}</p> @enderror |
| 171 | + @endunblaze |
| 172 | +</div> |
| 173 | +``` |
| 174 | + |
| 175 | +## Verification |
| 176 | + |
| 177 | +After applying any advanced optimizations: |
| 178 | + |
| 179 | +1. Run `php artisan view:clear` |
| 180 | +2. Test the application — verify optimized components render correctly |
| 181 | +3. If using trace-based optimization, ask the user to revisit the same pages and re-run `php artisan blaze:trace:list` to compare render times |
| 182 | +4. If a component breaks, remove its `@blaze` directive or add `compile: false` |
| 183 | + |
| 184 | +## Common Pitfalls |
| 185 | + |
| 186 | +- Memoizing components that have slots — memoization requires slot-free components |
| 187 | +- Folding components that access global state (auth, session, request, errors, time, CSRF) — produces stale HTML |
| 188 | +- Folding components where props drive internal logic without marking them `safe` |
| 189 | +- Forgetting to run `php artisan view:clear` after changes |
0 commit comments