@modern-js/main-doc 2.58.2 → 2.59.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. package/docs/en/apis/app/runtime/core/use-loader.mdx +1 -1
  2. package/docs/en/community/blog/_meta.json +1 -6
  3. package/docs/en/components/deploy.mdx +1 -1
  4. package/docs/en/components/init-app.mdx +0 -1
  5. package/docs/en/components/init-rspack-app.mdx +0 -1
  6. package/docs/en/components/ssr-monitor.mdx +3 -0
  7. package/docs/en/configure/_meta.json +1 -1
  8. package/docs/en/configure/app/output/ssg.mdx +52 -141
  9. package/docs/en/configure/app/tools/swc.mdx +1 -1
  10. package/docs/en/configure/app/tools/tailwindcss.mdx +1 -1
  11. package/docs/en/guides/advanced-features/_meta.json +0 -8
  12. package/docs/en/guides/advanced-features/bff/_meta.json +1 -6
  13. package/docs/en/guides/advanced-features/rsbuild-plugin.mdx +2 -2
  14. package/docs/en/guides/advanced-features/rspack-start.mdx +7 -22
  15. package/docs/en/guides/basic-features/_meta.json +31 -9
  16. package/docs/en/guides/basic-features/css/_meta.json +1 -0
  17. package/docs/en/guides/basic-features/css/css-in-js.mdx +34 -0
  18. package/docs/en/guides/basic-features/{css-modules.mdx → css/css-modules.mdx} +0 -4
  19. package/docs/en/guides/basic-features/css/css.mdx +25 -0
  20. package/docs/en/guides/basic-features/{css.mdx → css/tailwindcss.mdx} +5 -66
  21. package/docs/en/guides/basic-features/data/_meta.json +1 -4
  22. package/docs/en/guides/basic-features/data/data-fetch.mdx +134 -235
  23. package/docs/en/guides/basic-features/data/data-write.mdx +66 -77
  24. package/docs/en/guides/basic-features/debug/_meta.json +1 -0
  25. package/docs/en/guides/basic-features/debug/rsdoctor.mdx +57 -0
  26. package/docs/en/guides/{advanced-features → basic-features/debug}/using-storybook.mdx +2 -0
  27. package/docs/en/guides/basic-features/render/_meta.json +1 -0
  28. package/docs/en/guides/basic-features/render/ssg.mdx +208 -0
  29. package/docs/en/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +38 -50
  30. package/docs/en/guides/basic-features/render/ssr.mdx +301 -0
  31. package/docs/en/guides/basic-features/render/streaming-ssr.mdx +230 -0
  32. package/docs/en/guides/basic-features/routes.mdx +275 -263
  33. package/docs/en/guides/basic-features/static-assets/_meta.json +1 -0
  34. package/docs/en/guides/basic-features/static-assets.mdx +1 -1
  35. package/docs/en/guides/basic-features/testing/_meta.json +1 -0
  36. package/docs/en/guides/basic-features/testing/cypress.mdx +95 -0
  37. package/docs/en/guides/basic-features/testing/jest.mdx +148 -0
  38. package/docs/en/guides/basic-features/testing/playwright.mdx +111 -0
  39. package/docs/en/guides/basic-features/testing/vitest.mdx +100 -0
  40. package/docs/en/guides/concept/_meta.json +1 -4
  41. package/docs/en/guides/concept/entries.mdx +78 -47
  42. package/docs/en/guides/get-started/_meta.json +1 -7
  43. package/docs/en/guides/get-started/introduction.mdx +1 -1
  44. package/docs/en/guides/get-started/quick-start.mdx +1 -2
  45. package/docs/en/guides/get-started/tech-stack.mdx +4 -6
  46. package/docs/en/guides/get-started/upgrade.mdx +16 -2
  47. package/docs/en/guides/topic-detail/framework-plugin/_meta.json +1 -1
  48. package/docs/en/guides/topic-detail/generator/_meta.json +1 -1
  49. package/docs/en/guides/topic-detail/generator/create/_meta.json +1 -5
  50. package/docs/en/guides/topic-detail/generator/create/config.mdx +0 -10
  51. package/docs/en/guides/topic-detail/generator/create/use.mdx +0 -1
  52. package/docs/en/guides/topic-detail/generator/new/_meta.json +1 -5
  53. package/docs/en/guides/topic-detail/generator/plugin/_meta.json +1 -1
  54. package/docs/en/guides/troubleshooting/_meta.json +1 -6
  55. package/docs/en/tutorials/first-app/c03-css.mdx +1 -1
  56. package/docs/zh/apis/app/runtime/core/use-loader.mdx +1 -1
  57. package/docs/zh/community/blog/_meta.json +1 -6
  58. package/docs/zh/components/deploy.mdx +1 -1
  59. package/docs/zh/components/init-app.mdx +0 -1
  60. package/docs/zh/components/init-rspack-app.mdx +0 -1
  61. package/docs/zh/components/ssr-monitor.mdx +3 -0
  62. package/docs/zh/configure/_meta.json +1 -1
  63. package/docs/zh/configure/app/output/ssg.mdx +49 -139
  64. package/docs/zh/configure/app/tools/swc.mdx +1 -1
  65. package/docs/zh/configure/app/tools/tailwindcss.mdx +1 -1
  66. package/docs/zh/guides/advanced-features/_meta.json +0 -8
  67. package/docs/zh/guides/advanced-features/bff/_meta.json +1 -6
  68. package/docs/zh/guides/advanced-features/rsbuild-plugin.mdx +2 -2
  69. package/docs/zh/guides/advanced-features/rspack-start.mdx +8 -24
  70. package/docs/zh/guides/basic-features/_meta.json +31 -9
  71. package/docs/zh/guides/basic-features/css/_meta.json +1 -0
  72. package/docs/zh/guides/basic-features/css/css-in-js.mdx +34 -0
  73. package/docs/zh/guides/basic-features/css/css.mdx +25 -0
  74. package/docs/zh/guides/basic-features/{css.mdx → css/tailwindcss.mdx} +3 -64
  75. package/docs/zh/guides/basic-features/data/_meta.json +1 -4
  76. package/docs/zh/guides/basic-features/data/data-fetch.mdx +98 -214
  77. package/docs/zh/guides/basic-features/data/data-write.mdx +54 -55
  78. package/docs/zh/guides/basic-features/debug/_meta.json +1 -0
  79. package/docs/zh/guides/basic-features/debug/rsdoctor.mdx +57 -0
  80. package/docs/zh/guides/{advanced-features → basic-features/debug}/using-storybook.mdx +1 -1
  81. package/docs/zh/guides/basic-features/render/_meta.json +1 -0
  82. package/docs/zh/guides/basic-features/render/ssg.mdx +210 -0
  83. package/docs/zh/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +16 -26
  84. package/docs/zh/guides/basic-features/render/ssr.mdx +309 -0
  85. package/docs/zh/guides/{advanced-features/ssr/stream.mdx → basic-features/render/streaming-ssr.mdx} +22 -37
  86. package/docs/zh/guides/basic-features/routes.mdx +252 -237
  87. package/docs/zh/guides/basic-features/static-assets/_meta.json +1 -0
  88. package/docs/zh/guides/basic-features/static-assets.mdx +2 -6
  89. package/docs/zh/guides/basic-features/testing/_meta.json +1 -0
  90. package/docs/zh/guides/basic-features/testing/cypress.mdx +95 -0
  91. package/docs/zh/guides/basic-features/testing/jest.mdx +148 -0
  92. package/docs/zh/guides/basic-features/testing/playwright.mdx +112 -0
  93. package/docs/zh/guides/basic-features/testing/vitest.mdx +100 -0
  94. package/docs/zh/guides/concept/_meta.json +1 -4
  95. package/docs/zh/guides/concept/entries.mdx +80 -58
  96. package/docs/zh/guides/get-started/_meta.json +1 -7
  97. package/docs/zh/guides/get-started/introduction.mdx +2 -2
  98. package/docs/zh/guides/get-started/quick-start.mdx +1 -2
  99. package/docs/zh/guides/get-started/tech-stack.mdx +8 -10
  100. package/docs/zh/guides/get-started/upgrade.mdx +15 -1
  101. package/docs/zh/guides/topic-detail/framework-plugin/_meta.json +1 -1
  102. package/docs/zh/guides/topic-detail/generator/_meta.json +1 -1
  103. package/docs/zh/guides/topic-detail/generator/create/_meta.json +1 -5
  104. package/docs/zh/guides/topic-detail/generator/create/config.mdx +0 -10
  105. package/docs/zh/guides/topic-detail/generator/create/use.mdx +0 -1
  106. package/docs/zh/guides/topic-detail/generator/new/_meta.json +1 -5
  107. package/docs/zh/guides/topic-detail/generator/plugin/_meta.json +1 -1
  108. package/docs/zh/guides/troubleshooting/_meta.json +1 -6
  109. package/docs/zh/tutorials/first-app/c03-css.mdx +1 -1
  110. package/i18n.json +16 -4
  111. package/package.json +6 -6
  112. package/rspress.config.ts +1 -1
  113. package/src/components/ContentCard/index.tsx +1 -1
  114. package/src/components/Sandpack/index.tsx +1 -1
  115. package/src/components/ShowcaseList/index.tsx +1 -1
  116. package/src/i18n/index.ts +1 -1
  117. package/src/pages/index.tsx +2 -2
  118. package/docs/en/apis/app/hooks/config/storybook.mdx +0 -37
  119. package/docs/en/guides/advanced-features/ssg.mdx +0 -116
  120. package/docs/en/guides/advanced-features/ssr/_meta.json +0 -5
  121. package/docs/en/guides/advanced-features/ssr/index.mdx +0 -23
  122. package/docs/en/guides/advanced-features/ssr/stream.mdx +0 -248
  123. package/docs/en/guides/advanced-features/ssr/usage.mdx +0 -341
  124. package/docs/en/guides/advanced-features/ssr.mdx +0 -555
  125. package/docs/zh/apis/app/hooks/config/storybook.mdx +0 -38
  126. package/docs/zh/guides/advanced-features/ssg.mdx +0 -116
  127. package/docs/zh/guides/advanced-features/ssr/_meta.json +0 -5
  128. package/docs/zh/guides/advanced-features/ssr/index.mdx +0 -23
  129. package/docs/zh/guides/advanced-features/ssr/usage.mdx +0 -329
  130. /package/docs/en/guides/basic-features/{mock.mdx → debug/mock.mdx} +0 -0
  131. /package/docs/en/guides/basic-features/{proxy.mdx → debug/proxy.mdx} +0 -0
  132. /package/docs/en/guides/basic-features/{json-files.mdx → static-assets/json-files.mdx} +0 -0
  133. /package/docs/en/guides/basic-features/{svg-assets.mdx → static-assets/svg-assets.mdx} +0 -0
  134. /package/docs/en/guides/basic-features/{wasm-assets.mdx → static-assets/wasm-assets.mdx} +0 -0
  135. /package/docs/zh/guides/basic-features/{css-modules.mdx → css/css-modules.mdx} +0 -0
  136. /package/docs/zh/guides/basic-features/{mock.mdx → debug/mock.mdx} +0 -0
  137. /package/docs/zh/guides/basic-features/{proxy.mdx → debug/proxy.mdx} +0 -0
  138. /package/docs/zh/guides/basic-features/{json-files.mdx → static-assets/json-files.mdx} +0 -0
  139. /package/docs/zh/guides/basic-features/{svg-assets.mdx → static-assets/svg-assets.mdx} +0 -0
  140. /package/docs/zh/guides/basic-features/{wasm-assets.mdx → static-assets/wasm-assets.mdx} +0 -0
@@ -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
- 需要注意的是,这些 API 并不帮助应用发起请求,而是帮助开发者更好地管理数据,提升项目的性能。
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
- :::caution
66
- 这里路由组件和 `data` 文件共享类型,要使用 `import type` 语法。
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
- SSR 环境下,不管是首屏,还是在客户端的导航,`loader` 函数只会在服务端执行,这里可以调用任意的 Node.js API,同时这里使用的任何依赖和代码都不会包含在客户端的 bundle 中。
55
+ 在路由组件中,你可以通过 `useLoaderData` 函数获取数据:
73
56
 
74
- :::info
75
- 在以后的版本中,Modern.js 可能会支持在 CSR 环境下,`loader` 函数也在服务端运行,以提高性能和安全性,所以这里建议尽可能地保证 `loader` 的纯粹,只做数据获取的场景。
57
+ ```ts title="routes/user/page.tsx"
58
+ import { useLoaderData } from '@modern-js/runtime/router';
59
+ import type { ProfileData } from './page.data.ts';
76
60
 
61
+ export default function UserPage() {
62
+ const profileData = useLoaderData() as ProfileData;
63
+ return <div>{profileData}</div>;
64
+ }
65
+ ```
66
+
67
+ :::caution
68
+ 路由组件和 `.data` 文件共享类型,要使用 `import type` 语法,避免引入预期之外的副作用。
77
69
  :::
78
70
 
79
- 当在客户端导航时,基于 Modern.js 的[约定式路由](/guides/basic-features/routes),所有的 `loader` 函数会并行执行(请求),即当访问 `/user/profile` 时,`/user` 和 `/user/profile` 下的 loader 函数都会并行执行(请求),以提高客户端的性能。
71
+ CSR 项目中,`loader` 函数会在客户端执行,`loader` 函数内可以使用浏览器的 API(但通常不需要,也不推荐)。
72
+
73
+ 在 SSR 项目中,不管是首屏,还是在客户端的导航,`loader` 函数只会在服务端执行,这里可以调用任意的 Node.js API,同时这里使用的任何依赖和代码都不会包含在客户端的 bundle 中。
74
+
75
+ :::tip
76
+ 在以后的版本中,Modern.js 可能会支持在 CSR 环境下,`loader` 函数也在服务端运行,以提高性能和安全性,所以这里建议尽可能地保证 `loader` 的纯粹,只做数据获取的场景。
77
+ :::
80
78
 
81
- ### `loader` 函数
79
+ 当在浏览器端导航时,基于[约定式路由](/guides/basic-features/routes),Modern.js 能够支持所有的 `loader` 函数并行执行(请求)。即当访问 `/user/profile` 时,`/user` 和 `/user/profile` 下的 `loader` 函数都会并行执行(请求),这种方式解决了部分请求、渲染瀑布流的问题,较大的提升了页面性能。
82
80
 
83
- `loader` 函数有两个入参:
81
+ ## `loader` 函数
84
82
 
85
- #### `params`
83
+ `loader` 函数有两个入参,分别用于获取路由参数和请求信息。
86
84
 
87
- 当路由文件通过 `[]` 时,会作为[动态路由](/guides/basic-features/routes#动态路由),动态路由片段会作为参数传入 `loader` 函数:
85
+ ### params
88
86
 
89
- ```tsx
90
- // routes/user/[id]/page.data.ts
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
- 当访问 `/user/123` 时,`loader` 函数的参数为 `{ params: { id: '123' } }`。
101
-
102
- #### `request`
100
+ ### request
103
101
 
104
- `request` 是一个 [Fetch Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) 实例。
105
-
106
- 一个常见的使用场景是通过 `request` 获取查询参数:
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` 函数的返回值可以是任何可序列化的内容,也可以是一个 [Fetch Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) 实例:
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
- ### 请求 API
141
+ ## 在不同环境使用 Data Loader
142
+
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` 函数只在服务端执行可以带来以下好处:
147
151
 
148
- Modern.js `fetch` API 做了 polyfill, 用于发起请求,该 API 与浏览器的 `fetch` API 一致,但是在服务端也能使用该 API 发起请求,这意味着不管是 CSR 还是 SSR,都可以使用统一的 `fetch` API 进行数据获取:
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('https://api/user/profile');
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 状态码
188
201
 
189
- 在 SSR 项目中你可以通过在 data loader `throw response` 的方式,控制页面的状态码,展示对应的 UI,如以下示例,整条路由路线中有一个 loader
190
- throw response,页面的状态码将与这个 `response` 保持一致,页面的 UI 也会切换为 `ErrorBoundary`:
202
+ 在 SSR 项目中你可以通过在 `loader` 函数中 `throw response` 的方式,控制页面的状态码,展示对应的 UI
203
+
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
- 很多场景下,子组件需要获取到祖先组件 `loader` 中的数据,你可以通过 `useRouteLoaderData` 方便地获取到祖先组件的数据:
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`,在使用约定式路由时,Modern.js 会为你自动生成`routeId`,`routeId` 的值是对应组件相对于 `src/routes` 的路径,如上面的例子中,子组件想要获取 `routes/user/layout.tsx` 中 loader 返回的数据,`routeId` 的值就是 `user/layout`。
246
+ `userRouteLoaderData` 接受一个参数 `routeId`。在使用约定式路由时,Modern.js 会为你自动生成 `routeId`,`routeId` 的值是对应组件相对于 `src/routes` 的路径,如上面的例子中,子组件想要获取 `routes/user/layout.tsx` 中 loader 返回的数据,`routeId` 的值就是 `user/layout`。
233
247
 
234
- 在多入口(MPA) 场景下,`routeId` 的值需要加上对应入口的名称,入口名称非指定情况下一般是入口的目录名,如以下目录结构:
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
- ### Loading UI (Experimental)
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
- :::info
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
- ### 数据缓存
309
-
310
- 在路由导航时,Modern.js 只会加载路由变化的部分的数据,如当前路由是 `a/b`,`a` 路径对应的 Data Loader 已经执行过,当从 `/a/b` 跳转到 `/a/c`时,`a` 路径对应的 Data Loader 不会重新执行,`c` 路径对应的 Data Loader 会执行,并获取了数据。
318
+ ## 数据缓存
311
319
 
312
- Modern.js 在数据加载时,只会加载路由变化部分的数据,这种默认的优化策略避免了无效重复数据的请求。
320
+ 在路由导航时,Modern.js 只会加载路由变化的部分的数据。如当前路由是 `a/b`,`a` 路径对应的 Data Loader 已经执行过,当从 `/a/b` 跳转到 `/a/c`时,`a` 路径对应的 Data Loader 不会重新执行,`c` 路径对应的 Data Loader 会执行,并获取了数据。
313
321
 
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
- :::info
331
+ :::tip
324
332
  如果你在路由上定义了 [`shouldRevalidate`](#/shouldrevalidate) 函数,会先检查该函数,判断是否需要重新加载数据。
325
333
  :::
326
334
 
327
335
  ### `shouldRevalidate`
328
336
 
329
337
  :::warning
330
- 目前 `shouldRevalidate` 会在 csrstreaming ssr 下生效。
338
+ 目前 `shouldRevalidate` 只会在 CSRStreaming SSR 下生效。
331
339
  :::
332
340
 
333
- 在每个路由组件(`layout.tsx`,`page.tsx`, `$.tsx`)中,我们可以导出一个 `shouldRevalidate` 函数,在每次项目中的路由变化时,这个函数会触发,该函数可以控制要重新加载哪些路由中的数据,当这个函数返回 true 对应路由的数据就会重新加载。
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
- :::info
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
- 1. 如果原有项目中 loader 是以 `.loader.ts` 文件为约定的,需要修改 `.loader.ts` 为 `.data.ts`(如果 loader 是在 `.data.ts` 文件中定义,忽略这个步骤)。
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
- ```
405
-
365
+ ## 错误用法
406
366
 
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,76 +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 函数,作为接口层,避免多一层转发和执行。
485
-
428
+ ## 常见问题
486
429
 
487
- 2. 为什么 SSR 项目中 Data Loader 只会在服务端执行?
430
+ 1. `loader` BFF 函数的关系是什么?
488
431
 
489
- 我们设计 SSR 项目中 Data Loader 只会在服务端,在客户端渲染时,由框架发送请求到服务端主要有以下原因:
432
+ CSR 项目中,`loader` 在浏览器端执行,在 `loader` 可以直接调用 BFF 函数进行接口请求。
490
433
 
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` 获取数据。
503
-
504
- :::
505
-
506
- 以下是一个最简单的例子:
507
-
508
- ```tsx
509
- import { useLoader } from '@modern-js/runtime';
510
-
511
- export default () => {
512
- const { data } = useLoader(async () => {
513
- console.log('fetch in useLoader');
514
-
515
- // 这里没有发送真实的请求,只是返回了一个写死的数据。
516
- // 真实项目中,应该返回从远端获取的数据。
517
- return {
518
- name: 'Modern.js',
519
- };
520
- });
521
-
522
- return <div>Hello, {data?.name}</div>;
523
- };
524
- ```
525
-
526
- 上述代码启动后,访问页面。可以看到在终端输出了日志,而在浏览器终端却没有打印日志。
527
-
528
- 这是因为 Modern.js 在服务端渲染时,在会收集 `useLoader` 返回的数据,并将数据注入到响应的 HTML 中。如果 SSR 渲染成功,在 HTML 中可以看到如下代码片段:
529
-
530
- ```html
531
- <script>
532
- window._SSR_DATA = {};
533
- </script>
534
- ```
535
-
536
- 在这全局变量中,记录了每一份数据,而在浏览器端渲染的过程中,会优先使用这份数据。如果数据不存在,则会重新执行 `useLoader` 函数。
537
-
538
- :::note
539
- 在构建阶段,Modern.js 会自动为每个 `useLoader` 生成一个 Loader ID,并注入到 SSR 和 CSR 的 JS Bundle 中,用来关联 Loader 和数据。
540
-
541
- :::
542
-
543
- 相比于 Next.js 中的 `getServerSideProps`,在渲染前预先获取数据。使用 `useLoader`,可以在组件中获取局部 UI 所需要的数据,而不用将数据层层传递。同样,也不会因为不同路由需要不同数据请求,而在最外层的数据获取函数中添加冗余的逻辑。当然 `useLoader` 也存在一些问题,例如服务端代码 Treeshaking 困难,服务端需要多一次预渲染等。
544
-
545
- Modern.js 在新版本中,设计了全新的 Loader 方案。新方案解决了这些问题,并能够配合**嵌套路由**,对页面性能做优化。
546
-
547
- :::note
548
- 详细 API 可以查看 [useLoader](/apis/app/runtime/core/use-loader)。
549
-
550
- :::
434
+ SSR 项目中,每个 `loader` 也是一个服务端接口。我们推荐使用 `loader` 替代 http method `get` 的 BFF 函数,避免多一层转发和执行。