Skip to content

Flux Capacitor: A runtime attribute cache for Flux components #1498

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 46 commits into
base: main
Choose a base branch
from

Conversation

JohnathonKoster
Copy link

@JohnathonKoster JohnathonKoster commented Apr 15, 2025

This PR introduces an alternative compiler for Flux. This new compiler is largely the same as the existing FluxTagCompiler,
but uses custom Flux component directives in order to inject cache hooks into the compiled output.

The new compiler will emit the cache hooks for all components even though the cache system is opt-in per component. This is to enable simpler caching of delegate components, as well as prevent the need to parse/analyze components while compiling the main Blade views.

The following annotated output provides a high-level overview of the steps the new compiled output performs:

<?php if (isset($component)) { $__componentOriginal = $component; } ?>
<?php if (isset($attributes)) { $__attributesOriginal = $attributes; } ?>
<?php $component = Illuminate\View\AnonymousComponent::resolve(['view' => 'e60dd9d2c3a62d619c9acb38f20d5aa5::select.option.variants.default','data' => []] ?? [] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>
<?php $component->withName('flux::select.option.variants.default'); ?>
<?php if ($component->shouldRender()): ?>
<?php $__env->startComponent($component->resolveView(), $component->data()); ?>
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag): ?>
<?php $attributes = $attributes->except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>
<?php endif; ?>
<?php $component->withAttributes([]); ?>The Slot Content <?php
    if (isset($__fluxCacheKey)) { $__fluxCacheKeyOriginal = $__fluxCacheKey; }
    $__componentRenderData = $__env->fluxComponentData();

    // Compute the cache key for this component before rendering. If we have never seen this component
    // before, or if caching is _not_ enabled, this will always return null. The initial cache key
    // for the component will always be calculated _after_ the first the component is rendered.
    $__fluxCacheKey = \Flux\Flux::cache()->key($component->componentName, $__componentRenderData, $__env);
    if ($__fluxCacheKey === null || \Flux\Flux::cache()->has($__fluxCacheKey) === false): /* START: CACHE BLOCK */
        // We have not seen this component before, or the cache is not enabled.

        // We start "observing" this component. This will provide us the
        // opportunity to detect when a component has the cache enabled
        // at runtime, as well as provides a way for us to watch for
        // any "@aware" variables that may be used when exected.
        \Flux\Flux::cache()->startObserving($component->componentName);
        $__fluxTmpOutput = $__env->renderFluxComponent($__componentRenderData);
        \Flux\Flux::cache()->stopObserving($component->componentName);

        // After the initial render, we need to re-compute the cache key.
        // If the cache is enabled, we will now have a value for the
        // unique set of attributes passed to the component.
        $__fluxCacheKey = \Flux\Flux::cache()->key($component->componentName, $__componentRenderData, $__env);
        
        if ($__fluxCacheKey !== null) {
            // If we have the cache key, stash away the output
            // so we can use it later when the component is
            // rendered again with the same attributes.
            \Flux\Flux::cache()->put($component->componentName, $__fluxCacheKey, $__fluxTmpOutput);


            // The output may have "swap" string placeholders, which are inserted automatically
            // whenever the component compiler sees @uncached/@enduncached directive pairs or
            // the <flux:uncached></flux:uncached> compile-time component.
            $__fluxTmpOutput = \Flux\Flux::cache()->swap(
                $component->componentName,
                $__fluxTmpOutput,
                \Flux\Flux::cache()->runComponentSetup($component->componentName, $__componentRenderData)
            );
        }

        echo $__fluxTmpOutput;
        unset($__fluxTmpOutput);
    
    else: /* ELSE: CACHE BLOCK */
        // If we are in this block, the component was cached! Hooray!
        // The first thing we do is remove this component from the
        // Blade compiler's component stack to clean up a bit.
        $__env->popFluxComponent();

        // Fetch the cached output and perform the swaps.
        $__fluxTmpOutput = \Flux\Flux::cache()->swap(
            $component->componentName,
            \Flux\Flux::cache()->get($__fluxCacheKey),
            \Flux\Flux::cache()->runComponentSetup($component->componentName, $__componentRenderData)
        );
        echo $__fluxTmpOutput;
        
        unset($__fluxTmpOutput);
    endif; /* END: CACHE BLOCK */
    
    endif; /* END: RENDER */

    
    if (isset($__fluxCacheKey)) { unset($__fluxCacheKey); }
    if (isset($__fluxCacheKeyOriginal)) { $__fluxCacheKey = $__fluxCacheKeyOriginal; unset($__fluxCacheKeyOriginal); }
    if (isset($__componentRenderData)) { unset($__componentRenderData); }
    if (isset($__attributesOriginal)) { $attributes = $__attributesOriginal; unset($__attributesOriginal); }
    if (isset($__componentOriginal)) { $component = $__componentOriginal; unset($__componentOriginal); }
?>

Enabling the Component Cache

The attribute cache is an opt-in feature per component. This decision was made to allow for strategic caching of components, as well as not interfere with any components developers may have already published within their existing projects.

The component cache may be enabled for a component by using either the @cached or @optimized Flux compiler directives. The component's template must begin with one of these directives in order to enable the cache system:

@cached
@props([
    'value' => null,
])

<option
    {{ $attributes }}
    @if (isset($value)) value="{{ $value }}" @endif
    @if (isset($value)) wire:key="{{ $value }}" @endif
>{{ $slot }}</option>

When that component is executed, it will now be cached by Flux's attribute cache. The cache system is very explicit, and the $slot and $value output will remain the same across renderings if the attributes do not differ, even if the slot content is different. We can solve this by telling Flux not to cache specific sections of the component.

We will solve the slot first using the <flux:uncached></flux:uncached> compiler component:

@cached
@props([
    'value' => null,
])

<option
    {{ $attributes }}
    @if (isset($value)) value="{{ $value }}" @endif
    @if (isset($value)) wire:key="{{ $value }}" @endif
><flux:uncached>{{ $slot }}</flux:uncached></option>

Important

The <flux:uncached> component is not a real component. It is only considered at compile time. If the component does not have the attribute cache enabled, you may receive an error stating the component view could not be located.

Now, when the component is rendered, the main structure of the component will remain cached, but the slot contents will be dynamically swapped out for you. We can do something similar for the $value inside the HTML attributes using the @uncached/@enduncached directive pair:

@cached
@props([
    'value' => null,
])

<option
    {{ $attributes }}
    @uncached
    @if (isset($value)) value="{{ $value }}" @endif
    @if (isset($value)) wire:key="{{ $value }}" @endif
    @enduncached
><flux:uncached>{{ $slot }}</flux:uncached></option>

If we were to render the following:

<flux:the-component value="1">Slot 1</flux:the-component>
<flux:the-component value="2">Slot 2</flux:the-component>
<flux:the-component value="3">Slot 3</flux:the-component>

We would now receive unique output for all three renderings. However, this is probably not what we want in this scenario, as we would have three different cache entries because of the different value attributes. For some components, this is desired, but for trivial "just output" variables, we may want to exclude them from the cache.

We can achieve this by passing the name of the variables to ignore as the arguments to the @uncached directive:

@cached
@props([
    'value' => null,
])

<option
    {{ $attributes }}
    @uncached(['value'])
    @if (isset($value)) value="{{ $value }}" @endif
    @if (isset($value)) wire:key="{{ $value }}" @endif
    @enduncached
><flux:uncached>{{ $slot }}</flux:uncached></option>

After this change, we will only have one cache entry for all three renderings, and the value will still be updated dynamically.

If you need to exclude values from the cache using the <flux:uncached></flux:uncached> compiler component, you may pass them as a comma-delimited list to the exclude attribute:

<?php Flux::cache(); ?>
@props([
    'value' => null,
])

<flux:uncached exclude="value, anotherValue">
    @if (isset($value)) value="{{ $value }}" @endif
    @if (isset($value)) wire:key="{{ $value }}" @endif
</flux:uncached>

We may also exclude arbitrary values like so:

@php(Flux::cache()->exclude(['value']))

This is particularly useful in "wrapper" components that include delegate components.

The Setup Directive and Component

The cache compiler has the concept of a "setup" code block. You can think of this is as a function that gets called before the component is rendered, regardless of it is cached or not. This is useful for any logic that must be executed to ensure the correct behavior of the component, such as the @aware and @props directives.

If you'd like this logic to run each time the component is rendered, you may wrap that section of your component's template in a @setup/@endsetup directive pair:

@cached

@setup
@props(['value'])

@aware(['variant' => 'mini'])
@endsetup

// The rest of the component.

You may also use the <flux:setup></flux:setup> syntax:

@cached

<flux:setup>
@props(['value'])

@aware(['variant' => 'mini'])
</flux:setup>

// The rest of the component...

It is critical that any important logic that can impact the rendering of a component is wrapped in a setup block. A good example of where this is necessary is the icon/index.blade.php component. The $icon resolution logic should be included in the setup block:

@cached
{{-- Credit: Heroicons (https://heroicons.com) --}}

<flux:setup>
@props([
    'icon' => null,
    'name' => null,
])

@php
$icon = $name ?? $icon;
@endphp
</flux:setup>

// The reset of the component...

Ignoring Specific Attributes

You may ignore specific attributes from the cache key system by passing an array of options to the @cached directive:

@cached(['except' => 'wire:key'])

// The rest of the component...

When the cache system encounters these excluded attributes, it will substitute a placeholder value during the initial render in order to track where it would appear in the output. Because of this behavior, you should avoid any logic that depends on the existence of an attribute.

For example, the following types of components would no longer function correctly as a placeholder would always be available:

@cached(['except' => 'wire:key'])

@if ($attributes->has('wire:key'))
    // This would always be true.
@endif

You may ignore multiple attributes by supplying an array of attribute names:

@cached(['except' => ['wire:key', 'key']])

// The rest of the component...

Cache and @aware Variables

The attribute cache is capable of tracking variables inherited using the @aware directive. Under the hood, the @aware directive is swapped out for a @fluxAware directive when the cache is enabled on the component.

The @fluxAware will alert the cache manager that an inherited variable was used (made possible through the "observation" stack). These variables will be considered when computing new cache keys for that same component on subsequent renders.

The @optimized Compiler Directive

The @optimized directive is a simpler counterpart to the @cached directive. When using the optimized directive, all of the component's logic will still be evaluated each time the component is rendered, but the component's view will only be loaded once.

Let's take a look at what happens if we add optimized to the default select option variant:

@optimized
@aware([ 'variant' ])

@props([
    'variant' => 'default',
])

@php
// This prevents variants picked up by `@aware()` from other wrapping components like flux::modal from being used here...
$variant = $variant !== 'default' && Flux::componentExists('select.variants.' . $variant)
    ? 'custom'
    : 'default';
@endphp

<flux:with-field :$attributes>
    <flux:delegate-component :component="'select.option.variants.' . $variant">{{ $slot }}</flux:delegate-component>
</flux:with-field>

The caching component compiler will rewrite that to the following behind the scenes:

@cached

@setup
@aware([ 'variant' ])

@props([
    'variant' => 'default',
])
@endsetup

@uncached
@php
// This prevents variants picked up by `@aware()` from other wrapping components like flux::modal from being used here...
$variant = $variant !== 'default' && Flux::componentExists('select.variants.' . $variant)
    ? 'custom'
    : 'default';
@endphp

<flux:with-field :$attributes>
    <flux:delegate-component :component="'select.option.variants.' . $variant">{{ $slot }}</flux:delegate-component>
</flux:with-field>
@enduncached

As you can see, optimized is simply a shortcut for the wrapping your component's template in an uncached directive pair. You may also notice that the compiler added a setup/endsetup directive pair to the output. This happens automatically for optimized components whenever the compiler sees the @aware or @props directive being used. By default, it will wrap those directives and anything that appears before them in the setup pair. Most of the time this is fine, but there may be other logic you'd like to run before a component is rendered.

To do this, you may manually specify a setup block:

Using the setup directive:

@optimized

@setup
@aware([ 'variant' ])

@props([
    'variant' => 'default',
])

@php
// This prevents variants picked up by `@aware()` from other wrapping components like flux::modal from being used here...
$variant = $variant !== 'default' && Flux::componentExists('select.variants.' . $variant)
    ? 'custom'
    : 'default';
@endphp
@endsetup

<flux:with-field :$attributes>
    <flux:delegate-component :component="'select.option.variants.' . $variant">{{ $slot }}</flux:delegate-component>
</flux:with-field>

Using the setup component:

@optimized

<flux:setup>
@aware([ 'variant' ])

@props([
    'variant' => 'default',
])

@php
// This prevents variants picked up by `@aware()` from other wrapping components like flux::modal from being used here...
$variant = $variant !== 'default' && Flux::componentExists('select.variants.' . $variant)
    ? 'custom'
    : 'default';
@endphp
</flux:setup>

<flux:with-field :$attributes>
    <flux:delegate-component :component="'select.option.variants.' . $variant">{{ $slot }}</flux:delegate-component>
</flux:with-field>

If you have manually specified a setup block the compiler will not add its own, but will still add the uncached directive pair for you.

Flux Compiler Directives and Components

It is important to note that the @uncached, @enduncached, @setup, and @endsetup directives as well as the <flux:uncached></flux:uncached> and <flux:setup></flux:setup> components are only compiled if the component begins with @cached or @optimized.

Nesting these features is not supported, so don't do things like:

@uncached
    <flux:uncached>{{ $slot }}</flux:uncached>
@enduncached

If the caching compiler is disabled entirely, the original raw contents that appear between the directive or component pairs will be returned.

Example Implementation

The following is an example of how the select component may be updated.

In select/option/variants/default.blade.php:

@cached

@props([
    'value' => null,
])

<option
    {{ $attributes }}
    @uncached(['value'])
    @if (isset($value)) value="{{ $value }}" @endif
    @if (isset($value)) wire:key="{{ $value }}" @endif
    @enduncached
><flux:uncached>{{ $slot }}</flux:uncached></option>

In select/option/index.blade.php:

@cached

@aware([ 'variant' ])

@props([
    'variant' => 'default',
])

@php
    // This prevents variants picked up by `@aware()` from other wrapping components like flux::modal from being used here...
    $variant = $variant !== 'default' && Flux::componentExists('select.variants.' . $variant)
        ? 'custom'
        : 'default';
@endphp

@php(Flux::cache()->exclude(['value']))

<flux:with-field :$attributes>
    <flux:delegate-component :component="'select.option.variants.' . $variant"><flux:uncached>{{ $slot }}</flux:uncached></flux:delegate-component>
</flux:with-field>

Using a template like:

<flux:select variant="listbox">
    <x-slot name="button">
        <flux:select.button class="rounded-full!" placeholder="Choose industry..." :invalid="$errors->has('...')" />
    </x-slot>

    @for ($i = 0; $i < 3_000; $i++)
        <flux:select.option value="value-{{ $i }}">Option: {{ $i }}</flux:select.option>
    @endfor
</flux:select>

<flux:select>
    @for ($i = 0; $i < 3_000; $i++)
        <flux:select.option value="another-value-{{ $i }}">Another Option: {{ $i }}</flux:select.option>
    @endfor
</flux:select>

Produces results similar to the following:

With cache compiler enabled:

OS Debug Mode w/ Debug Bar Enabled Opcache Enabled Views Loaded Response Time (Dev Tools) Time (Debug Bar) Memory
Windows Yes Yes 21 ~274ms ~193ms ~23MB
Windows No Yes 21 ~230ms N/A ~7MB
Windows Yes No 21 ~444ms ~345ms ~46MB
Windows No No 21 ~230ms N/A ~30MB

With cache compiler disabled:

OS Debug Mode w/ Debug Bar Enabled Opcache Enabled Views Loaded Response Time (Dev Tools) Time (Debug Bar) Memory
Windows Yes Yes 30,011 ~4.29s ~3.64s ~67MB
Windows No Yes 30,011 ~2s N/A ~8MB
Windows Yes No 30,011 ~9.28s ~8.64s ~92MB
Windows No No 30,011 ~6.58s N/A ~30MB

Views loaded count determined by Laravel view creation events.

@JohnathonKoster JohnathonKoster marked this pull request as draft April 15, 2025 18:43
@JohnathonKoster
Copy link
Author

JohnathonKoster commented Apr 15, 2025

This is largely complete, but there are a few edge cases I am reviewing.

Should be good to go for initial rounds of testing and review 💪

@JohnathonKoster JohnathonKoster marked this pull request as ready for review April 15, 2025 21:32
@ChrisThompsonTLDR
Copy link

@JohnathonKoster I haven't had a chance to test this PR out, but I love the name Flux Capacitor.

Would there be value in making default caching a .env param so that it could be disabled on localhost and enabled on production?

@JohnathonKoster
Copy link
Author

@JohnathonKoster I haven't had a chance to test this PR out, but I love the name Flux Capacitor.

Would there be value in making default caching a .env param so that it could be disabled on localhost and enabled on production?

I'd have to spend some more time thinking about that - but my immediate thought is you'd probably want it to be running both locally and in production. The way this is implemented is it wouldn't have any impact on view caching itself (or requiring extra steps to bust the cache), nor does it store anything within an actual cache store - just an in-memory cache for that specific request.

Revert back to "normal" behavior, just to reduce the amount of custom code and behavior
If we have some code before the the first aware/props, it's probably important for adjusting state before those things evaluate. An example of this is the Avatar component.

Instead of having to rethink how all of that works, this change simply hoists that content so the component does not need to be reworked in order to use "optimized"
Thought of a better way to do this
Only really needing that value in component compiler for now
This will be used to process aware/props/etc., even for cached components. This change currently requires manual demarking like:

```
@setup
....
@endsetup
```

Some of this can be done automatically later
@nfauchelle
Copy link

@JohnathonKoster
I updated my composer to pull in your branch instead of livewire/flux
I've verified your changes are in the vendor/livewire/flux folder.

I some of the components I am using, changing the livewire / component / blade files and first tried <?php Flux::cache(); at the top, and then changed to using @cached. Doing either of these didn't seem to have an impact on the load time to reduce the templates being pulled in.

Am I missing something, or you still working through things.

@JohnathonKoster
Copy link
Author

@nfauchelle Which components are you working with, and do you have a brief example of how they are being used?

@nfauchelle
Copy link

@JohnathonKoster

I was playing with
<flux:select variant="combobox" placeholder="Choose industry...">
<flux:select label="Bob" variant="listbox" searchable>
and
<flux:input label="Username" description="Foo Bar." />
doing different sizes and props just to test.

I've pulled in your recent changes since my comment, and reset the blade components removing any @cached and Flux::cache I added.

I then created a flux-test.blade.php file, pulling in the css/js needed for the site.

Loads fast, and 1 view.

I added

<flux:select variant="combobox" placeholder="Choose industry...">
</flux:select>

Which bumps it up to 13 views rendered.

Adding <flux:select.option>Photography</flux:select.option> gives us 20 views.

Adding several more options (this comes from the FluxUI docs)

 <flux:select variant="combobox" placeholder="Choose industry...">
     <flux:select.option>Photography</flux:select.option>
     <flux:select.option>Design services</flux:select.option>
     <flux:select.option>Web development</flux:select.option>
     <flux:select.option>Accounting</flux:select.option>
     <flux:select.option>Legal services</flux:select.option>
     <flux:select.option>Consulting</flux:select.option>
     <flux:select.option>Other</flux:select.option>
</flux:select>

gives us 32 views and the page speed is now over twice as long.

I notice when I open the select all the options say Photography. Maybe that is because the docs are missing the wire:key.

Simply doing this really shows the slow down and high view count.

<flux:select variant="combobox" placeholder="Choose industry...">
    @foreach(range(1, 200) as $i)
     <flux:select.option value="{{ $i }}" wire:key="{{ $i }}">{{ $i }}</flux:select.option>
    @endforeach
</flux:select>

Do I need to change something in the templates? (since the stubs are in the repo should they be updated?

Appreciate you looking into the speed.

Leave the original content alone temporarily so Livewire can get in there and insert its markers the way it wants to.

If things are wrapped in the uncached callbacks too early, Livewire's patterns may not match correctly inside attributes, etc.
Prevents pushing markers to the end of the compiled document by putting the swap placeholder back where it was extracted.

Consistent newlines
@JohnathonKoster
Copy link
Author

@JohnathonKoster

I was playing with <flux:select variant="combobox" placeholder="Choose industry..."> <flux:select label="Bob" variant="listbox" searchable> and <flux:input label="Username" description="Foo Bar." /> doing different sizes and props just to test.

I've pulled in your recent changes since my comment, and reset the blade components removing any @cached and Flux::cache I added.

I then created a flux-test.blade.php file, pulling in the css/js needed for the site.

Loads fast, and 1 view.

I added

<flux:select variant="combobox" placeholder="Choose industry...">
</flux:select>

Which bumps it up to 13 views rendered.

Adding <flux:select.option>Photography</flux:select.option> gives us 20 views.

Adding several more options (this comes from the FluxUI docs)

 <flux:select variant="combobox" placeholder="Choose industry...">
     <flux:select.option>Photography</flux:select.option>
     <flux:select.option>Design services</flux:select.option>
     <flux:select.option>Web development</flux:select.option>
     <flux:select.option>Accounting</flux:select.option>
     <flux:select.option>Legal services</flux:select.option>
     <flux:select.option>Consulting</flux:select.option>
     <flux:select.option>Other</flux:select.option>
</flux:select>

gives us 32 views and the page speed is now over twice as long.

I notice when I open the select all the options say Photography. Maybe that is because the docs are missing the wire:key.

Simply doing this really shows the slow down and high view count.

<flux:select variant="combobox" placeholder="Choose industry...">
    @foreach(range(1, 200) as $i)
     <flux:select.option value="{{ $i }}" wire:key="{{ $i }}">{{ $i }}</flux:select.option>
    @endforeach
</flux:select>

Do I need to change something in the templates? (since the stubs are in the repo should they be updated?

Appreciate you looking into the speed.

I've left the stubs in the repo alone for now so the team can decided if/how they want to update.

In the case of the select option, if you add the @cached directive to the top of select/option/index.blade.php file:

@cached

@aware([ 'variant' ])

@props([
    'variant' => 'default',
])

// ...

you will get the behavior of each option repeating the same slot content. This is happens because the cache layer stores the output for each unique set of attributes. We can tell the cache system to swap out the slot content, even for cached instances, by wrapping that part of the component in the <flux:unached></flux:uncached> compiler tag:

// ...
<flux:with-field :$attributes>
    <flux:delegate-component :component="'select.option.variants.' . $variant"><flux:uncached>{{ $slot }}</flux:uncached></flux:delegate-component>
</flux:with-field>

That would prevent additional views being loaded for the same set of attributes, while still swapping out the slot contents on-the-fly.

There also exists the @optimized version which is a bit simpler to use, but doesn't have as dramatic of speed improvements (both when in debug/not-debug mode). To use this, we would add the @optimized directive at the beginning of the component:

@optimized

@aware([ 'variant' ])

@props([
    'variant' => 'default',
])

// ...

When using optimized, we don't have to worry about marking sections of the component with <flux:uncached> as the component's inner logic will be executed each time, just without reloading the views.

I'm in the process of updating the PR description, since it is out of date, but hope this helps in the meantime!

@nfauchelle
Copy link

Thanks @JohnathonKoster I will pull in these recent changes and have another play.

Which version of flux pro do you have in your composer? I've been using the latest, I wonder if that could be causing me issues since the non pro (this repo) might be expecting flux pro 2.1.2 or something, or have you rebased recently?

@JohnathonKoster
Copy link
Author

Thanks @JohnathonKoster I will pull in these recent changes and have another play.

Which version of flux pro do you have in your composer? I've been using the latest, I wonder if that could be causing me issues since the non pro (this repo) might be expecting flux pro 2.1.2 or something, or have you rebased recently?

I'm using latest 2.x for Pro 👍

@nfauchelle
Copy link

nfauchelle commented May 3, 2025

Thanks. I've pulled in the latest changes. Reset all my stubs to default.
I added a couple of @cached here and there, but it's not kicking in.

dd'ing in the function compile in ComponentCompiler.php never gets hit.

shouldCache in the FluxManager.php does get called and return true. I can look into it more if you'd like but maybe I am missing something. I am using Laravel 11 if that makes any difference.

@JohnathonKoster
Copy link
Author

JohnathonKoster commented May 3, 2025

@nfauchelle Laravel 11 shouldn't make a difference. Hard to say what's happening in your setup without seeing more. If you have a repo (a private one would work as well, if has pro stubs/etc. or there are things you'd rather not make public) you can share I'd be more than happy to dive in and help debug!

@JohnathonKoster JohnathonKoster marked this pull request as ready for review May 3, 2025 23:17
@joshhanley
Copy link
Member

@nfauchelle can I offer a suggestion? I would suggest holding off testing until what we are proposing is ready. You're more than welcome to keep trying to get this running and testing it if you like. But, John, Caleb and myself are currently all working on these performances issue in both Flux and Livewire.

The work John is doing here is going to be the base of the speed improvements in Flux, but it won't be something that users will need to be concerned about directly. We will be applying this caching, etc. to Flux's components once this is stable and merged into Flux. The thing that started all of this for us was due to selects being slow.

By all means though, if you're still keen to play around with it knowing all of the above, then feel free. 🙂

@nfauchelle
Copy link

No worries Josh, just let me know when it's ready for testing and I am more than happy to give feedback.

@joshhanley
Copy link
Member

@nfauchelle can do, that'd be great! 😁

@nfauchelle
Copy link

Hey guys, just want to check in, feels like this is dead now but I am sure lots is happening behind the scenes and it's complex (I've listened to the pod cast).

It is having an impact on things I am doing so wanted to see if there was any news. Thanks.

@joshhanley
Copy link
Member

@nfauchelle we're still working on it 🙂 not a lot more I can say at this stage

@rzv-me
Copy link
Contributor

rzv-me commented Jun 3, 2025

fyi, I created a PR in Laravel 12.x that was merged on 2025-05-10 and was released with v12.14.0 on 2025-05-13 that decreases the compilation times of components by up to 80%.

Also this was not backported to Laravel 11.x

So if you have inconsistencies in compilation time between Laravel 11.x and Laravel 12.x, this might be the issue

laravel/framework#55703

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants