mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
docs: improve explanation of ssr + data fetching (#29010)
This commit is contained in:
parent
e553032df6
commit
90025976f8
@ -8,21 +8,17 @@ Nuxt comes with two composables and a built-in library to perform data-fetching
|
|||||||
|
|
||||||
In a nutshell:
|
In a nutshell:
|
||||||
|
|
||||||
- [`useFetch`](/docs/api/composables/use-fetch) is the most straightforward way to handle data fetching in a component setup function.
|
- [`$fetch`](/docs/api/utils/dollarfetch) is the simplest way to make a network request.
|
||||||
- [`$fetch`](/docs/api/utils/dollarfetch) is great to make network requests based on user interaction.
|
- [`useFetch`](/docs/api/composables/use-fetch) is wrapper around `$fetch` that fetches data only once in [universal rendering](/docs/guide/concepts/rendering#universal-rendering).
|
||||||
- [`useAsyncData`](/docs/api/composables/use-async-data), combined with `$fetch`, offers more fine-grained control.
|
- [`useAsyncData`](/docs/api/composables/use-async-data) is similar to `useFetch` but offers more fine-grained control.
|
||||||
|
|
||||||
Both `useFetch` and `useAsyncData` share a common set of options and patterns that we will detail in the last sections.
|
Both `useFetch` and `useAsyncData` share a common set of options and patterns that we will detail in the last sections.
|
||||||
|
|
||||||
Before that, it's imperative to know why these composables exist in the first place.
|
## The need for `useFetch` and `useAsyncData`
|
||||||
|
|
||||||
## Why use specific composables for data fetching?
|
Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the [`$fetch` function](/docs/api/utils/dollarfetch) is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This can cause hydration issues, increase the time to interactivity and cause unpredictable behavior.
|
||||||
|
|
||||||
Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the [`$fetch` function](/docs/api/utils/dollarfetch) is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This is why Nuxt offers specific data fetching composables so data is fetched only once.
|
The [`useFetch`](/docs/api/composables/use-fetch) and [`useAsyncData`](/docs/api/composables/use-async-data) composables solve this problem by ensuring that if an API call is made on the server, the data is forwarded to the client in the payload.
|
||||||
|
|
||||||
### Network calls duplication
|
|
||||||
|
|
||||||
The [`useFetch`](/docs/api/composables/use-fetch) and [`useAsyncData`](/docs/api/composables/use-async-data) composables ensure that once an API call is made on the server, the data is properly forwarded to the client in the payload.
|
|
||||||
|
|
||||||
The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/docs/api/composables/use-nuxt-app#payload). It is used on the client to avoid refetching the same data when the code is executed in the browser [during hydration](/docs/guide/concepts/rendering#universal-rendering).
|
The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/docs/api/composables/use-nuxt-app#payload). It is used on the client to avoid refetching the same data when the code is executed in the browser [during hydration](/docs/guide/concepts/rendering#universal-rendering).
|
||||||
|
|
||||||
@ -30,17 +26,71 @@ The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/
|
|||||||
Use the [Nuxt DevTools](https://devtools.nuxt.com) to inspect this data in the **Payload tab**.
|
Use the [Nuxt DevTools](https://devtools.nuxt.com) to inspect this data in the **Payload tab**.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
```vue [app.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { data } = await useFetch('/api/data')
|
||||||
|
|
||||||
|
async function handleFormSubmit() {
|
||||||
|
const res = await $fetch('/api/submit', {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
// My form data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="data == null">
|
||||||
|
No data
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<form @submit="handleFormSubmit">
|
||||||
|
<!-- form input tags -->
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
In the example above, `useFetch` would make sure that the request would occur in the server and is properly forwarded to the browser. `$fetch` has no such mechanism and is a better option to use when the request is solely made from the browser.
|
||||||
|
|
||||||
### Suspense
|
### Suspense
|
||||||
|
|
||||||
Nuxt uses Vue’s [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense) component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-calls basis.
|
Nuxt uses Vue’s [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense) component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-call basis.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
You can add the [`<NuxtLoadingIndicator>`](/docs/api/components/nuxt-loading-indicator) to add a progress bar between page navigations.
|
You can add the [`<NuxtLoadingIndicator>`](/docs/api/components/nuxt-loading-indicator) to add a progress bar between page navigations.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
## `$fetch`
|
||||||
|
|
||||||
|
Nuxt includes the [ofetch](https://github.com/unjs/ofetch) library, and is auto-imported as the `$fetch` alias globally across your application.
|
||||||
|
|
||||||
|
```vue twoslash [pages/todos.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
async function addTodo() {
|
||||||
|
const todo = await $fetch('/api/todos', {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
// My todo data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](#the-need-for-usefetch-and-useasyncdata). :br
|
||||||
|
It is recommended to use `$fetch` for client-side interactions (event based) or combined with [`useAsyncData`](#useasyncdata) when fetching the initial component data.
|
||||||
|
::
|
||||||
|
|
||||||
|
::read-more{to="/docs/api/utils/dollarfetch"}
|
||||||
|
Read more about `$fetch`.
|
||||||
|
::
|
||||||
|
|
||||||
## `useFetch`
|
## `useFetch`
|
||||||
|
|
||||||
The [`useFetch`](/docs/api/composables/use-fetch) composable is the most straightforward way to perform data fetching.
|
The [`useFetch`](/docs/api/composables/use-fetch) composable uses `$fetch` under-the-hood to make SSR-safe network calls in the setup function.
|
||||||
|
|
||||||
```vue twoslash [app.vue]
|
```vue twoslash [app.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -62,32 +112,6 @@ Watch the video from Alexander Lichter to avoid using `useFetch` the wrong way!
|
|||||||
|
|
||||||
:link-example{to="/docs/examples/features/data-fetching"}
|
:link-example{to="/docs/examples/features/data-fetching"}
|
||||||
|
|
||||||
## `$fetch`
|
|
||||||
|
|
||||||
Nuxt includes the [ofetch](https://github.com/unjs/ofetch) library, and is auto-imported as the `$fetch` alias globally across your application. It's what `useFetch` uses behind the scenes.
|
|
||||||
|
|
||||||
```vue twoslash [pages/todos.vue]
|
|
||||||
<script setup lang="ts">
|
|
||||||
async function addTodo() {
|
|
||||||
const todo = await $fetch('/api/todos', {
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
// My todo data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
::warning
|
|
||||||
Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](#why-use-specific-composables-for-data-fetching). :br
|
|
||||||
It is recommended to use `$fetch` for client-side interactions (event based) or combined with [`useAsyncData`](#useasyncdata) when fetching the initial component data.
|
|
||||||
::
|
|
||||||
|
|
||||||
::read-more{to="/docs/api/utils/dollarfetch"}
|
|
||||||
Read more about `$fetch`.
|
|
||||||
::
|
|
||||||
|
|
||||||
## `useAsyncData`
|
## `useAsyncData`
|
||||||
|
|
||||||
The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved.
|
The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved.
|
||||||
|
@ -11,18 +11,41 @@ By default, Nuxt uses **universal rendering** to provide better user experience,
|
|||||||
|
|
||||||
## Universal Rendering
|
## Universal Rendering
|
||||||
|
|
||||||
When the browser requests a URL with universal (server-side + client-side) rendering enabled, the server returns a fully rendered HTML page to the browser. Whether the page has been generated in advance and cached or is rendered on the fly, at some point, Nuxt has run the JavaScript (Vue.js) code in a server environment, producing an HTML document. Users immediately get the content of our application, contrary to client-side rendering. This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications.
|
This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications. When the browser requests a URL with universal rendering enabled, Nuxt runs the JavaScript (Vue.js) code in a server environment and returns a fully rendered HTML page to the browser. Nuxt may also return a fully rendered HTML page from a cache if the page was generated in advance. Users immediately get the entirety of the initial content of the application, contrary to client-side rendering.
|
||||||
|
|
||||||
To not lose the benefits of the client-side rendering method, such as dynamic interfaces and pages transitions, the Client (browser) loads the JavaScript code that runs on the Server in the background once the HTML document has been downloaded. The browser interprets it again (hence **Universal rendering**) and Vue.js takes control of the document and enables interactivity.
|
Once the HTML document has been downloaded, the browser interprets this and Vue.js takes control of the document. The same JavaScript code that once ran on the server runs on the client (browser) **again** in the background now enabling interactivity (hence **Universal rendering**) by binding its listeners to the HTML. This is called **Hydration**. When hydration is complete, the page can enjoy benefits such as dynamic interfaces and page transitions.
|
||||||
|
|
||||||
Making a static page interactive in the browser is called "Hydration".
|
|
||||||
|
|
||||||
Universal rendering allows a Nuxt application to provide quick page load times while preserving the benefits of client-side rendering. Furthermore, as the content is already present in the HTML document, crawlers can index it without overhead.
|
Universal rendering allows a Nuxt application to provide quick page load times while preserving the benefits of client-side rendering. Furthermore, as the content is already present in the HTML document, crawlers can index it without overhead.
|
||||||
|
|
||||||
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/ssr.svg)
|
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/ssr.svg)
|
||||||
|
|
||||||
|
**What's server-rendered and what's client-rendered?**
|
||||||
|
|
||||||
|
It is normal to ask which parts of a Vue file runs on the server and/or the client in universal rendering mode.
|
||||||
|
|
||||||
|
```vue [app.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
const counter = ref(0); // executes in server and client environments
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
counter.value++; // executes only in a client environment
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>Count: {{ counter }}</p>
|
||||||
|
<button @click="handleClick">Increment</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
On the initial request, the `counter` ref is initialized in the server since it is rendered inside the `<p>` tag. The contents of `handleClick` is never executed here. During hydration in the browser, the `counter` ref is re-initialized. The `handleClick` finally binds itself to the button; Therefore it is reasonable to deduce that the body of `handleClick` will always run in a browser environment.
|
||||||
|
|
||||||
|
[Middlewares](/docs/guide/directory-structure/middleware) and [pages](/docs/guide/directory-structure/pages) run in the server and on the client during hydration. [Plugins](/docs/guide/directory-structure/plugins) can be rendered on the server or client or both. [Components](/docs/guide/directory-structure/components) can be forced to run on the client only as well. [Composables](/docs/guide/directory-structure/composables) and [utilities](/docs/guide/directory-structure/utils) are rendered based on the context of their usage.
|
||||||
|
|
||||||
**Benefits of server-side rendering:**
|
**Benefits of server-side rendering:**
|
||||||
- **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated content. At the same time, Nuxt preserves the interactivity of a web application when the hydration process happens.
|
- **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated content. At the same time, Nuxt preserves the interactivity of a web application during the hydration process.
|
||||||
- **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly.
|
- **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly.
|
||||||
|
|
||||||
**Downsides of server-side rendering:**
|
**Downsides of server-side rendering:**
|
||||||
|
Loading…
Reference in New Issue
Block a user