Async script output
See original GitHub issueClear and concise description of the problem
Currently Vite outputs <script type="module">
scripts which are executed in deferred mode. Although this is a lot better than the blocking default of standard <script>
tags, it does have an issue with streaming.
Specifically defer
does not execute any scripts until DOMContentLoaded
has fired. This means that if you are using SSR + streaming (or even if your document is sufficiently large to be parsed by the browser in multiple segments) that no scripts execute until the browser receives the final byte of html.
This is especially bad with streaming + partial hydration, where you are intentionally are sending down sections of interactive html and would like for it to hydrate ASAP. Instead right now in Vite hydration will be blocked until streaming has finished.
Simply adding async
to these scripts causes them to download asynchronously without blocking the rest of the document (as the name implies) similar to defer
, however it also allows the scripts to execute as soon as they are downloaded.
Adding async
blindly to Vite’s output scripts as it stands would cause a race condition for common setups though, since they could be relying on the document being complete by the time initialization code runs, for example:
import { hydrate } from "popular-library";
hydrate(document.getElementById("my-app"), ...);
This does not account for the case where the script has downloaded and is ready, but the #my-app
content has not yet been sent by the server.
It would be nice if there was some way to preserve the simple behavior of Vite to support the above without much complexity, while also allowing a way to opt into the async
style hydration.
Suggested solution
One potential option could be to support a special handling of <script async type="module" src="..."/>
in the .html
files. We could perhaps use this to indicate that the chunk being loaded is capable of initializing immediately. I think this might be a bit tricky though since technically you can have multiple scripts in your .html
file that would get merged into the same chunk and loaded together.
Alternative
One not so great option would be to have a dynamic import in the .html
file like:
<script type="module">
import("./my-actual-entry");
</script>
This is a bit verbose though and currently does not seem to actually work. A naive compilation of this might also introduce an additional network waterfall, which trades one perf issue with another.
Additional context
I’m not clear on the solution here but wanted to get the discussion started. Happy to create a PR if we can decide on a direction.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:2
- Comments:5 (3 by maintainers)
Top GitHub Comments
I’d be happy to work on it but I’ve got several other things on my plate for the moment. Probably won’t be able to put up a PR for a couple weeks.
Yeah I hit this while working Solid plugin a few weeks back as well. We ended up doing an inline plugin like:
But really any library that wants to take advantage of progressive rendering would want this feature so that scripts can start loading and executing before the document is finished streaming in. Without it progressive rendering(streaming) loses its primary benefit.