-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Feature: List rendered components during SSR #5476
Conversation
@pngwn @Rich-Harris @antony @allofthenamesaretaken @benmccann @mindrones Can we get this added to the next release? Very essential feature for anyone who wants to speed up their application by being able be able to http/2 push or preload asychronous modules that have been rendered during SSR. |
What's the reason for opening a new PR? What changed between this and #4856? |
Added tests & docs @benmccann |
Really need this feature ASAP for the current project I am working on in my startup. How soon before this is added to the next release? |
Ok, thanks for the clarification. And thanks for adding tests! Just FYI, you can update existing PRs with changes like that and don't have to open new ones |
I have to admit this feels a bit like 'scenario solving'. The way we've solved this elsewhere is by analysing the SSR bundle alongside the route manifest that informs SSR. This has a key advantage over the approach outlined here: <script>
// Foo is a dependency of this component, and will need to be loaded
// before this component can render...
import Foo from './Foo.svelte';
let condition = false;
</script>
<!-- ...but it isn't rendered during SSR, so won't show up in the list of necessary components -->
{#if condition}
<Foo/>
{/if}
<button on:click={() => condition = true}>
show foo
</button> Using a route manifest is more work but more reliable, and doesn't increase API surface area. |
I think the way you'd write that code sample would probably look more like: <script>
export let Foo;
let condition = false;
// Foo is imported dynamically only when needed
if (condition) {
import('./Foo.svelte').then(mod => {
Foo = mod.default;
});
}
</script>
<!-- it likely will render Foo on the client only if it rendered on the server - though this is not guaranteed if the if statement checks for the presence of window or does something similar to specifically make the client-side rendering purposefully different -->
{#if condition}
<svelte:component this={Foo}/>
{/if}
<button on:click={() => condition = true}>
show foo
</button> That makes the case for this feature a bit more reasonable. Whether it meets the bar for being worth an API addition though I'm not sure of. I've personally never run into a case where the manifest approach is not sufficient What we want to do here may also be influenced by what we decide regarding browser vs server scripts (somewhat discussed in sveltejs/rfcs#27). The more browser and server logic diverge probably the less useful it is to load client-side scripts based upon what happened on the server |
Thank you for your comment @Rich-Harris In the example you gave above, If we call that component In an actual async example, (eg: The problem with a route-manifest is that it only works one layer deep (eg: Page level). If you try importing nested async components we won't have any idea of what to preload. This would be especially useful for the There's also some scenarios where a route-manifest won't help you too much, consider you have a router that dynamically returns a different page component on the same url. Adding the logic to handle that stuff would be incredibly difficult, especially if there's lots of these types of routes eg: Imagine a news website that doesn't want to use subfolder prefixes for knowing what type of page to render. They may want to do this for SEO/UX purposes. (eg: let pageTypes = {
article: () => import('@/templates/article'),
factcheck: () => import('@/templates/factcheck'),
category: () => import('@/templates/category'),
}
let routes = [
//catch all path
new Path('/*', async ({slug}) => {
let pageType = await getPageType(slug) //contact backend to find page type of this URL
if(!pageType) return () => import('@/pages/404')
return pageTypes[pageType] // this could return 1 of 3 PageTypes
})
] Your route-manifest won't help much here, as the URLs are dynamic. Another use-case: In this scenario, the dashboard components in this route are unknown until rendering, and you could only know which components to preload if an API such as this was exposed. I don't like to create additional API surface, but this is a pretty essential feature that exists in other frontend frameworks. |
Even if it were asynchronously loaded (eg: The problem this is solving Time-till-interactive caused by loading async components on an already SSR'd page. In fact, what can happen is that the client hydration failure may cause the entire page to flash white until the async components load, this is of course bad UX. Like you said, using a route manifest will involve more work, and this solution would save time and cover over 90% of use cases. I am using this fork in 4 production builds because of it's loading speed & SEO benefits. I really love what Svelte is doing, and a feature like this will help mature it into a strong option for enterprises. I plan to publicly release the router we have created using this system, which would help contribute to the svelte ecosystem. It's based on Vue's We are using this feature for a SaaS startup my team and I are building. We would love to see it make the core :) |
Bump I would love to see this merged so I can release my router I'm using across a few projects that does SSR & client-side routing module & supports HTTP2/Push. |
I don't think we're at the state where we'd be merging this as there is no consensus among maintainers that this feature is required. We'll circle back on this as time allows. |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
SvelteKit handles preloading pretty well. If you had dynamic imports this might allow them to be preloaded, which we can't do today as we don't know which ones actually get used |
Resolves issue: #4854
Done
Feature
This feature adds a
renderedComponents
array with a list of filenames of components rendered during SSR.You can match these filenames to client-side modules output by a bundler such as Rollup in order to preload necessary asychnronous components before the client-side realises it even needs it.
This feature is important for apps that use asynchronous components, and would like to ensure the client-side modules are loaded asynchronously instead of synchronously (waterfall effect)
Without this feature Total time:
2,161ms
With this feature Total time:
1,645ms
This time gains compound for each additional nested asychronous layer (eg: async components requiring async components).
Time savings also be improved by implementing HTTP/2 Push, which is currently not possible without this feature.
Use case:
This feature is essential for enterprise applications that would like to provide a fast client-side experience, with minimal loading times for asynchronous components.
Asset injection ensures that the necessary scripts are prefetched/preloaded for the client in parallel (and enables the possibility of
http/2 PUSH
for async output files)