You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -59,9 +59,9 @@ By using the route location to fetch data, we ensure a consistent relationship b
59
59
60
60
It's important to avoid side effects in the loader function. Don't call `watch`, or create reactive effects like `ref`, `toRefs()`, `computed`, etc.
61
61
62
-
##Accessing Global Properties
62
+
###Global Properties
63
63
64
-
In the loader function, you can access global properties like the router instance, a store, etc. This is because using `inject()` within the loader function **is possible**. Since loaders are asynchronous, make sure you are using the `inject` function **before any `await`**:
64
+
In the loader function, you can access global properties like the router instance, a store, etc. This is because using `inject()` within the loader function **is possible**, just like within navigation guards. Since loaders are asynchronous, make sure you are using the `inject` function **before any `await`**:
65
65
66
66
```ts twoslash
67
67
import'unplugin-vue-router/client'
@@ -70,7 +70,7 @@ import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic'
Since loaders happen within the context of a navigation, you can control the navigation by returning a `NavigationResult` object. This is similar to returning a value in a navigation guard
97
+
98
+
```ts{1,8,9}
99
+
import { NavigationResult } from 'unplugin-vue-router/data-loaders'
// same as returning '/login' in a navigation guard
107
+
return new NavigationResult('/login')
108
+
}
109
+
throw err // unexpected error
110
+
}
111
+
})
112
+
```
113
+
114
+
::: tip
115
+
116
+
Note that [lazy loaders](#lazy-loaders) cannot control the navigation since they do not block it.
117
+
118
+
:::
119
+
120
+
Read more in the [Navigation Aware](./navigation-aware.md) section.
121
+
122
+
### Errors
123
+
124
+
Any thrown Error will abort the navigation, just like in navigation guards. They will trigger the `router.onError` handler if defined.
125
+
126
+
::: tip
127
+
128
+
Note that [lazy loaders](#lazy-loaders) cannot control the navigation since they do not block it, any thrown error will appear in the `error` property and not abort the navigation nor appear in the `router.onError` handler.
129
+
130
+
:::
131
+
132
+
It's possible to define expected errors so they don't abort the navigation. You can read more about it in the [Error Handling](./error-handling.md) section.
133
+
94
134
## Options
95
135
96
-
All
136
+
Data loaders are designed to be flexible and allow for customization. Despite being navigation-centric, they can be used outside of a navigation and this flexibility is key to their design.
137
+
138
+
### Non blocking loaders with `lazy`
139
+
140
+
By default, loaders are _non-lazy_, meaning they will block the navigation until the data is fetched. But this behavior can be changed by setting the `lazy` option to `true`.
141
+
142
+
```vue{10,16} twoslash
143
+
<script lang="ts">
144
+
// ---cut-start---
145
+
import 'unplugin-vue-router/client'
146
+
import './typed-router.d'
147
+
import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic'
148
+
// ---cut-end---
149
+
import { getUserById } from '../api'
150
+
151
+
export const useUserData = defineBasicLoader(
152
+
'/users/[id]',
153
+
async (to) => {
154
+
const user = await getUserById(to.params.id)
155
+
return user
156
+
},
157
+
{ lazy: true } // 👈 marked as lazy
158
+
)
159
+
</script>
160
+
161
+
<script setup>
162
+
// Differently from the example above, `user.value` can and will be initially `undefined`
This patterns is useful to avoid blocking the navigation while _non critical data_ is being fetched. It will display the page earlier while lazy loaders are still loading and you are able to display loader indicators thanks to the `isLoading` property.
171
+
172
+
Since lazy loaders do not block the navigation, any thrown error will not abort the navigation nor appear in the `router.onError` handler. Instead, the error will be available in the `error` property.
173
+
174
+
Note this still allows for having different behavior during SSR and client side navigation, e.g.: if we want to wait for the loader during SSR but not during client side navigation:
175
+
176
+
```ts{6-7}
177
+
export const useUserData = defineBasicLoader(
178
+
async (to) => {
179
+
// ...
180
+
},
181
+
{
182
+
lazy: !import.env.SSR, // Vite specific
183
+
}
184
+
)
185
+
```
186
+
187
+
You can even pass a function to `lazy` to determine if the loader should be lazy or not based on each load/navigation:
This is really useful when you can display the old data while fetching the new one and some of the parts of the page require the route to be updated like search results and pagination buttons. By using a lazy loader only when the route changes, the pagination can be updated immediately while the search results are being fetched, allowing the user to click multiple times on the pagination buttons without waiting for the search results to be fetched.
202
+
203
+
### Delaying data updates with `commit`
204
+
205
+
By default, the data is updated only once all loaders are resolved. This is useful to avoid displaying partially loaded data or worse, incoherent data aggregation.
206
+
207
+
Sometimes you might want to immediately update the data as soon as it's available, even if other loaders are still pending. This can be achieved by changing the `commit` option:
In the case of [lazy loaders](#lazy-loaders), they also default to `commit: 'after-load'`. They will commit after all other non-lazy loaders if they can but since they are not awaited, they might not be able to. In this case, the data will be available when finished loading, which can be much later than the navigation is completed.
226
+
227
+
### Server optimization with `server`
228
+
229
+
During SSR, it might be more performant to avoid loading data that isn't critical for the initial render. This can be achieved by setting the `server` option to `false`. That will completely skip the loader during SSR.
230
+
231
+
```ts{3} twoslash
232
+
import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic'
233
+
interface Book {
234
+
title: string
235
+
isbn: string
236
+
description: string
237
+
}
238
+
function fetchRelatedBooks(id: string | string[]): Promise<Book[]> {
239
+
return {} as any
240
+
}
241
+
// ---cut---
242
+
export const useRelatedBooks = defineBasicLoader(
243
+
(to) => fetchRelatedBooks(to.params.id),
244
+
{ server: false }
245
+
)
246
+
```
247
+
248
+
You can read more about server side rendering in the [SSR](./ssr.md) section.
97
249
98
250
## Connecting a loader to a page
99
251
100
-
## Why
252
+
The router needs to know what loaders should be ran with which page. This is achieved in two ways:
253
+
254
+
-**Automatically**: when a loader is exported from a page component that is lazy loaded, the loader will be automatically connected to the page
255
+
256
+
::: code-group
257
+
258
+
```ts{8} [router.ts]
259
+
import { createRouter, createWebHistory } from 'vue-router'
Copy file name to clipboardExpand all lines: docs/data-loaders/navigation-aware.md
+4Lines changed: 4 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -5,3 +5,7 @@
5
5
## Consistent updates
6
6
7
7
## Lazy loaders
8
+
9
+
## Loading after the navigation
10
+
11
+
It's possible to not start loading the data until the navigation is done. To do this, simply **do not attach the loader to the page**. It will eventually start loading when the page is mounted.
0 commit comments