Description
Describe the problem
I've come to the view that stuff
(as it's currently designed) is a mistake, because it forces nested routes to load serially and because it prevents us from taking advantage of nested routes to stream responses (see #4910).
Ideally if we had a route like this...
<!-- src/routes/__layout.svelte -->
<script context="module">
export async function load({ fetch }) {
const res = await fetch('/one.json');
const { one } = await res.json();
return {
props: { one }
};
}
</script>
<script>
export let one;
</script>
<h1>{one}</h1>
<div>
<slot/>
</div>
<!-- src/routes/foo/__layout.svelte -->
<script context="module">
export async function load({ fetch }) {
const res = await fetch('/two.json');
const { two } = await res.json();
return {
props: { two }
};
}
</script>
<script>
export let two;
</script>
<h2>{two}</h2>
<div>
<slot/>
</div>
<!-- src/routes/foo/bar.svelte -->
<script context="module">
export async function load({ fetch }) {
const res = await fetch('/three.json');
const { three } = await res.json();
return {
props: { three }
};
}
</script>
<script>
export let three;
</script>
<p>{three}</p>
...then two things would happen. Firstly, we'd fetch one.json
, two.json
and three.json
concurrently. Secondly, as soon as one.json
returned, we'd flush
<h1>1</h1>
<div>
then as soon as two.json
returned, we'd flush
<h2>2</h2>
<div>
then as soon as three.json
returned, we'd flush the remainder:
<p>3</p>
</div>
</div>
At present, that's not possible. We run load
functions in sequence for two reasons...
- we want to bail out early if a layout errors or causes a redirect — subsequent
load
functions should not run - a nested route should be able to access any
stuff
that was returned from an rootward layout
...and we can't render anything until everything has loaded, because the root layout might make use of $page.stuff
.
Ideally, we'd be able to solve all these problems without relying on a design that forces serial loading and delayed rendering.
Describe the proposed solution
Honestly, I haven't really figured this out yet — I'm really just trying to articulate the problem in hopes that a solution will present itself. But here's a straw man:
<script context="module">
export async function load({ fetch, parent }) {
const stuff = await parent(); // merges stuff from all parent layouts
return {
stuff: {
b: stuff.a + 1
},
props: (stuff) => ({
// because `props` is a function, rendering is
// delayed until we have the final `stuff` object
title: stuff.title
})
};
}
</script>
<script>
// this comes from the leaf component
export let title;
</script>
<svelte:head>
<title>{title}</title>
</svelte:head>
This doesn't feel totally intuitive, but it would speed up performance in the common case where load
isn't blocked on its parents, and $page.stuff
isn't used. And we would still have the ability to bail out if a parent load
redirects (maybe await parent()
throws an error for non-200 responses?)
Alternatives considered
I am all ears. (I'm also interested to know how people are using stuff
)
Importance
would make my life easier
Additional Information
No response