@modern-js/main-doc 2.58.3 → 2.59.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. package/docs/en/apis/app/runtime/core/use-loader.mdx +1 -1
  2. package/docs/en/components/init-app.mdx +0 -1
  3. package/docs/en/components/init-rspack-app.mdx +0 -1
  4. package/docs/en/components/ssr-monitor.mdx +3 -0
  5. package/docs/en/configure/app/output/ssg.mdx +52 -141
  6. package/docs/en/configure/app/tools/tailwindcss.mdx +1 -1
  7. package/docs/en/guides/advanced-features/_meta.json +0 -8
  8. package/docs/en/guides/advanced-features/rsbuild-plugin.mdx +2 -2
  9. package/docs/en/guides/advanced-features/rspack-start.mdx +6 -14
  10. package/docs/en/guides/basic-features/_meta.json +31 -9
  11. package/docs/en/guides/basic-features/css/_meta.json +1 -0
  12. package/docs/en/guides/basic-features/css/css-in-js.mdx +34 -0
  13. package/docs/en/guides/basic-features/{css-modules.mdx → css/css-modules.mdx} +0 -4
  14. package/docs/en/guides/basic-features/css/css.mdx +25 -0
  15. package/docs/en/guides/basic-features/{css.mdx → css/tailwindcss.mdx} +5 -66
  16. package/docs/en/guides/basic-features/data/data-fetch.mdx +134 -235
  17. package/docs/en/guides/basic-features/data/data-write.mdx +66 -77
  18. package/docs/en/guides/basic-features/debug/_meta.json +1 -0
  19. package/docs/en/guides/basic-features/debug/rsdoctor.mdx +57 -0
  20. package/docs/en/guides/{advanced-features → basic-features/debug}/using-storybook.mdx +2 -0
  21. package/docs/en/guides/basic-features/render/_meta.json +1 -0
  22. package/docs/en/guides/basic-features/render/ssg.mdx +208 -0
  23. package/docs/en/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +38 -50
  24. package/docs/en/guides/basic-features/render/ssr.mdx +301 -0
  25. package/docs/en/guides/basic-features/render/streaming-ssr.mdx +230 -0
  26. package/docs/en/guides/basic-features/routes.mdx +275 -263
  27. package/docs/en/guides/basic-features/static-assets/_meta.json +1 -0
  28. package/docs/en/guides/basic-features/static-assets.mdx +1 -1
  29. package/docs/en/guides/basic-features/testing/_meta.json +1 -0
  30. package/docs/en/guides/basic-features/testing/cypress.mdx +95 -0
  31. package/docs/en/guides/basic-features/testing/jest.mdx +148 -0
  32. package/docs/en/guides/basic-features/testing/playwright.mdx +111 -0
  33. package/docs/en/guides/basic-features/testing/vitest.mdx +100 -0
  34. package/docs/en/guides/concept/entries.mdx +9 -2
  35. package/docs/en/guides/get-started/quick-start.mdx +1 -1
  36. package/docs/en/guides/get-started/tech-stack.mdx +4 -4
  37. package/docs/en/guides/topic-detail/generator/create/config.mdx +0 -10
  38. package/docs/en/guides/topic-detail/generator/create/use.mdx +0 -1
  39. package/docs/en/tutorials/first-app/c03-css.mdx +1 -1
  40. package/docs/zh/apis/app/runtime/core/use-loader.mdx +1 -1
  41. package/docs/zh/components/init-app.mdx +0 -1
  42. package/docs/zh/components/init-rspack-app.mdx +0 -1
  43. package/docs/zh/components/ssr-monitor.mdx +3 -0
  44. package/docs/zh/configure/app/output/ssg.mdx +49 -139
  45. package/docs/zh/configure/app/tools/tailwindcss.mdx +1 -1
  46. package/docs/zh/guides/advanced-features/_meta.json +0 -8
  47. package/docs/zh/guides/advanced-features/rsbuild-plugin.mdx +2 -2
  48. package/docs/zh/guides/advanced-features/rspack-start.mdx +7 -16
  49. package/docs/zh/guides/basic-features/_meta.json +31 -9
  50. package/docs/zh/guides/basic-features/css/_meta.json +1 -0
  51. package/docs/zh/guides/basic-features/css/css-in-js.mdx +34 -0
  52. package/docs/zh/guides/basic-features/css/css.mdx +25 -0
  53. package/docs/zh/guides/basic-features/{css.mdx → css/tailwindcss.mdx} +3 -64
  54. package/docs/zh/guides/basic-features/data/data-fetch.mdx +96 -211
  55. package/docs/zh/guides/basic-features/data/data-write.mdx +54 -55
  56. package/docs/zh/guides/basic-features/debug/_meta.json +1 -0
  57. package/docs/zh/guides/basic-features/debug/rsdoctor.mdx +57 -0
  58. package/docs/zh/guides/{advanced-features → basic-features/debug}/using-storybook.mdx +1 -1
  59. package/docs/zh/guides/basic-features/render/_meta.json +1 -0
  60. package/docs/zh/guides/basic-features/render/ssg.mdx +210 -0
  61. package/docs/zh/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +16 -26
  62. package/docs/zh/guides/basic-features/render/ssr.mdx +309 -0
  63. package/docs/zh/guides/{advanced-features/ssr/stream.mdx → basic-features/render/streaming-ssr.mdx} +22 -37
  64. package/docs/zh/guides/basic-features/routes.mdx +252 -237
  65. package/docs/zh/guides/basic-features/static-assets/_meta.json +1 -0
  66. package/docs/zh/guides/basic-features/static-assets.mdx +2 -6
  67. package/docs/zh/guides/basic-features/testing/_meta.json +1 -0
  68. package/docs/zh/guides/basic-features/testing/cypress.mdx +95 -0
  69. package/docs/zh/guides/basic-features/testing/jest.mdx +148 -0
  70. package/docs/zh/guides/basic-features/testing/playwright.mdx +112 -0
  71. package/docs/zh/guides/basic-features/testing/vitest.mdx +100 -0
  72. package/docs/zh/guides/concept/entries.mdx +6 -3
  73. package/docs/zh/guides/get-started/quick-start.mdx +1 -1
  74. package/docs/zh/guides/get-started/tech-stack.mdx +8 -8
  75. package/docs/zh/guides/topic-detail/generator/create/config.mdx +0 -10
  76. package/docs/zh/guides/topic-detail/generator/create/use.mdx +0 -1
  77. package/docs/zh/tutorials/first-app/c03-css.mdx +1 -1
  78. package/i18n.json +16 -4
  79. package/package.json +6 -6
  80. package/docs/en/apis/app/hooks/config/storybook.mdx +0 -37
  81. package/docs/en/guides/advanced-features/ssg.mdx +0 -116
  82. package/docs/en/guides/advanced-features/ssr/_meta.json +0 -1
  83. package/docs/en/guides/advanced-features/ssr/index.mdx +0 -23
  84. package/docs/en/guides/advanced-features/ssr/stream.mdx +0 -248
  85. package/docs/en/guides/advanced-features/ssr/usage.mdx +0 -341
  86. package/docs/en/guides/advanced-features/ssr.mdx +0 -555
  87. package/docs/zh/apis/app/hooks/config/storybook.mdx +0 -38
  88. package/docs/zh/guides/advanced-features/ssg.mdx +0 -116
  89. package/docs/zh/guides/advanced-features/ssr/_meta.json +0 -1
  90. package/docs/zh/guides/advanced-features/ssr/index.mdx +0 -23
  91. package/docs/zh/guides/advanced-features/ssr/usage.mdx +0 -329
  92. /package/docs/en/guides/basic-features/{mock.mdx → debug/mock.mdx} +0 -0
  93. /package/docs/en/guides/basic-features/{proxy.mdx → debug/proxy.mdx} +0 -0
  94. /package/docs/en/guides/basic-features/{json-files.mdx → static-assets/json-files.mdx} +0 -0
  95. /package/docs/en/guides/basic-features/{svg-assets.mdx → static-assets/svg-assets.mdx} +0 -0
  96. /package/docs/en/guides/basic-features/{wasm-assets.mdx → static-assets/wasm-assets.mdx} +0 -0
  97. /package/docs/zh/guides/basic-features/{css-modules.mdx → css/css-modules.mdx} +0 -0
  98. /package/docs/zh/guides/basic-features/{mock.mdx → debug/mock.mdx} +0 -0
  99. /package/docs/zh/guides/basic-features/{proxy.mdx → debug/proxy.mdx} +0 -0
  100. /package/docs/zh/guides/basic-features/{json-files.mdx → static-assets/json-files.mdx} +0 -0
  101. /package/docs/zh/guides/basic-features/{svg-assets.mdx → static-assets/svg-assets.mdx} +0 -0
  102. /package/docs/zh/guides/basic-features/{wasm-assets.mdx → static-assets/wasm-assets.mdx} +0 -0
@@ -0,0 +1,309 @@
1
+ ---
2
+ title: 服务端渲染(SSR)
3
+ ---
4
+
5
+ # 服务端渲染(SSR)
6
+
7
+ 通过在服务器端将网页的 HTML 内容渲染成完整的网页(Server-Side Rendering,简称 SSR),然后将生成的网页发送到浏览器端,浏览器端只需要显示网页即可,不需要再进行额外的渲染。
8
+
9
+ 它主要的优势在于:
10
+
11
+ - 提高首屏加载速度:SSR 可以在服务器端生成完整的网页,浏览器端只需要下载网页内容即可,不需要再进行额外的渲染,从而提高了首屏加载速度。
12
+ - 有利于 SEO:SSR 可以生成完整的 HTML 内容,搜索引擎可以直接索引 HTML 内容,从而提高网站的排名。
13
+
14
+ 如果你有以下场景的需求,开发者可以考虑使用 SSR 来渲染你的页面:
15
+
16
+ 1. 对首屏加载速度要求较高的网站,如电商网站、新闻网站等。
17
+ 2. 对用户体验要求较高的网站,如社交网站、游戏网站等。
18
+ 3. 对 SEO 要求较高的网站,如企业官网、博客等。
19
+
20
+ 在 Modern.js 中,SSR 也是开箱即用的。开发者无需为 SSR 编写复杂的服务端逻辑,也无需关心 SSR 的运维,或是创建单独的服务。
21
+
22
+ 除了开箱即用的 SSR 服务,为了保证开发者的开发体验,Modern.js 还具备:
23
+
24
+ - 完备的 SSR 降级策略,保证页面能够安全运行。
25
+ - 自动分割子路由,按需加载,减少首屏资源体积。
26
+ - 内置缓存系统,解决服务端负载高的问题。
27
+
28
+ ## 开启 SSR
29
+
30
+ 在 Modenr.js 启用 SSR 非常简单,只需要设置 [`server.ssr`](/configure/app/server/ssr) 为 `true` 即可:
31
+
32
+ ```ts title="modern.config.ts"
33
+ import { defineConfig } from '@modern-js/app-tools';
34
+
35
+ export default defineConfig({
36
+ server: {
37
+ ssr: true,
38
+ },
39
+ });
40
+ ```
41
+
42
+ ## 数据获取
43
+
44
+ :::tip 前置阅读
45
+ 如果你还不了解 Data Loader 如何使用或 Client Loader 的概念,请先阅读[数据获取](/guides/basic-features/data/data-fetch)。
46
+ :::
47
+
48
+ ### 基本用法
49
+
50
+ Modern.js 中提供了 Data Loader,方便开发者在 SSR、CSR 下同构地获取数据。每个路由模块,如 `layout.tsx` 和 `page.tsx` 都可以定义自己的 Data Loader:
51
+
52
+ ```ts title="src/routes/page.data.ts"
53
+ export const loader = () => {
54
+ return {
55
+ message: 'Hello World',
56
+ };
57
+ };
58
+ ```
59
+
60
+ 在组件中可以通过 Hooks API 的方式获取 `loader` 函数返回的数据:
61
+
62
+ ```tsx
63
+ import { useLoaderData } from '@modern-js/runtime/router';
64
+ export default () => {
65
+ const data = useLoaderData();
66
+ return <div>{data.message}</div>;
67
+ };
68
+ ```
69
+
70
+
71
+ ### 使用 Client Loader
72
+
73
+ :::info
74
+ 该功能需要 x.36.0 以上版本,推荐使用框架最新版本。
75
+ :::
76
+
77
+ 默认情况下,在 SSR 应用中,`loader` 函数只会在服务端执行。但有些场景下,开发者可能期望在浏览器端发送的请求不经过 SSR 服务,直接请求数据源,例如:
78
+
79
+ 1. 在浏览器端希望减少网络消耗,直接请求数据源。
80
+ 2. 应用在浏览器端有数据缓存,不希望请求 SSR 服务获取数据。
81
+
82
+ Modern.js 支持在 SSR 应用中额外添加 `.data.client` 文件,同样具名导出 `loader`。此时 SSR 应用在服务端执行 Data Loader 报错降级,或浏览器端切换路由时,会像 CSR 应用一样在浏览器端执行该 `loader` 函数,而不是再向 SSR 服务发送数据请求。
83
+
84
+ ```ts title="page.data.client.ts"
85
+ import cache from 'my-cache';
86
+
87
+ export async function loader({ params }) {
88
+ if (cache.has(params.id)) {
89
+ return cache.get(params.id);
90
+ }
91
+ const res = await fetch('URL_ADDRESS?id={params.id}');
92
+ return {
93
+ message: res.message,
94
+ }
95
+ }
96
+ ```
97
+
98
+ :::warning
99
+ 要使用 Client Loader,必须有对应的 Server Loader,且 Server Loader 必须是 `.data` 文件约定,不能是 `.loader` 文件约定。
100
+ :::
101
+
102
+ ## SSR 降级
103
+
104
+ 在 Modern.js 中,如果应用在 SSR 过程中出现异常,Modern.js 会自动降级到 CSR 模式,并在 CSR 重新发起数据请求,保证页面能够正常展示。SSR 降级的原因主要分为两种:
105
+
106
+ 1. Data Loader 执行报错
107
+ 2. React 组件在服务端渲染报错
108
+
109
+ ### Data Loader 执行报错
110
+
111
+ 默认情况下,如果路由对应的 `loader` 函数执行报错,框架会在服务端直接渲染 `<ErrorBoundary>` 组件,并展示错误信息,这也是社区内大多数框架的默认行为。
112
+
113
+ Modern.js 也支持通过 [`server.ssr`](/configure/app/server/ssr) 配置项中的 `loaderFailureMode` 字段,自定义降级策略。当该字段被配置为 `clientRender` 时,会直接降级到 CSR 模式,并重新发起数据请求。
114
+
115
+ 此时,如果路由中定义了 Client Loader,则会优先使用 Client Loader 发起数据请求。如果重新渲染仍然出错,再展示 `<ErrorBoundary>` 组件。
116
+
117
+ {/* Todo 补个图 */}
118
+
119
+ ### 组件渲染报错
120
+
121
+ 当组件渲染报错时,Modern.js 会自动降级到 CSR 模式,并重新发起数据请求。如果重新渲染仍然出错,则展示 `<ErrorBoundary>` 组件。
122
+
123
+ :::tip
124
+ 组件渲染报错的行为,不会受到 `loaderFailureMode` 的影响,也不会在浏览器端执行 Client Loader。
125
+ :::
126
+
127
+ {/* Todo 补个图 */}
128
+
129
+ ## 日志与监控
130
+
131
+ import Monitor from '@site-docs/components/ssr-monitor';
132
+
133
+ <Monitor />
134
+
135
+ ## 页面缓存
136
+
137
+ Modern.js 中内置了缓存的能力,详细请参考[渲染缓存](/guides/basic-features/render/ssr-cache)。
138
+
139
+ ## 运行环境差异
140
+
141
+ SSR 应用会同时运行在服务端和浏览器端,两者在运行环境上不完全相同,存在 Web API 和 Node API 的差异。
142
+
143
+ 开启 SSR 时,Modern.js 会用相同的入口,构建出 SSR Bundle 和 CSR Bundle 两份产物。因此,在 SSR Bundle 中存在 Web API,或是在 CSR Bundle 中存在 Node API 时,都可能导致运行出错。出现这类问题的场景主要是两类:
144
+
145
+ - 应用自身代码存在问题
146
+ - 应用依赖的包中存在副作用
147
+
148
+ ### 自身代码问题
149
+
150
+ 这种场景通常出现在应用从 CSR 迁移到 SSR,CSR 应用通常会在代码中引入 Web API。例如应用希望做全局的事件监听:
151
+
152
+ ```tsx
153
+ document.addEventListener('load', () => {
154
+ console.log('document load');
155
+ });
156
+ const App = () => {
157
+ return <div>Hello World</div>;
158
+ };
159
+ export default App;
160
+ ```
161
+
162
+ 对于这种场景,你可以直接使用 Modern.js 内置的环境变量 `MODERN_TARGET` 进行判断,在构建时删除无用代码:
163
+
164
+ ```ts
165
+ if (process.env.MODERN_TARGET === 'browser') {
166
+ document.addEventListener('load', () => {
167
+ console.log('document load');
168
+ });
169
+ }
170
+ ```
171
+
172
+ 开发环境打包后,SSR 产物和 CSR 产物会被编译成以下内容。因此 SSR 环境中不会再因为 Web API 报错:
173
+
174
+ ```ts
175
+ // SSR 产物
176
+ if (false) {
177
+ }
178
+
179
+ // CSR 产物
180
+ if (true) {
181
+ document.addEventListener('load', () => {
182
+ console.log('document load');
183
+ });
184
+ }
185
+ ```
186
+
187
+ :::note
188
+ 更多内容可以查看[环境变量](/guides/basic-features/env-vars)。
189
+ :::
190
+
191
+ ### 依赖中的副作用
192
+
193
+ 这类场景是在 SSR 应用中随时可能出现的,因为社区中的包并不都支持在两个运行环境中运行,有些包也无需在两个环境中运行。例如在代码中引入了包 A,它内部有使用了 Web API 的副作用:
194
+
195
+ ```ts title="packageA"
196
+ document.addEventListener('load', () => {
197
+ console.log('document load');
198
+ });
199
+
200
+ export const doSomething = () => {}
201
+ ```
202
+
203
+ 如果直接引用到组件中,会造成 CSR 加载报错,即使你已经使用环境变量进行判断,但仍然无法移除依赖中副作用的执行。
204
+
205
+ ```tsx title="routes/page.tsx"
206
+ import { doSomething } from 'packageA';
207
+
208
+ export const Page = () => {
209
+ if (process.env.MODERN_TARGET === 'browser') {
210
+ doSomething();
211
+ }
212
+ return <div>Hello World</div>
213
+ }
214
+ ```
215
+
216
+ Modern.js 也支持通过 `.server.` 后缀的文件来区分 SSR Bundle 和 CSR Bundle 产物的打包文件。可以创建同名的 `.ts` 和 `.server.ts` 文件做一层代理:
217
+
218
+ ```ts title="a.ts"
219
+ export { doSomething } from 'packageA';
220
+ ```
221
+
222
+ ```ts title="a.server.ts"
223
+ export const doSomething: any = () => {};
224
+ ```
225
+
226
+ 在文件中直接引入 `./a`,此时 SSR 打包下会优先使用 `.server.ts` 后缀的文件,CSR 打包下会使用 `.ts` 后缀的文件。
227
+
228
+ ```tsx title="routes/page.tsx"
229
+ import { doSomething } from './a'
230
+
231
+ export const Page = () => {
232
+ doSomething();
233
+ return <div>Hello World</div>
234
+ }
235
+ ```
236
+
237
+ ## 常见问题
238
+
239
+ ### 保持渲染一致
240
+
241
+ SSR 业务需要保证在服务端渲染时的结果和浏览器端 Hydrate 的结果一致,否则很有可能出现不符合预期的渲染结果。这里通过一个例子,演示当 SSR 与 CSR 渲染不一致时出现的问题,在组件中添加以下代码:
242
+
243
+ ```tsx
244
+ {
245
+ typeof window !== 'undefined' ? <div>browser content</div> : null;
246
+ }
247
+ ```
248
+
249
+ 启动应用后,访问页面,会发现浏览器控制台抛出警告信息:
250
+
251
+ ```sh
252
+ Warning: Expected server HTML to contain a matching <div> in <div>.
253
+ ```
254
+
255
+ 这是 React hydrate 结果与 SSR 渲染结果不一致造成的。虽然当前页面表现正常,但在复杂应用中,很有可能因此出现 DOM 层级混乱、样式混乱等问题。
256
+
257
+ :::info
258
+ 关于 React hydrate 逻辑请参考[这里](https://zh-hans.react.dev/reference/react-dom/hydrate)。
259
+
260
+ :::
261
+
262
+ 应用需要保持 SSR 与 CSR 渲染结果的一致性,如果存在不一致的情况,说明这部分内容无需在 SSR 中进行渲染。Modern.js 为这类在 SSR 中不需要渲染的内容提供 [`<NoSSR>` 工具组件](/apis/app/runtime/core/use-runtime-context):
263
+
264
+ ```ts
265
+ import { NoSSR } from '@modern-js/runtime/ssr';
266
+ ```
267
+
268
+ 在不需要进行 SSR 的元素外部,用 `NoSSR` 组件包裹:
269
+
270
+ ```tsx
271
+ <NoSSR>
272
+ <div>browser content</div>
273
+ </NoSSR>
274
+ ```
275
+
276
+ 修改代码后,刷新页发现之前的 Waring 消失。打开浏览器开发者工具的 Network 窗口,查看返回的 HTML 文档是不包含 `NoSSR` 组件包裹的内容的。
277
+
278
+ 在实际场景中,有些应用的 UI 展示会和用户设备有关,例如 [UA](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) 信息。Modern.js 也提供了
279
+ [`useRuntimeContext`](/apis/app/runtime/core/use-runtime-context) 这类 API,可以在组件中获取完整的请求信息,利用它保证 SSR 与 CSR 的渲染结果一致。
280
+
281
+ ### 关注内存泄漏
282
+
283
+ :::warning 警告
284
+ 在 SSR 场景下,开发者需要特别关注内存泄露问题,即使是微小的内存泄露,在大量的访问后也会对服务造成影响。
285
+
286
+ :::
287
+
288
+ SSR 时,浏览器的每次请求,都会触发服务端重新执行一次组件渲染逻辑。所以,需要避免在全局定义任何可能不断增长的数据结构,或在全局进行事件订阅,或创建不会被销毁的流。
289
+
290
+ 例如以下代码,使用 [redux-observable](https://redux-observable.js.org/) 时,习惯了 CSR 的开发者通常会在组件中这样编码:
291
+
292
+ ```tsx
293
+ /* 代码仅作为示例,不可运行 */
294
+ import { createEpicMiddleware, combineEpics } from 'redux-observable';
295
+
296
+ const epicMiddleware = createEpicMiddleware();
297
+ const rootEpic = combineEpics();
298
+
299
+ export default function Test() {
300
+ epicMiddleware.run(rootEpic);
301
+ return <div>Hello Modern.js</div>;
302
+ }
303
+ ```
304
+
305
+ 在组件外层创建 Middleware 实例 `epicMiddleware`,并在组件内部调用 `epicMiddleware.run`。
306
+
307
+ 在浏览器端,这段代码不会造成任何问题,但是在 SSR 时,Middleware 实例会一直无法被销毁。每次渲染组件,调用 `epicMiddleware.run(rootEpic)` 时,都会在内部添加新的事件绑定,导致整个对象不断变大,最终对应用性能造成影响。
308
+
309
+ CSR 中这类问题不易被发觉,因此从 CSR 切换到 SSR 时,如果不确定应用是否存在这类隐患,可以对应用进行压测。
@@ -1,11 +1,8 @@
1
1
  ---
2
- sidebar_position: 2
3
- title: 流式渲染
2
+ title: 服务端流式渲染(Streaming SSR)
4
3
  ---
5
4
 
6
- # 流式渲染
7
-
8
- ## 概述
5
+ # 服务端流式渲染(Streaming SSR)
9
6
 
10
7
  流式渲染是一种新的渲染方式,它可以在用户与页面交互时,实时地更新页面内容,从而提高用户体验。
11
8
 
@@ -18,7 +15,7 @@ title: 流式渲染
18
15
  - 拥有更好的性能控制:流式渲染可以让开发者更好地控制页面加载的优先级和顺序,从而更好地优化性能和用户体验。
19
16
  - 更好的适应性:流式渲染可以更好地适应不同网络速度和设备性能,使得页面在各种环境下都能有更好的表现。
20
17
 
21
- ## 使用
18
+ ## 开启流式渲染
22
19
 
23
20
  Modern.js 支持了 React 18 的流式渲染,可以通过如下配置启用:
24
21
 
@@ -40,9 +37,9 @@ Modern.js 的流式渲染基于 React Router 实现,主要涉及 API 有:
40
37
  - [`Await`](https://reactrouter.com/en/main/components/await):用于渲染 Data Loader 返回的异步数据。
41
38
  - [`useAsyncValue`](https://reactrouter.com/en/main/hooks/use-async-value):用于从最近的父级 `Await` 组件中获取数据。
42
39
 
43
- ### 异步获取数据
40
+ ## 获取数据
44
41
 
45
- ```ts title="page.data.ts"
42
+ ```ts title="user/[id]/page.data.ts"
46
43
  import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
47
44
 
48
45
  interface User {
@@ -70,14 +67,11 @@ export const loader = ({ params }: LoaderFunctionArgs) => {
70
67
  };
71
68
  ```
72
69
 
73
- `user` 是一个 Promise 类型的对象,表示需要异步获取的数据,通过 `defer` 处理需要异步获取的 `user`。注意,`defer` 必须接收一个对象类型的参数,
74
- 因此, 传入 `defer` 的参数为:`{ data: user }`
75
-
76
- `defer` 还可以同时接收异步数据和同步数据。例如:
70
+ `user` 是一个 Promise 类型的对象,表示需要异步获取的数据,通过 `defer` 处理需要异步获取的 `user`。注意,`defer` 必须接收一个对象类型的参数,不能直接传递 Promise 对象。
77
71
 
78
- ```ts title="page.data.ts"
79
- // 省略部分代码
72
+ 另外,`defer` 还可以同时接收异步数据和同步数据。在下述例子中,我们等待部分耗时较短的请求,在响应后通过对象数据返回,而耗时较长时间的请求,则通过 Promise 返回:
80
73
 
74
+ ```ts title="user/[id]/page.data.ts"
81
75
  export const loader = ({ params }: LoaderFunctionArgs) => {
82
76
  const userId = params.id;
83
77
 
@@ -87,7 +81,7 @@ export const loader = ({ params }: LoaderFunctionArgs) => {
87
81
  name: `user-${userId}`,
88
82
  age: 18,
89
83
  });
90
- }, 200);
84
+ }, 2000);
91
85
  });
92
86
 
93
87
  const otherData = new Promise<string>(resolve => {
@@ -103,13 +97,13 @@ export const loader = ({ params }: LoaderFunctionArgs) => {
103
97
  };
104
98
  ```
105
99
 
106
- `otherData` 前加了 `await`,所以是同步获取的数据,它可以和异步获取的数据 `user` 同时传入 `defer`。
100
+ 这样,应用无需等待最耗时的数据请求响应后才展示页面内容,可以优先展示部分有数据的页面内容
107
101
 
108
- ### 渲染异步数据
102
+ ## 渲染数据
109
103
 
110
104
  通过 `Await` 组件,可以获取到 Data Loader 中异步返回的数据,然后进行渲染。例如:
111
105
 
112
- ```tsx title="page.tsx"
106
+ ```tsx title="user/[id]/page.tsx"
113
107
  import { Await, useLoaderData } from '@modern-js/runtime/router';
114
108
  import { Suspense } from 'react';
115
109
  import type { Data } from './page.data';
@@ -143,22 +137,16 @@ export default Page;
143
137
  `Suspense` 组件 `fallback` 属性设置的内容。
144
138
 
145
139
  :::warning 注意
146
- Data Loader 文件导入类型时,需要使用 import type 语法,保证只导入类型信息,这样可以避免 Data Loader 的代码打包到前端产物的 bundle 文件中。
147
-
148
- 所以,这里的导入方式为:`import type { Data } from './page.data'`;
149
-
140
+ `page.data.ts` 文件导入类型时,需要使用 `import type` 语法,保证只导入类型信息,避免 Data Loader 的代码打包到前端产物中。
150
141
  :::
151
142
 
152
- 也可以通过 `useAsyncValue` 获取 Data Loader 返回的异步数据。例如:
143
+ 在组件中,你也可以通过 `useAsyncValue` 获取 Data Loader 返回的异步数据。例如:
153
144
 
154
145
  ```tsx title='page.tsx'
155
146
  import { useAsyncValue } from '@modern-js/runtime/router';
156
147
 
157
- // 省略部分代码
158
-
159
148
  const UserInfo = () => {
160
149
  const user = useAsyncValue();
161
-
162
150
  return (
163
151
  <div>
164
152
  name: {user.name}, age: {user.age}
@@ -168,7 +156,6 @@ const UserInfo = () => {
168
156
 
169
157
  const Page = () => {
170
158
  const data = useLoaderData() as Data;
171
-
172
159
  return (
173
160
  <div>
174
161
  User info:
@@ -184,10 +171,9 @@ const Page = () => {
184
171
  export default Page;
185
172
  ```
186
173
 
187
- ### 错误处理
174
+ ## 错误处理
188
175
 
189
- `Await` 组件的 `errorElement` 属性,可以用来处理当 Data Loader 执行时,或者子组件渲染时抛出的错误。
190
- 例如,我们故意在 Data Loader 函数中抛出错误:
176
+ `Await` 组件的 `errorElement` 属性,可以用来处理 Data Loader 或者子组件渲染时的报错。例如,我们故意在 Data Loader 函数中抛出错误:
191
177
 
192
178
  ```ts title="page.loader.ts"
193
179
  import { defer } from '@modern-js/runtime/router';
@@ -238,15 +224,14 @@ function ErrorElement() {
238
224
 
239
225
  然而,当一个爬虫访问该页面时,它可能需要先加载所有内容,直接输出整个 HTML,而不是渐进式地加载它。
240
226
 
241
- Modern.js 使用 [isbot](https://www.npmjs.com/package/isbot) 对请求的 `uesr-agent`, 以判断请求是否来自爬虫。
227
+ Modern.js 使用 [isbot](https://www.npmjs.com/package/isbot) 对请求的 `uesr-agent`,以判断请求是否来自爬虫。
242
228
 
243
- :::info 补充信息
244
-
245
- 1. [Deferred Data](https://reactrouter.com/en/main/guides/deferred)
246
- 2. [New Suspense SSR Architecture in React 18](https://github.com/reactwg/react-18/discussions/37)
247
-
248
- :::
249
229
 
250
230
  import StreamSSRPerformance from '@site-docs/components/stream-ssr-performance';
251
231
 
252
232
  <StreamSSRPerformance />
233
+
234
+ ## 相关文档
235
+
236
+ 1. [Deferred Data](https://reactrouter.com/en/main/guides/deferred)
237
+ 2. [New Suspense SSR Architecture in React 18](https://github.com/reactwg/react-18/discussions/37)