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
fix(core): add hydration protected elements #52478
base: main
Are you sure you want to change the base?
Conversation
@arturovt as we've discussed offline, the proposed fix would not work due to potential performance issues. This extra logic should be limited to the following scenario:
There are 2 possible cases:
Those 2 cases are being handled by different code paths (static case, binding case). Static attributes are applied when an element is created/hydrated and we can handle that. The binding case is more complicated: it's invoked as a part of change detection and currently there is no information whether an underlying element was created or hydrated, so we need to store more info about it (but only for Here is a proposed solution:
The
Additional note: there might be other elements affected by the same problem, we'd like to ask if you could check the spec for elements like Please let me know if any additional information is required. |
Here's the list of valid and invalid cases: elements marked in red reload resources when hydrated. We don't consider cases when the cache is disabled through devtools or the remote resource returns a 302 status code.
<img src="/assets/img_girl.jpg" />
<picture>
<source media="(min-width: 650px)" srcset="/assets/img_food.jpg" />
<source media="(min-width: 465px)" srcset="/assets/img_car.jpg" />
<img src="/assets/img_girl.jpg" />
</picture>
<svg>
<image xlink:href="/assets/angular.svg" />
</svg>
<video controls autoplay>
<source src="/assets/mov_bbb.mp4" type="video/mp4" />
<source src="/assets/mov_bbb.ogg" type="video/ogg" />
</video>
<audio controls src="/assets/t-rex-roar.mp3"></audio>
<video controls src="/assets/friday.mp4">
<track default kind="captions" srclang="en" src="/assets/friday.vtt" />
</video>
<object
type="application/pdf"
data="/assets/In-CC0.pdf"
></object>
<embed type="video/webm" src="/assets/flower.mp4" />
<iframe src="https://www.youtube-nocookie.com/embed/7HVMn1TpXKQ"></iframe> |
@arturovt thanks for additional information, this is very helpful. We can extend the |
@AndrewKushnir I am interested in providing the fix; I'll need to go through your comments and start implementing it locally. I'll message you if I have any questions. |
e834d25
to
8d2cffe
Compare
ee825ad
to
ada559b
Compare
Thanks @arturovt and @AndrewKushnir for your efforts on this. |
0dd36bf
to
480bb0f
Compare
@arturovt thanks for implementing this 👍 I've added this PR to the v18 milestone. If you get a chance, could you please try to find corresponding sections of HTML spec that talk about this specific behavior of the |
@AndrewKushnir I went through the HTML standard and I may guess it doesn't explicitly mandate this behavior or provide detailed instructions on how user agents should handle this aspect of network requests. Because if we talk about I am confident that what we're doing in this PR doesn't go against the spec because we're essentially targeting that hydration case with the very first render. (Please correct me if I'm wrong...). Out of curiosity, wanna check what engines do internally... UPD: I examined the behavior of Safari's JSC for images and iframes. Each HTML element class has a hook called For iframes, In the case of images, it's more intricate and aligns with our previous discussion. if (oldValue != newValue)
selectImageSource(RelevantMutation::Yes);
else
m_imageLoader->updateFromElementIgnoringPreviousErrorToSameValue(); And But you know, it's more like interesting information to keep in mind. Unfortunately, such implementation details are not included in the specification. |
eec6111
to
ec8c509
Compare
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.
@arturovt thanks for the PR, the code looks great! I've just left a few comments with some proposals.
Could you please also mark this PR as "Ready for review" via GitHub UI?
packages/core/src/render3/util/attrs_utils_with_hydration_support.ts
Outdated
Show resolved
Hide resolved
packages/core/src/render3/util/attrs_utils_with_hydration_support.ts
Outdated
Show resolved
Hide resolved
ec8c509
to
a506013
Compare
Done. Symbol tests are failing, I will update symbols later. Gotta play with unit tests first to spy on the setAttribute. |
a506013
to
fec38df
Compare
packages/core/src/render3/util/attrs_utils_with_hydration_support.ts
Outdated
Show resolved
Hide resolved
7752b64
to
05bfe2b
Compare
@AndrewKushnir I have addressed your comments and also updated unit tests with spying on the |
b3e360c
to
7c18e66
Compare
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.
@arturovt thanks for addressing the feedback! I've left a comment about some extra testing cases.
// Assert that the spy for the hydration-protected element renderer has not been called | ||
// at all. | ||
expect(setAttributeSpies[1]).not.toHaveBeenCalled(); |
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.
Could you please also add some logic here to change the value
and make sure that the setAttribite
was invoked as needed (i.e. that the protection logic doesn't prevent subsequent value updates)?
@AndrewKushnir your last comment helped me realize I was moving in the wrong direction. We only covered the attribute case ( I have updated Wondering whether we have to update the |
With this commit, we are now able to handle hydration-protected elements and their attributes. This enhancement is aimed at improving UX and reducing the number of HTTP requests made by browsers to external resources during the hydration process. Take, for example, the `iframe` element with a static `src` attribute, which is rendered on the server. When we begin hydrating that element on the client, it attempts to set up the static attributes again. This includes setting the `src` attribute again with the same value it already has. Browsers interpret each change to the `src` attribute as a new request to load the specified resource. Consequently, the video may flicker as the browser attempts to reload it. We now maintain a map of elements that should be protected from hydration. Each element (acting as a key) maps to a list of attributes, for instance, `iframe -> ['src']`. When we look up an existing element, we check its tag name and if there's a list of attributes associated with it. If we encounter an `iframe` (or other protected element), we iterate over its protected attributes and check whether that element already has those attributes set. If it does, we save this information in the hydration info map to use it when calling `setAttributes`.
7c18e66
to
8c950ff
Compare
No description provided.