-
-
Notifications
You must be signed in to change notification settings - Fork 315
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
[LiveComponent] Lazy load LiveComponent #1515
Conversation
I use on some projects https://www.stimulus-components.com/docs/stimulus-content-loader/ |
To clarify?
|
If I follow the logic: Based on what I understand I have a question: |
Yep i'll edit the description you're right :) |
It's not "when a component is on the viewport" it can be a lot of things (a component is "no more intersecting" for instance) So let's say "at a given time" for now :)
Yes
You can dispatch any event you want :)
In that particular scenario, as the intersection observer is still obeserving the component, it would indeed trigger a "live:appear" event when the component start intersecting the viewport, and then unobserve it. Now my question is: why would you do that ? :) I mean, if you trigger manually some "disconnect" event to a stimulus component, while not really disconnecting it, that would create some weird side effects... same for all the " If someone voluntarily call internal/prive code, there is not much we can do to anticipate it / handle it, and there is nothing we can do to prevent it. Did you have a specific / particular case in mind ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's quite an elegant, simple solution. Turbo also uses loading="lazy"
on Turbo Frames, so I like that attribute.
Though, then doing <twig:CustomThing loading="lazy">
would be more consistent than having a lazy
boolean attribute. Doing loading="lazy"
might also help clarify loading
vs defer
. The explanation would be something like:
To defer loading your component until later, add the
defer
attribute. The component will render empty (or with some loading markup) then immediately make an AJAX call to load the real content.
To not load the content until the element becomes visible, addloading="lazy"
.
So, like Turbo, there would be loading="eager"
(the default) and loading="lazy"
. And we expose this detail to the user vs hiding it from them.
Should we then support |
In HTML,
As lazy implies defer here, your suggestion makes perfect sense... but it complexifies a bit the DX <twig:MyComponent defer lazy="{{ loop.index > 4 }}" /> <twig:MyComponent loading="{{ loop.index > 4 ? 'lazy' : 'defer' }}" /> Can we keep "defer" and "lazy" as some kind of aliases of "loading=XXX" ? Or is it not worth it ? |
Yea... just having a
The |
146be89
to
e8121c5
Compare
Updated for loading="lazy" / loading="defer"
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs/backend code looks good!
Hey, I just have a question in a previous project we were using lazy loading in a specific way. The user was able to scroll a list of full-page images and we started the lazy load 3 images before the current image. I don't know if this PR can cover such a need. And if we should care about such a need? |
I dont think this will be necessary. With a placeholder, and knowing your component will load probably in less than 200ms so it will more look like an animation than an ajax request. An image is often much larger than a live component request. I’d suggest we release without any customization and lets see if the need presents itself IRL ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fantastic - a few last tweaks only!
src/LiveComponent/src/EventListener/DeferLiveComponentSubscriber.php
Outdated
Show resolved
Hide resolved
unset($data['defer']); | ||
} | ||
|
||
if (\array_key_exists('loading', $data)) { | ||
if (\in_array($data['loading'], ['defer', 'lazy'], true)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, what if the user does loading="foo"
? On the one hand, I think we should throw an exception. On the other hand, maybe we should allow that and render it as an attribute. Right now, I think we're doing neither - as it looks like loading="foo"
would not be rendered due to the unset()
below, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only HTML element that supports the loading
attribute today are img
and iframe
, probably portal
soon.
None of them can be a LiveComponent so i think we can ignore this for now. And decide what to do if someone raises some concern about that ?
wdyt ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, throwing an exception would be the most helpful for the user and the most conservative (if we removed the exception and allowed the attribute to render later, there is no way that will affect anyone).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we should ignore false
and ''
value, keep lazy
or defer
, and throw elsewhen.. right ?
6fe862b
to
262b700
Compare
262b700
to
9e4e02e
Compare
All is ready for me :) |
One last comment (hopefully) and a phpcs failure :) |
phpcs due to rebase, they are fixed in: #1579 |
074bb80
to
7244c52
Compare
7244c52
to
b0b4cda
Compare
Fantastic! Thanks Simon! |
Thanks to @jakubtobiasz for paving the way with #1143 :) |
UPDATED COMMENT
This PR add loading support for LiveComponent
loading="defer"
to load the component on page loadloading="lazy"
to load the component when the component is visibleThe
defer
attribute is deprecated for this solution.See more details in the document.
ORIGINAL PR:
I have some live component stuff in progress, so let's post this one now to start the discussion.
This PR implements a volontarely-minimal
loading="lazy"
mode on LiveComponents, based on top of thedefer
one.This feature is dedicated to Live Component (as there is some particularities due to keeping "state").
(if you need some lazy loading with classic twig components, there are already methods out there to handle such things)
Defer ? Lazy ?
Both defer and lazy attributes allow to ... defer the full rendering of the component. So when the page first renders, it contains only a skeletton / empty component.
Then, depending of the attribute used, the component will fetch its content via a fetch request:
defer
: when the page loads (it react to the "connect" Stimulus)lazy
: when the component enters the viewport (so as soon it's at least 1px visible in it)I precise "visible" because if you use some tabbed content, horizontal sliders, etc... the "intersect" is not triggered until the component is actually.... visible.
Usage
Note:
lazy
impliesdefer
, so this is equivalent toSo.. defer or lazy ?
As i see it:
defer
should be used for any content that will 100% be visible in the main content, but too heavy to be computed with the original request (think Facebook/Insta first results of the timeline)Lazy
should be used for anything really not in the viewport, and that you're not sure will be rendered to the user (comments, tabbed content, ...)In both situation, this is not a good idea to have too many lazy live components in the page, it's probably a sign that you should use Turbo instead!
(i'll add some doc about defer/lazy and layout shift too, but the idea is: better "keep the space you'll need")
Rendering
IntersectionObserver
The loading is triggered with a dedicated intersection observer.
It happens only once and the the element in unregistered.
No settings allowed (as images, iframes, etc.. in the browser)
TODO: optimize threesholds.
Loading=lazy
I chose to use the
loading="lazy"
attribute tag on the rendered HTML, as the goal is to have the same behaviour on the browser side.It is standard for image, iframes, maybe portals soon, and i think it's self-explanatory.
Example: https://web.dev/articles/browser-level-image-lazy-loading
For browsers and for seo , this seems to be a good way to hint what this div is (i'll see aria stuff later as it touches many things on the codebase)
DX
Boolean attribute values
This is how we document the defer feature
For better DX, i add a small bool cast for both lazy and defer attributes values. It allows to have some dynamism on the calling side with HTML syntax too.
(Tests / doc incoming, demo(s) in progress)
-- IN PROGRESS --