-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Colocate remote data fetching in svelte component files #4741
Comments
There's not currently any groundwork at all for doing cross-component optimizations - each component is compiled in isolation. I don't think there's anything particularly currently actionable for Svelte to do here. Sapper is solving this using |
I agree with much of what @Conduitry says. Developers can implement manually any of the optimizations that I am describing. My belief is that, because there are a lot of repetitive tasks involved, and some patterns are better than others, a compiler may provide better developer experience in the implementation of those data optimizations. I also agree that a lot of thinking is required before baking stuff in Svelte core. Once you add it, you can't remove it, and you have to maintain it. If you change ideas later, you broke tons of people code. The problem is that it is not possible to provide a nice developer experience, like having a dedicated special tag, without hooking into the compiler, which Svelte does not allow so far. Vue will in Vue 3 allow developer to hack the template compiler, with the possibility to extend syntax. That is very much in beta and pretty much nobody is using it, so this is really bleeding edge stuff. Might be worth keeping in some parts of the brain for the future. Anyways let's see what others say. |
I think there might be an opportunity to do something like Next's In many ways I think this is a much more elegant solution than "magic static methods" and dead code elimination since it fels like the risk for accidentally leaking server secrets would be way lower. <script context="build">
// any code in here would be pre-evaluated at build time,
// and then added to the module scope(?)
</script>
<script context="server">
// any code in here would run during SSR, and for client builds
// it could be exposed as middleware that could be consumed
// by a server or exposed as serverless function(s)
export default async (req, res, context) => { ... }
</script> The coordination part for the server-context would also need to be solved. Thats probably the harder problem.. It's possible that limiting the use to "routes" and/or pages, like |
I was just reading about Svelte in Markdown and I realized that there is a preprocessor option in Svelte. That could probably be used to pre-compile away the extra tag that I was proposing. Going to investigate that. |
I checked if it's possible to extend
I am still wondering what would be a clean way to extract the additional context and pass it to the bundler (rollup in my case). Here is my experimental fork. |
@brucou Сorrect me if I'm wrong, but it just looks like synchronous component rendering right after data fetching. Sort of, we want to display the component only when the data it needs is loaded. Since the component can be dynamic (code-splitted), so data load function must be part of that component. If so, then the // UserProfile.svelte <h1>Hello {user.name}</h1>
<script context="module">
export async function preload({ userId }) {
const res = await fetch(`/users/${userId}`);
const user = res.json();
return { user };
}
</script>
<script>
export let user = null;
</script> // somewhere in the app <script>
import Viewpoint from 'svelte-viewpoint';
...
</script>
<Viewpoint component={() => import('./routes/UserProfile.svelte')} {userId}>
<div slot="loading">
<Spinner />
</div>
<div slot="error" let:error>
<Error {error} />
</div>
</Viewpoint>
|
Hi @PaulMaly thanks for your answer. I did not know about svelte-viewpoint and it is great that you mentioned it. My original post was about the possibility to use a compiler to aggregate data fetches that are scattered in components for optimization purposes. Optimization-aside, we can indeed fetch data as you recommend. Optimization may include not refetching data that has already ben fetched, aggregate fetches into a single request, and more. The inspiration here is graphQL/Relay. I thought a compiler would add value here because data fetching and the related issues are a common and generic need that can be abstracted behind an API (output by the compiler). Most of the state handled by apps (app local state that can be accessed by the app synchronously) I have seen are 80% composed of copy of remote state (state in databases that can be only be accessed asynchronously). The source of truth being the remote state, such apps more often than not need a synchronization layer. Synchronization of data sources is one of the generic problems I alluded to. In the React world, React Query is gaining in popularity as it handles fetching, caching and synchronization. That is one thing. The other thing is optimization and the inspiration here is graphQL and its client libraries (Relay for instance). Apollo handles optimistic fetching, caching, refetching, error handling, pagination, and more. The same way you do code-splitting and code bundling, you can do data-splitting, and query bundling. So the data requirements for the required components for a page may be aggregated in a lower number of more efficient queries (that is what I called query bundling). If the user is querying data in a component that is never used, then the unused data can be trimmed out of the query (dead data elimination :-). The idea is that if the compiler knows at build time (via some declarative API or syntax under the That would fit Svelte idea of developer productivity and writing less code and performant-enough by default. Implementation would not have to be baked in Svelte, but the API (or declarative language) would have to be imposed by Svelte. Just like there are several implementations of graphQL but one specification for it. An implementation could be passed through CLI options. So this differs from @jonatansberg proposal of varying <remote lang=js context=server>
// any code in here would run during SSR
<remote/> and <remote lang=graphql context=build endpoint=...>
// some graphql query
<remote/> ? I need to flesh my ideas out a bit (or a lot) more I guess. In any case, I don't really support Svelte baking this in, but rather Svelte coming up (if it does not already have) an architecture that respects the open/closed principle, i.e. open for extension, closed for modification. The subsuming idea in fine is the ability of adding custom tags and integrating custom processing within the svelte toolchain --- just like you have hooks, lifecycle methods, middleware or plugins that add custom processing in pre-determined integration points. Svelte preprocessor is a good example of allowing custom processing. Svelte Native, Svelte GL, Svelte TypeScript may be other examples of possible extensions that would benefit from reusing Svelte Core without modification (if they don't already do). |
One example (using graphql) of what a declarative description of a query could be in Svelte with a custom tag, and its possible preprocessing:
<remote preload lang='graphQL' endpoint=... >
query events($page: Int){
events(page: $page) {
name
}
}
</remote> <script>
async function fetchMoreEvents(){
page++;
await events.fetchMore({
variables: {page},
updateQuery: (previousResult, {fetchMoreResult}) => {
return {events: ...previousResult.events, ...fetchMoreResult.events}
}
})
}
<script/>
{#await events}
Loading...
{:then result}
{#each result.data.events as event}
{event.name}
{/each}
<SvelteInfiniteScroll on:loadMore={fetchMoreEvents}>
{:catch}
...
{/await}
<script>
import {query} from 'svelte-apollo'
import {client} from './client'
import {gql} from 'apollo-boost'
import SvelteInfiniteScroll from 'svelte-infinite-scroll'
const _query = gql`
query events($page: Int){
events(page: $page) {
name
}
}
`
let page = 0;
const events = query(client, {query:_query, variables: {page}})
async function fetchMoreEvents(){
page++;
await events.fetchMore({
variables: {page},
updateQuery: (previousResult, {fetchMoreResult}) => {
return {events: ...previousResult.events, ...fetchMoreResult.events}
}
})
}
</script>
{#await events}
Loading...
{:then result}
{#each result.data.events as event}
{event.name}
{/each}
<SvelteInfiniteScroll on:loadMore={fetchMoreEvents}>
{:catch}
...
{/await}
<script/> I have a few things to flesh out (the The |
This issue 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. |
Is your feature request related to a problem? Please describe.
I just came upon this idea, and apologies if by any chance it has already been discussed before. This occurred to me while watching a React talk dealing with all the stuff that they put in place to ensure a smooth user experience, including loading experience. There is a ton of complexity there, and I wonder if a compiler-based approach may not help.
Describe the solution you'd like
The idea is, like React/Relay folks do, to colocate the description of the data a component needs in the component template. So for instance, in a
Post.svelte
component:In the data section (name could be anything, that is just the first thing coming to mind), devs would put a description of the remote data used by the component, a-la graphQL:
In a React context, GraphQL clients like Relay gather all the data requirements from components into a single query, and minimize server roundtrips. The alternative is to render then fetch, which I believe is the common approach used in a Svelte context (Sapper use excluded). That approach may lead to this kind of loading experience:
That is probably a ton of work to implement, so it is just really an informative question about whether this has been considered already and whether that is of interest. I believe that having a compiler take care of all those subtle optimizations would be a terrific validation of Svelte approach.
Also putting this here for reference: #3203.
Describe alternatives you've considered
I am unaware of the existing patterns by which this is achieved with Svelte currently. I believe Sapper is taking care of smooth transitions between pages in a SPA, which is one of the use cases React folk cover with a clever use of Relay. They basically fetch the data and the page component in parallel, before doing any rendering. That is possible because they know ahead of time (gathered from the declaration colocated in component) what to fetch.
I am not current with Sapper though so can't tell the extent to which Sapper helps devs ensure a smooth user experience neither compare both approaches.
How important is this feature to you?
Can't say it is important in the sense that I have nothing depending on it. But this is nice to have for sure.
Additional context
That is a talk where the Facebook/React/GraphQL approach is described: https://www.youtube.com/watch?v=Tl0S7QkxFE4
The text was updated successfully, but these errors were encountered: