@modern-js/main-doc 0.0.0-nightly-20240827170702 → 0.0.0-nightly-20240829170706
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/en/apis/app/runtime/core/use-loader.mdx +1 -1
- package/docs/en/components/ssr-monitor.mdx +3 -0
- package/docs/en/configure/app/output/ssg.mdx +52 -141
- package/docs/en/guides/advanced-features/_meta.json +0 -7
- package/docs/en/guides/basic-features/_meta.json +7 -1
- package/docs/en/guides/basic-features/data/data-fetch.mdx +134 -235
- package/docs/en/guides/basic-features/data/data-write.mdx +66 -77
- package/docs/en/guides/basic-features/render/_meta.json +1 -0
- package/docs/en/guides/basic-features/render/ssg.mdx +208 -0
- package/docs/en/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +38 -50
- package/docs/en/guides/basic-features/render/ssr.mdx +301 -0
- package/docs/en/guides/basic-features/render/streaming-ssr.mdx +230 -0
- package/docs/en/guides/basic-features/routes.mdx +275 -263
- package/docs/en/guides/concept/entries.mdx +9 -2
- package/docs/zh/apis/app/runtime/core/use-loader.mdx +1 -1
- package/docs/zh/components/ssr-monitor.mdx +3 -0
- package/docs/zh/configure/app/output/ssg.mdx +49 -139
- package/docs/zh/guides/advanced-features/_meta.json +0 -7
- package/docs/zh/guides/basic-features/_meta.json +7 -1
- package/docs/zh/guides/basic-features/data/data-fetch.mdx +98 -213
- package/docs/zh/guides/basic-features/data/data-write.mdx +54 -55
- package/docs/zh/guides/basic-features/render/_meta.json +1 -0
- package/docs/zh/guides/basic-features/render/ssg.mdx +210 -0
- package/docs/zh/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +16 -26
- package/docs/zh/guides/basic-features/render/ssr.mdx +309 -0
- package/docs/zh/guides/{advanced-features/ssr/stream.mdx → basic-features/render/streaming-ssr.mdx} +22 -37
- package/docs/zh/guides/basic-features/routes.mdx +252 -237
- package/docs/zh/guides/concept/entries.mdx +6 -3
- package/package.json +6 -6
- package/docs/en/guides/advanced-features/ssg.mdx +0 -116
- package/docs/en/guides/advanced-features/ssr/_meta.json +0 -1
- package/docs/en/guides/advanced-features/ssr/index.mdx +0 -23
- package/docs/en/guides/advanced-features/ssr/stream.mdx +0 -248
- package/docs/en/guides/advanced-features/ssr/usage.mdx +0 -341
- package/docs/en/guides/advanced-features/ssr.mdx +0 -555
- package/docs/zh/guides/advanced-features/ssg.mdx +0 -116
- package/docs/zh/guides/advanced-features/ssr/_meta.json +0 -1
- package/docs/zh/guides/advanced-features/ssr/index.mdx +0 -23
- package/docs/zh/guides/advanced-features/ssr/usage.mdx +0 -329
@@ -5,28 +5,15 @@ sidebar_position: 3
|
|
5
5
|
|
6
6
|
# 数据获取
|
7
7
|
|
8
|
-
Modern.js 中提供了开箱即用的数据获取能力,开发者可以通过这些 API
|
8
|
+
Modern.js 中提供了开箱即用的数据获取能力,开发者可以通过这些 API,在项目中获取数据。需要注意的是,这些 API 并不帮助应用发起请求,而是帮助开发者更好地管理数据,提升项目的性能。
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
## Data Loader(推荐)
|
13
|
-
|
14
|
-
Modern.js 推荐使用约定式路由做路由的管理,通过 Modern.js 的[约定式(嵌套)路由](/guides/basic-features/routes#约定式路由),每个路由组件(`layout.ts` 或 `page.ts`)可以有一个同名的 `data` 文件,该 `data` 文件可以导出一个 `loader` 函数,函数会在组件渲染之前执行,为路由组件提供数据。
|
15
|
-
|
16
|
-
:::info
|
17
|
-
Modern.js v1 支持通过 [useLoader](#useloader(旧版)) 获取数据,这已经不是我们推荐的用法。除迁移过程外,不推荐两者混用。
|
18
|
-
|
19
|
-
:::
|
20
|
-
|
21
|
-
:::warning
|
22
|
-
- 在之前的版本中,Modern.js Data Loader 是定义在 `loader` 文件中的,在之后的版本中,我们推荐定义在 `data` 文件中,同时我们会保持对 `loader` 文件的兼容。
|
23
|
-
- 在 `data` 文件中,对应的 `loader` 需要具名导出。
|
10
|
+
## 什么是 Data Loader
|
24
11
|
|
12
|
+
:::note
|
13
|
+
Modern.js v1 项目通过 `useLoader` 获取数据,这已经不是我们推荐的用法,建议迁移到 Data Loader。
|
25
14
|
:::
|
26
15
|
|
27
|
-
|
28
|
-
|
29
|
-
路由组件如 `layout.ts` 或 `page.ts`,可以定义同名的 `data` 文件,`data` 文件中导出一个 `loader` 函数,该函数提供组件所需的数据,然后在路由组件中通过 `useLoaderData` 函数获取数据,如下面示例:
|
16
|
+
Modern.js 推荐使用[约定式路由](/guides/basic-features/routes)做路由的管理,每个路由组件(`layout.ts`,`page.ts` 或 `$.tsx`)都可以有一个同名的 `.data` 文件。这些文件可以导出一个 `loader` 函数,我们称为 Data Loader,它会在对应的路由组件渲染之前执行,为组件提供数据。如下面示例:
|
30
17
|
|
31
18
|
```bash
|
32
19
|
.
|
@@ -39,17 +26,7 @@ Modern.js v1 支持通过 [useLoader](#useloader(旧版)) 获取数据,这
|
|
39
26
|
└── page.data.ts
|
40
27
|
```
|
41
28
|
|
42
|
-
|
43
|
-
|
44
|
-
```ts title="routes/user/page.tsx"
|
45
|
-
import { useLoaderData } from '@modern-js/runtime/router';
|
46
|
-
import type { ProfileData } from './page.data.ts';
|
47
|
-
|
48
|
-
export default function UserPage() {
|
49
|
-
const profileData = useLoaderData() as ProfileData;
|
50
|
-
return <div>{profileData}</div>;
|
51
|
-
}
|
52
|
-
```
|
29
|
+
在 `routes/user/page.data.ts` 文件中,可以导出一个 `loader` 函数:
|
53
30
|
|
54
31
|
```ts title="routes/user/page.data.ts"
|
55
32
|
export type ProfileData = {
|
@@ -62,34 +39,57 @@ export const loader = async (): Promise<ProfileData> => {
|
|
62
39
|
};
|
63
40
|
```
|
64
41
|
|
65
|
-
:::
|
66
|
-
|
42
|
+
:::warning 兼容性
|
43
|
+
- 在之前的版本中,Data Loader 是定义在 `.loader` 文件中的。当前版本中,我们推荐定义在 `.data` 文件中,同时我们会保持对 `.loader` 文件的兼容。
|
44
|
+
- 在 `.loader` 文件中,Data Loader 可以默认导出。但在 `data` 文件中,Data Loader 需要以 `loader` 具名导出。
|
45
|
+
```ts
|
46
|
+
// xxx.loader.ts
|
47
|
+
export default () => {}
|
67
48
|
|
49
|
+
// xxx.data.ts
|
50
|
+
export const loader = () => {}
|
51
|
+
```
|
68
52
|
:::
|
69
53
|
|
70
|
-
在 CSR 环境下,`loader` 函数会在客户端执行,`loader` 函数内可以使用浏览器的 API(但通常不需要,也不推荐)。
|
71
54
|
|
72
|
-
|
55
|
+
在路由组件中,你可以通过 `useLoaderData` 函数获取数据:
|
73
56
|
|
74
|
-
|
75
|
-
|
57
|
+
```ts title="routes/user/page.tsx"
|
58
|
+
import { useLoaderData } from '@modern-js/runtime/router';
|
59
|
+
import type { ProfileData } from './page.data.ts';
|
60
|
+
|
61
|
+
export default function UserPage() {
|
62
|
+
const profileData = useLoaderData() as ProfileData;
|
63
|
+
return <div>{profileData}</div>;
|
64
|
+
}
|
65
|
+
```
|
76
66
|
|
67
|
+
:::caution
|
68
|
+
路由组件和 `.data` 文件共享类型,要使用 `import type` 语法,避免引入预期之外的副作用。
|
77
69
|
:::
|
78
70
|
|
79
|
-
|
71
|
+
在 CSR 项目中,`loader` 函数会在客户端执行,`loader` 函数内可以使用浏览器的 API(但通常不需要,也不推荐)。
|
72
|
+
|
73
|
+
在 SSR 项目中,不管是首屏,还是在客户端的导航,`loader` 函数只会在服务端执行,这里可以调用任意的 Node.js API,同时这里使用的任何依赖和代码都不会包含在客户端的 bundle 中。
|
80
74
|
|
81
|
-
|
75
|
+
:::tip
|
76
|
+
在以后的版本中,Modern.js 可能会支持在 CSR 环境下,`loader` 函数也在服务端运行,以提高性能和安全性,所以这里建议尽可能地保证 `loader` 的纯粹,只做数据获取的场景。
|
77
|
+
:::
|
82
78
|
|
83
|
-
`loader`
|
79
|
+
当在浏览器端导航时,基于[约定式路由](/guides/basic-features/routes),Modern.js 能够支持所有的 `loader` 函数并行执行(请求)。即当访问 `/user/profile` 时,`/user` 和 `/user/profile` 下的 `loader` 函数都会并行执行(请求),这种方式解决了部分请求、渲染瀑布流的问题,较大的提升了页面性能。
|
84
80
|
|
85
|
-
|
81
|
+
## `loader` 函数
|
86
82
|
|
87
|
-
|
83
|
+
`loader` 函数有两个入参,分别用于获取路由参数和请求信息。
|
88
84
|
|
89
|
-
|
90
|
-
|
85
|
+
### params
|
86
|
+
|
87
|
+
`params` 是当路由为[动态路由](/guides/basic-features/routes#动态路由)时的动态路由片段,会作为参数传入 `loader` 函数:
|
88
|
+
|
89
|
+
```tsx title="routes/user/[id]/page.data.ts"
|
91
90
|
import { LoaderFunctionArgs } from '@modern-js/runtime/router';
|
92
91
|
|
92
|
+
// 访问 /user/123 时,函数的参数为 `{ params: { id: '123' } }`
|
93
93
|
export const loader = async ({ params }: LoaderFunctionArgs) => {
|
94
94
|
const { id } = params;
|
95
95
|
const res = await fetch(`https://api/user/${id}`);
|
@@ -97,16 +97,11 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
|
97
97
|
};
|
98
98
|
```
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
#### `request`
|
103
|
-
|
104
|
-
`request` 是一个 [Fetch Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) 实例。
|
100
|
+
### request
|
105
101
|
|
106
|
-
|
102
|
+
`request` 是一个 [Fetch Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) 实例。一个常见的使用场景是通过 `request` 获取查询参数:
|
107
103
|
|
108
104
|
```tsx
|
109
|
-
// routes/user/[id]/page.data.ts
|
110
105
|
import { LoaderFunctionArgs } from '@modern-js/runtime/router';
|
111
106
|
|
112
107
|
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
@@ -116,9 +111,9 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
116
111
|
};
|
117
112
|
```
|
118
113
|
|
119
|
-
|
114
|
+
### 返回值
|
120
115
|
|
121
|
-
`loader`
|
116
|
+
`loader` 函数的返回值**只能是两种数据结构之一**,可序列化的数据对象或者 [Fetch Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) 实例。
|
122
117
|
|
123
118
|
```tsx
|
124
119
|
const loader = async (): Promise<ProfileData> => {
|
@@ -143,19 +138,37 @@ const loader = async (): Promise<ProfileData> => {
|
|
143
138
|
};
|
144
139
|
```
|
145
140
|
|
146
|
-
|
141
|
+
## 在不同环境使用 Data Loader
|
147
142
|
|
148
|
-
|
143
|
+
`loader` 函数可能会在服务端或浏览器端执行。在服务端执行的 `loader` 函数,我们称为 Server Loader,在浏览器端执行的称为 Client Loader。
|
144
|
+
|
145
|
+
在 CSR 应用中,`loader` 函数会在浏览器端执行,即默认都是 Client Loader。
|
146
|
+
|
147
|
+
在 SSR 应用中,`loader` 函数只会在服务端执行,即默认都是 Server Loader。在 SSR 渲染时,Modern.js 会直接在服务端调用对应的 `loader` 函数。在浏览器端切换路由时,Modern.js 会发送一个 http 请求到 SSR 服务,同样在服务端触发 `loader` 函数。
|
148
|
+
|
149
|
+
:::note
|
150
|
+
SSR 应用的 `loader` 函数只在服务端执行可以带来以下好处:
|
151
|
+
|
152
|
+
- **简化使用方式**:保证 SSR 应用获取数据的方式是同构的,开发者无需根据环境区分 `loader` 函数执行的代码。
|
153
|
+
- **减少浏览器端 bundle 体积**:将逻辑代码及其依赖,从浏览器端移动到了服务端。
|
154
|
+
- **提高可维护性**:将逻辑代码移动到服务端,减少了数据逻辑对前端 UI 的直接影响。此外,也避免了浏览器端 bundle 中误引入服务端依赖,或服务端 bundle 中误引入浏览器端依赖的问题。
|
155
|
+
|
156
|
+
:::
|
157
|
+
|
158
|
+
我们推荐在 `loader` 函数中使用 `fetch` API 发起请求。在 Modern.js 中默认对 `fetch` API 做了 polyfill,允许服务端使用该 API 发起请求,这意味你都可以在 CSR 和 SSR 时同构的获取数据:
|
149
159
|
|
150
160
|
```tsx
|
151
161
|
export async function loader() {
|
152
|
-
const res = await fetch('
|
162
|
+
const res = await fetch('URL_ADDRESS');
|
163
|
+
return {
|
164
|
+
message: res.message
|
165
|
+
}
|
153
166
|
}
|
154
167
|
```
|
155
168
|
|
156
|
-
|
169
|
+
## 错误处理
|
157
170
|
|
158
|
-
|
171
|
+
### 基本用法
|
159
172
|
|
160
173
|
在 `loader` 函数中,可以通过 `throw error` 或者 `throw response` 的方式处理错误,当 `loader` 函数中有错误被抛出时,Modern.js 会停止执行当前 `loader` 中的代码,并将前端 UI 切换到定义的 [`ErrorBoundary`](/guides/basic-features/routes#错误处理) 组件:
|
161
174
|
|
@@ -184,10 +197,11 @@ const ErrorBoundary = () => {
|
|
184
197
|
export default ErrorBoundary;
|
185
198
|
```
|
186
199
|
|
187
|
-
|
200
|
+
### 修改 HTTP 状态码
|
201
|
+
|
202
|
+
在 SSR 项目中你可以通过在 `loader` 函数中 `throw response` 的方式,控制页面的状态码,展示对应的 UI。
|
188
203
|
|
189
|
-
|
190
|
-
throw response,页面的状态码将与这个 `response` 保持一致,页面的 UI 也会切换为 `ErrorBoundary`:
|
204
|
+
如以下示例,页面的状态码将与这个 `response` 保持一致,页面也会展示为 `ErrorBoundary` 的 UI:
|
191
205
|
|
192
206
|
```ts
|
193
207
|
// routes/user/profile/page.data.ts
|
@@ -209,9 +223,9 @@ const ErrorBoundary = () => {
|
|
209
223
|
export default ErrorBoundary;
|
210
224
|
```
|
211
225
|
|
212
|
-
|
226
|
+
## 获取上层组件的数据
|
213
227
|
|
214
|
-
|
228
|
+
很多场景下,子组件需要获取到上层组件 `loader` 中的数据,你可以通过 `useRouteLoaderData` 方便地获取到上层组件的数据:
|
215
229
|
|
216
230
|
```tsx
|
217
231
|
// routes/user/profile/page.tsx
|
@@ -229,9 +243,9 @@ export function UserLayout() {
|
|
229
243
|
}
|
230
244
|
```
|
231
245
|
|
232
|
-
`userRouteLoaderData` 接受一个参数 `routeId
|
246
|
+
`userRouteLoaderData` 接受一个参数 `routeId`。在使用约定式路由时,Modern.js 会为你自动生成 `routeId`,`routeId` 的值是对应组件相对于 `src/routes` 的路径,如上面的例子中,子组件想要获取 `routes/user/layout.tsx` 中 loader 返回的数据,`routeId` 的值就是 `user/layout`。
|
233
247
|
|
234
|
-
|
248
|
+
在多入口场景下,`routeId` 的值需要加上对应入口的名称,入口名称非指定情况下一般是入口的目录名,如以下目录结构:
|
235
249
|
|
236
250
|
```bash
|
237
251
|
.
|
@@ -247,11 +261,10 @@ export function UserLayout() {
|
|
247
261
|
如果想获取 `entry1/routes/layout.tsx` 中 `loader` 返回的数据,`routeId` 的值就是 `entry1_layout`。
|
248
262
|
|
249
263
|
|
250
|
-
|
251
|
-
|
252
|
-
:::info
|
253
|
-
此功能目前是实验性质,后续 API 可能有调整。
|
264
|
+
## Loading UI (Experimental)
|
254
265
|
|
266
|
+
:::info Experimental
|
267
|
+
此功能当前是实验性功能,后续 API 可能有调整。
|
255
268
|
:::
|
256
269
|
|
257
270
|
创建 `user/layout.data.ts`,并添加以下代码:
|
@@ -298,39 +311,34 @@ export default function UserLayout() {
|
|
298
311
|
}
|
299
312
|
```
|
300
313
|
|
301
|
-
:::
|
302
|
-
Await 组件的具体用法请查看 [Await](https://reactrouter.com/en/main/components/await)
|
303
|
-
|
304
|
-
defer 的具体用法请查看 [defer](https://reactrouter.com/en/main/guides/deferred)
|
305
|
-
|
314
|
+
:::tip
|
315
|
+
`<Await>` 组件的具体用法请查看 [Await](https://reactrouter.com/en/main/components/await),`defer` 的具体用法请查看 [defer](https://reactrouter.com/en/main/guides/deferred)。
|
306
316
|
:::
|
307
317
|
|
308
|
-
|
318
|
+
## 数据缓存
|
309
319
|
|
310
|
-
在路由导航时,Modern.js
|
320
|
+
在路由导航时,Modern.js 只会加载路由变化的部分的数据。如当前路由是 `a/b`,`a` 路径对应的 Data Loader 已经执行过,当从 `/a/b` 跳转到 `/a/c`时,`a` 路径对应的 Data Loader 不会重新执行,`c` 路径对应的 Data Loader 会执行,并获取了数据。
|
311
321
|
|
312
|
-
|
313
|
-
|
314
|
-
你可能会问,如何更新 `a` 路径对应 Data Loader 的数据?
|
322
|
+
这种默认的优化策略避免了无效重复数据的请求。此时你可能会疑惑,如何更新 `a` 路径对应 Data Loader 的数据?
|
315
323
|
|
316
324
|
在 Modern.js 中,以下几种情况,Modern.js 会重新加载对应路由路径的数据:
|
317
325
|
|
318
326
|
1. 在 [Data Action](/guides/basic-features/data/data-write.md) 触发后
|
319
|
-
2. URL
|
327
|
+
2. URL 参数发生变化后
|
320
328
|
3. 用户点击的链接与当前页面的 URL 相同
|
321
|
-
4. 在路由组件中定义了 [`shouldRevalidate`](#/shouldrevalidate) 函数,该函数返回 true
|
329
|
+
4. 在路由组件中定义了 [`shouldRevalidate`](#/shouldrevalidate) 函数,该函数返回 `true`
|
322
330
|
|
323
|
-
:::
|
331
|
+
:::tip
|
324
332
|
如果你在路由上定义了 [`shouldRevalidate`](#/shouldrevalidate) 函数,会先检查该函数,判断是否需要重新加载数据。
|
325
333
|
:::
|
326
334
|
|
327
335
|
### `shouldRevalidate`
|
328
336
|
|
329
337
|
:::warning
|
330
|
-
目前 `shouldRevalidate`
|
338
|
+
目前 `shouldRevalidate` 只会在 CSR 和 Streaming SSR 下生效。
|
331
339
|
:::
|
332
340
|
|
333
|
-
|
341
|
+
在路由组件(`layout.tsx`,`page.tsx`,`$.tsx`)中,我们可以导出一个 `shouldRevalidate` 函数。每次项目中的路由变化时,这个函数会被触发,该函数可以控制要重新加载哪些路由中的数据。如果这个函数返回 `true`,Modern.js 就会重新加载对应路由的数据。
|
334
342
|
|
335
343
|
```ts title="routes/user/layout.tsx"
|
336
344
|
import type { ShouldRevalidateFunction } from '@modern-js/runtime/router';
|
@@ -350,71 +358,13 @@ export const shouldRevalidate: ShouldRevalidateFunction = ({
|
|
350
358
|
};
|
351
359
|
```
|
352
360
|
|
353
|
-
:::
|
354
|
-
`shouldRevalidate` 函数的更多信息可以参考 [react-router](https://reactrouter.com/en/main/route/should-revalidate)
|
355
|
-
:::
|
356
|
-
|
357
|
-
### Client Loader
|
358
|
-
|
359
|
-
:::info
|
360
|
-
1. 这个 feature 需要 x.36.0 以上版本,推荐使用框架最新版本
|
361
|
-
2. 只有 SSR 项目中有 Client Loader,CSR 项目中可以认为默认就是 Client Loader
|
362
|
-
3. 这个特性可以渐进使用,并不是每个项目都需要,具体可以看下面文档适用场景的说明
|
363
|
-
|
364
|
-
:::
|
365
|
-
|
366
|
-
#### 适用场景
|
367
|
-
|
368
|
-
在 SSR 项目中,Data Loader 中的代码只会在服务端执行,当客户端进行 SPA 导航时,
|
369
|
-
框架会发送一个 http 请求到 SSR 服务,触发 Data Loader 的执行,
|
370
|
-
但有些场景下,我们可能期望在客户端发送的请求不经过 SSR 服务,直接请求数据源。
|
371
|
-
|
372
|
-
:::info
|
373
|
-
为什么 SSR 项目中 Data Loader 只会在服务端执行可参考 [常见问题](#常见问题)
|
374
|
-
|
375
|
-
:::
|
376
|
-
|
377
|
-
例如以下场景:
|
378
|
-
|
379
|
-
1. 在 SSR 降级时,不希望框架向 SSR 服务发送请求获取数据,希望能直接请求后端服务。
|
380
|
-
2. 在客户端有一些缓存,不希望请求 SSR 服务获取数据。
|
381
|
-
|
382
|
-
这些场景下,我们可以使用 Client Loader。添加 Client Loader 后,会调用 Client Loader 中的代码,而不再像 SSR 服务发送请求:
|
383
|
-
|
384
|
-
1. SSR 降级为 CSR 后,在客户端获取数据时,会执行 Client Loader 代替框架发送请求到 Data Loader(Server) 获取数据。
|
385
|
-
2. SSR 项目进行 SPA 跳转时,获取数据,会执行 Clinet Loader。
|
386
|
-
|
387
|
-
|
388
|
-
#### 使用方式
|
389
|
-
|
390
|
-
:::warning
|
391
|
-
要使用 client loader,必须有对应的 server loader(data loader)
|
361
|
+
:::tip
|
362
|
+
`shouldRevalidate` 函数的更多信息可以参考 [react-router](https://reactrouter.com/en/main/route/should-revalidate)。
|
392
363
|
:::
|
393
364
|
|
394
|
-
|
395
|
-
|
396
|
-
- 将 `.loader.ts` 文件重命名为 `.data.ts`
|
397
|
-
- 将文件中的代码做以下改动:
|
398
|
-
```ts
|
399
|
-
// xxx.loader.ts
|
400
|
-
export default () => {}
|
401
|
-
|
402
|
-
// xxx.data.ts
|
403
|
-
export const loader = () => {}
|
404
|
-
```
|
365
|
+
## 错误用法
|
405
366
|
|
406
|
-
|
407
|
-
2. 添加 client loader,client loader API 中的入参和 data loader 是一致的。
|
408
|
-
|
409
|
-
```
|
410
|
-
// xxx.data.client.ts
|
411
|
-
export const loader = () => {}
|
412
|
-
```
|
413
|
-
|
414
|
-
|
415
|
-
### 错误用法
|
416
|
-
|
417
|
-
1. `loader` 中只能返回可序列化的数据,在 SSR 环境下,`loader` 函数的返回值会被序列化为 JSON 字符串,然后在客户端被反序列化为对象。因此,`loader` 函数中不能返回不可序列化的数据(如函数)。
|
367
|
+
1. `loader` 中只能返回可序列化的数据,在 SSR 环境下,`loader` 函数的返回值会被序列化为 JSON 字符串,然后在浏览器端被反序列化为对象。因此,`loader` 函数中不能返回不可序列化的数据(如函数)。
|
418
368
|
|
419
369
|
:::warning
|
420
370
|
目前 CSR 下没有这个限制,但我们强烈推荐你遵循该限制,且未来我们可能在 CSR 下也加上该限制。
|
@@ -475,75 +425,10 @@ export const loader = async (): Promise<ProfileData> => {
|
|
475
425
|
|
476
426
|
4. 在服务端运行时,`loader` 函数会被打包为一个统一的 bundle,所以我们不推荐服务端的代码使用 `__filename` 和 `__dirname`。
|
477
427
|
|
478
|
-
|
479
|
-
|
480
|
-
1. `loader` 和 BFF 函数的关系
|
481
|
-
|
482
|
-
在 CSR 项目中,`loader` 在客户端执行,在 `loader` 可以直接调用 BFF 函数进行接口请求。
|
483
|
-
|
484
|
-
在 SSR 项目中,每个 `loader` 也是一个服务端接口,我们推荐使用 `loader` 替代 http method 为 `get` 的 BFF 函数,作为接口层,避免多一层转发和执行。
|
428
|
+
## 常见问题
|
485
429
|
|
430
|
+
1. `loader` 和 BFF 函数的关系是什么?
|
486
431
|
|
487
|
-
|
432
|
+
在 CSR 项目中,`loader` 在浏览器端执行,在 `loader` 可以直接调用 BFF 函数进行接口请求。
|
488
433
|
|
489
|
-
|
490
|
-
|
491
|
-
- **简化使用方式**,有 server loader 后,SSR 阶段和 CSR 阶段数据获取的操作都可以放在 server loader 中(真实的调用由框架层去做),server loader 中的代码无需关心是在浏览器环境中还是服务端环境中。
|
492
|
-
- **减少网络请求的数据**,作为 BFF 层,可以减少前端运行时需要获取的数据。
|
493
|
-
- **减少客户端 bundle 体积**,将逻辑代码及其依赖,从客户端移动到了服务端。
|
494
|
-
- **提高可维护性**,将逻辑代码移动到服务端,减少了数据逻辑对前端 UI 的直接影响。此外,也避免了客户端 bundle 中误引入服务端依赖,或服务端 bundle 中误引入客户端依赖的问题。
|
495
|
-
|
496
|
-
|
497
|
-
## useLoader(旧版)
|
498
|
-
|
499
|
-
**`useLoader`** 是 Modern.js 老版本中的 API。该 API 是一个 React Hook,专门提供给 SSR 应用使用,让开发者能同构的在组件中获取数据。
|
500
|
-
|
501
|
-
:::tip
|
502
|
-
CSR 的项目没有必要使用 `useLoader` 获取数据,且在使用 Rspack 作为打包工具时,`useLoader` 不支持使用。
|
503
|
-
:::
|
504
|
-
|
505
|
-
以下是一个最简单的例子:
|
506
|
-
|
507
|
-
```tsx
|
508
|
-
import { useLoader } from '@modern-js/runtime';
|
509
|
-
|
510
|
-
export default () => {
|
511
|
-
const { data } = useLoader(async () => {
|
512
|
-
console.log('fetch in useLoader');
|
513
|
-
|
514
|
-
// 这里没有发送真实的请求,只是返回了一个写死的数据。
|
515
|
-
// 真实项目中,应该返回从远端获取的数据。
|
516
|
-
return {
|
517
|
-
name: 'Modern.js',
|
518
|
-
};
|
519
|
-
});
|
520
|
-
|
521
|
-
return <div>Hello, {data?.name}</div>;
|
522
|
-
};
|
523
|
-
```
|
524
|
-
|
525
|
-
上述代码启动后,访问页面。可以看到在终端输出了日志,而在浏览器终端却没有打印日志。
|
526
|
-
|
527
|
-
这是因为 Modern.js 在服务端渲染时,在会收集 `useLoader` 返回的数据,并将数据注入到响应的 HTML 中。如果 SSR 渲染成功,在 HTML 中可以看到如下代码片段:
|
528
|
-
|
529
|
-
```html
|
530
|
-
<script>
|
531
|
-
window._SSR_DATA = {};
|
532
|
-
</script>
|
533
|
-
```
|
534
|
-
|
535
|
-
在这全局变量中,记录了每一份数据,而在浏览器端渲染的过程中,会优先使用这份数据。如果数据不存在,则会重新执行 `useLoader` 函数。
|
536
|
-
|
537
|
-
:::note
|
538
|
-
在构建阶段,Modern.js 会自动为每个 `useLoader` 生成一个 Loader ID,并注入到 SSR 和 CSR 的 JS Bundle 中,用来关联 Loader 和数据。
|
539
|
-
|
540
|
-
:::
|
541
|
-
|
542
|
-
相比于 Next.js 中的 `getServerSideProps`,在渲染前预先获取数据。使用 `useLoader`,可以在组件中获取局部 UI 所需要的数据,而不用将数据层层传递。同样,也不会因为不同路由需要不同数据请求,而在最外层的数据获取函数中添加冗余的逻辑。当然 `useLoader` 也存在一些问题,例如服务端代码 Treeshaking 困难,服务端需要多一次预渲染等。
|
543
|
-
|
544
|
-
Modern.js 在新版本中,设计了全新的 Loader 方案。新方案解决了这些问题,并能够配合**嵌套路由**,对页面性能做优化。
|
545
|
-
|
546
|
-
:::note
|
547
|
-
详细 API 可以查看 [useLoader](/apis/app/runtime/core/use-loader)。
|
548
|
-
|
549
|
-
:::
|
434
|
+
在 SSR 项目中,每个 `loader` 也是一个服务端接口。我们推荐使用 `loader` 替代 http method 为 `get` 的 BFF 函数,避免多一层转发和执行。
|