@modern-js/main-doc 2.35.1 → 2.36.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. package/docs/en/apis/app/hooks/src/routes.mdx +1 -1
  2. package/docs/en/components/debug-app.mdx +1 -1
  3. package/docs/en/components/turtorials-example-list.mdx +2 -0
  4. package/docs/en/configure/app/source/entries.mdx +7 -6
  5. package/docs/en/guides/advanced-features/bff/sdk.mdx +119 -0
  6. package/docs/en/guides/advanced-features/rspack-start.mdx +1 -1
  7. package/docs/en/guides/advanced-features/ssr.mdx +17 -17
  8. package/docs/en/guides/basic-features/data/_category_.json +4 -0
  9. package/docs/en/guides/basic-features/{data-fetch.mdx → data/data-fetch.mdx} +35 -27
  10. package/docs/en/guides/basic-features/data/data-write.mdx +241 -0
  11. package/docs/en/guides/basic-features/routes.mdx +7 -7
  12. package/docs/en/guides/concept/entries.mdx +31 -30
  13. package/docs/en/guides/get-started/quick-start.mdx +1 -1
  14. package/docs/en/guides/topic-detail/framework-plugin/extend.mdx +0 -1
  15. package/docs/en/guides/topic-detail/framework-plugin/hook-list.mdx +0 -1
  16. package/docs/en/guides/topic-detail/framework-plugin/hook.mdx +1 -1
  17. package/docs/en/guides/topic-detail/framework-plugin/implement.mdx +1 -2
  18. package/docs/en/guides/topic-detail/framework-plugin/introduction.mdx +1 -1
  19. package/docs/en/guides/topic-detail/framework-plugin/lifecycle.mdx +1 -1
  20. package/docs/en/guides/topic-detail/framework-plugin/plugin-api.mdx +31 -11
  21. package/docs/en/guides/topic-detail/framework-plugin/relationship.mdx +2 -2
  22. package/docs/en/guides/topic-detail/generator/new/config.md +0 -5
  23. package/docs/en/guides/topic-detail/micro-frontend/c03-main-app.mdx +2 -2
  24. package/docs/en/tutorials/first-app/c05-loader.mdx +2 -2
  25. package/docs/en/tutorials/first-app/c06-model.mdx +4 -4
  26. package/docs/en/tutorials/first-app/c07-container.mdx +3 -3
  27. package/docs/en/tutorials/foundations/introduction.mdx +3 -2
  28. package/docs/zh/apis/app/hooks/src/routes.mdx +1 -1
  29. package/docs/zh/components/debug-app.mdx +1 -1
  30. package/docs/zh/components/micro-runtime-config.mdx +2 -2
  31. package/docs/zh/components/turtorials-example-list.mdx +2 -0
  32. package/docs/zh/configure/app/source/entries.mdx +7 -6
  33. package/docs/zh/guides/advanced-features/bff/sdk.mdx +119 -0
  34. package/docs/zh/guides/advanced-features/rspack-start.mdx +1 -1
  35. package/docs/zh/guides/advanced-features/ssr.mdx +16 -16
  36. package/docs/zh/guides/basic-features/data/_category_.json +4 -0
  37. package/docs/zh/guides/basic-features/{data-fetch.mdx → data/data-fetch.md} +31 -27
  38. package/docs/zh/guides/basic-features/data/data-write.mdx +236 -0
  39. package/docs/zh/guides/basic-features/routes.mdx +7 -7
  40. package/docs/zh/guides/concept/entries.mdx +34 -32
  41. package/docs/zh/guides/get-started/quick-start.mdx +1 -1
  42. package/docs/zh/guides/topic-detail/framework-plugin/extend.mdx +0 -1
  43. package/docs/zh/guides/topic-detail/framework-plugin/hook-list.mdx +0 -1
  44. package/docs/zh/guides/topic-detail/framework-plugin/hook.mdx +1 -1
  45. package/docs/zh/guides/topic-detail/framework-plugin/implement.mdx +0 -1
  46. package/docs/zh/guides/topic-detail/framework-plugin/introduction.mdx +1 -1
  47. package/docs/zh/guides/topic-detail/framework-plugin/lifecycle.mdx +1 -1
  48. package/docs/zh/guides/topic-detail/framework-plugin/plugin-api.mdx +31 -11
  49. package/docs/zh/guides/topic-detail/framework-plugin/relationship.mdx +1 -1
  50. package/docs/zh/guides/topic-detail/generator/new/config.md +0 -5
  51. package/docs/zh/guides/topic-detail/micro-frontend/c03-main-app.mdx +2 -2
  52. package/docs/zh/guides/topic-detail/monorepo/create-sub-project.mdx +0 -14
  53. package/docs/zh/guides/topic-detail/monorepo/sub-project-interface.mdx +7 -43
  54. package/docs/zh/tutorials/first-app/c05-loader.mdx +2 -2
  55. package/docs/zh/tutorials/first-app/c06-model.mdx +3 -3
  56. package/docs/zh/tutorials/first-app/c07-container.mdx +3 -3
  57. package/docs/zh/tutorials/foundations/introduction.mdx +3 -2
  58. package/package.json +7 -7
@@ -15,7 +15,7 @@ Because the two pages need to share the same set of state (point of contact tabu
15
15
 
16
16
  Modern.js support obtaining data through Data Loader in `layout.tsx`, we first move the data acquisition part of the code to `src/routes/layout.tsx`:
17
17
 
18
- ```ts title="src/routes/layout.loader.ts"
18
+ ```ts title="src/routes/layout.data.ts"
19
19
  export type LoaderData = {
20
20
  code: number;
21
21
  data: {
@@ -25,7 +25,7 @@ export type LoaderData = {
25
25
  }[];
26
26
  };
27
27
 
28
- export default async (): Promise<LoaderData> => {
28
+ export const loader = async (): Promise<LoaderData> => {
29
29
  const data = new Array(20).fill(0).map(() => {
30
30
  const firstName = name.firstName();
31
31
  return {
@@ -58,7 +58,7 @@ import 'tailwindcss/base.css';
58
58
  import 'tailwindcss/components.css';
59
59
  import 'tailwindcss/utilities.css';
60
60
  import '../styles/utils.css';
61
- import type { LoaderData } from './layout.loader';
61
+ import type { LoaderData } from './layout.data';
62
62
 
63
63
  export default function Layout() {
64
64
  const { data } = useLoaderData() as LoaderData;
@@ -31,7 +31,8 @@ We have prepared a tutorial on creating a "contact list app" that you can follow
31
31
 
32
32
  We offer some commonly used case studies for your reference during the development process. Here you can find some usage patterns of the feature combinations provided by Modern.js. We will continue to improve the case studies here.
33
33
 
34
- - [Route Authorization](/tutorials/examples/csr-auth.html)
35
- - ...
34
+ import ExampleList from '@site-docs-en/components/turtorials-example-list';
35
+
36
+ <ExampleList />
36
37
 
37
38
  Let's start from [creating a project](tutorials/first-app/c01-start) now!
@@ -54,7 +54,7 @@ sidebar_position: 2
54
54
 
55
55
  在组件中,可以通过 [useParams](/apis/app/runtime/router/router#useparams) 获取对应命名的参数。
56
56
 
57
- 在使用 [loader](/guides/basic-features/data-fetch#loader-函数) 函数获取数据时,`params` 会作为 `loader` 函数的入参,通过 `params` 的属性可以获取到对应的参数。
57
+ 在使用 [loader](/guides/basic-features/data/data-fetch#loader-函数) 函数获取数据时,`params` 会作为 `loader` 函数的入参,通过 `params` 的属性可以获取到对应的参数。
58
58
 
59
59
  ## 布局组件
60
60
 
@@ -6,7 +6,7 @@ $ pnpm run dev
6
6
  > modern dev
7
7
 
8
8
  info Starting dev server...
9
- ready Client compiled in 50ms
9
+ ready Client compiled in 50 ms
10
10
 
11
11
  > Local: http://localhost:8080/
12
12
  > Network: http://192.168.0.1:8080/
@@ -5,11 +5,11 @@ defineConfig(App, {
5
5
  masterApp: {
6
6
  apps: [{
7
7
  name: 'Table',
8
- entry: 'http://localhost:8001',
8
+ entry: 'http://localhost:8081',
9
9
  // activeWhen: '/table'
10
10
  }, {
11
11
  name: 'Dashboard',
12
- entry: 'http://localhost:8002'
12
+ entry: 'http://localhost:8082'
13
13
  // activeWhen: '/dashboard'
14
14
  }]
15
15
  },
@@ -0,0 +1,2 @@
1
+ - [路由鉴权](/tutorials/examples/csr-auth.html)
2
+ - ...
@@ -39,8 +39,8 @@ import { defineConfig } from '@modern-js/app-tools';
39
39
  export default defineConfig({
40
40
  source: {
41
41
  entries: {
42
- // 指定一个名称为 entry_customize 的新入口
43
- entry_customize: './src/home/test/index.ts',
42
+ // 指定一个名称为 'my-entry' 的新入口
43
+ 'my-entry': './src/home/test/index.ts',
44
44
  },
45
45
  // 禁用默认入口扫描
46
46
  disableDefaultEntries: true,
@@ -92,9 +92,10 @@ import { defineConfig } from '@modern-js/app-tools';
92
92
  export default defineConfig({
93
93
  source: {
94
94
  entries: {
95
- entry_customize: {
95
+ 'my-entry': {
96
96
  // 入口文件路径
97
- entry: './src/home/test/index.tsx',
97
+ entry: './src/my-page/index.tsx',
98
+ disableMount: true,
98
99
  },
99
100
  },
100
101
  // 禁用默认入口扫描
@@ -113,8 +114,8 @@ export default defineConfig({
113
114
  export default defineConfig({
114
115
  source: {
115
116
  entries: {
116
- entry_customize: {
117
- entry: './src/home/test/index.tsx',
117
+ 'my-entry': {
118
+ entry: './src/my-page/index.tsx',
118
119
  disableMount: true,
119
120
  },
120
121
  },
@@ -0,0 +1,119 @@
1
+ ---
2
+ sidebar_position: 4
3
+ title: 自定义请求 SDK
4
+ ---
5
+ # 自定义请求 SDK
6
+
7
+ Modern.js 的 BFF 在 CSR 和 SSR 是同构的。在浏览器端依赖了[Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch),在服务端依赖了 [node-fetch](https://www.npmjs.com/package/node-fetch)。但在很多业务场景下我们需要对请求或响应做一些额外的处理,例如:
8
+
9
+ - 在请求头中写入鉴权信息
10
+ - 对响应的数据或错误进行统一的处理
11
+ - 特定平台无法使用浏览器的原生 fetch 函数,需要使用其他方式发送请求
12
+
13
+ 针对上述的场景,Modern.js 提供了 `configure` 函数,自定义能力从低到高,可以用它配置 ssr 透传请求头,拦截器,请求 SDK。
14
+
15
+ :::caution 注意
16
+ `configure` 函数的调用需要在所有 BFF 请求发送前调用,以确保覆盖默认的请求配置。
17
+
18
+ :::
19
+
20
+
21
+ ```ts title="App.tsx"
22
+ import { configure } from '@modern-js/runtime/bff';
23
+
24
+ configure({
25
+ request: customRequest
26
+ })
27
+ ```
28
+
29
+ ## 配置 ssr 透传请求头
30
+
31
+ 在同时使用 Modernjs SSR 和 BFF 的场景下,常常需要将 SSR 页面请求上的一些请求头信息,透传给 BFF 服务。
32
+
33
+ 例如项目有页面地址是 `https://website.com`,该页面是 SSR 的,在组件中会调用 API 接口 `https://website.com/api/info`,该接口需要用户的 cookie 信息做鉴权。页面在请求该 API 接口时,需要将 SSR 页面请求的 `cookie` 传给 BFF。
34
+
35
+ 目前以下请求头在 Modernjs 中是自动透传的:
36
+
37
+ - cookie
38
+ - x-tt-logid
39
+ - user-agent
40
+ - x-tt-stress
41
+
42
+ 可以通过 `configure` 配置请求头。例如以下例子,Modern.js 会自动将 SSR 页面请求的 cookie 信息透传给 BFF 服务:
43
+
44
+ ```tsx title="App.tsx"
45
+ import { configure } from '@modern-js/runtime/bff';
46
+
47
+ configure({
48
+ allowedHeaders: ['x-uid']
49
+ })
50
+ ```
51
+
52
+ ## 配置拦截器
53
+
54
+ 在有些业务场景下需要对请求和响应进行一些统一的处理,这种场景下可以配置拦截器满足需求:
55
+
56
+ ```tsx title="App.tsx"
57
+ configure({
58
+ // 这里的 request 是一体化默认的请求工具,interceptor 函数需返回一个新的 request。
59
+ // 新 request 的出参必须是 parse body 之后的结果
60
+ interceptor(request){
61
+ return async(url, params) => {
62
+ const res = await request(url, params);
63
+ return res.json();
64
+ };
65
+ }
66
+ });
67
+ ```
68
+
69
+ ## 配置自定义请求 SDK
70
+
71
+ 如果仅仅通过配置拦截器无法满足需求,需要对请求的 SDK 做进一步的自定义,可以通过 `configure` 函数配置自定义请求 SDK:
72
+
73
+ :::caution 注意
74
+ 在 SSR 和一体化调用的场景下,在 SSR 向 BFF 服务发送请求时,Modern.js 会通过**服务发现**找到 BFF 服务内网 IP,并通过 IP 发送请求,以提高性能。如果使用自定义请求 SDK 会**失去这种优化**。
75
+
76
+ :::
77
+
78
+ ```tsx title="App.tsx"
79
+ import nodeFetch from 'node-fetch';
80
+
81
+ const customFetch = (input: RequestInfo | URL, init: RequestInit) => {
82
+ const curFetch = process.env.MODERN_TARGET !== 'node' ? fetch : nodeFetch as unknown as typeof fetch;
83
+ return curFetch(input, init).then(async res => {
84
+ const data = await res.json();
85
+ data.hello = 'hello custom sdk';
86
+ return data;
87
+ });
88
+ };
89
+
90
+ configure({
91
+ request: customFetch,
92
+ });
93
+ ```
94
+
95
+ 配置自定义请求 SDK 有以下约定:
96
+
97
+ - 通过 `configure` 函数可以配置一个 `request` 函数,这个函数的入参与浏览器中的 Fetch 或 node-fetch 对齐,所有的一体化 BFF 函数会通过该函数发送请求。
98
+ - `request` 函数出参必须是接口实际返回的数据,不能是 Promise,否则会导致一体化 BFF 函数无法正常返回数据。
99
+ - 如果是 SSR 项目,`request` 必须要同时支持浏览器端和服务器端发送请求。
100
+
101
+ 使用 axios 定制自定义请求 SDK 的示例:
102
+
103
+ ```tsx title="App.tsx"
104
+ import { configure } from '@modern-js/runtime/bff';
105
+ import type { Method, AxiosRequestHeaders as Headers } from 'axios';
106
+
107
+ configure({
108
+ async request(...config: Parameters<typeof fetch>) {
109
+ const [url, params] = config;
110
+ const res = await axios({
111
+ url: url as string, // 这里因为 fetch 和 axios 类型有些不兼容,需要使用 as
112
+ method: params?.method as Method,
113
+ data: params?.body,
114
+ headers: params?.headers as Headers,
115
+ });
116
+ return res.data;
117
+ },
118
+ });
119
+ ```
@@ -26,7 +26,7 @@ import InitRspackApp from '@site-docs/components/init-rspack-app';
26
26
  在使用 Rspack 作为打包工具时,由于部分能力尚在开发中,以下 features 暂时无法使用,我们将在未来提供支持:
27
27
 
28
28
  - Storybook 调试
29
- - 客户端渲染(CSR)使用 [useLoader](/guides/basic-features/data-fetch.html)
29
+ - 客户端渲染(CSR)使用 [useLoader](/guides/basic-features/data/data-fetch.html)
30
30
 
31
31
  :::
32
32
 
@@ -22,8 +22,8 @@ export default defineConfig({
22
22
 
23
23
  Modern.js 中提供了 Data Loader,方便开发者在 SSR、CSR 下同构的获取数据。每个路由模块,如 `layout.tsx` 和 `page.tsx` 都可以定义自己的 Data Loader:
24
24
 
25
- ```ts title="src/routes/page.loader.ts"
26
- export default () => {
25
+ ```ts title="src/routes/page.data.ts"
26
+ export const loader = () => {
27
27
  return {
28
28
  message: 'Hello World',
29
29
  };
@@ -48,7 +48,7 @@ Modern.js 打破传统的 SSR 开发模式,提供了用户无感的 SSR 开发
48
48
 
49
49
  1. 当以客户端路由的方式请求页面时,Modern.js 会发送一个 HTTP 请求,服务端接收到请求后执行页面对应的 Data Loader 函数,然后将执行结果作为请求的响应返回浏览器。
50
50
 
51
- 2. 使用 Data Loader 时,数据获取发生在渲染前,Modern.js 也仍然支持在组件渲染时获取数据。更多相关内容可以查看[数据获取](/guides/basic-features/data-fetch)。
51
+ 2. 使用 Data Loader 时,数据获取发生在渲染前,Modern.js 也仍然支持在组件渲染时获取数据。更多相关内容可以查看[数据获取](/guides/basic-features/data/data-fetch)。
52
52
 
53
53
  :::
54
54
 
@@ -147,8 +147,8 @@ SPR 利用预渲染与缓存技术,为 SSR 页面提供静态 Web 的响应性
147
147
 
148
148
  这里模拟一个使用 `useLoaderData` API 的组件,Data Loader 中的请求需要消耗 2s 时间。
149
149
 
150
- ```tsx title="page.loader.ts"
151
- export default async () => {
150
+ ```tsx title="page.data.ts"
151
+ export const loader = async () => {
152
152
  await new Promise((resolve, reject) => {
153
153
  setTimeout(() => {
154
154
  resolve(null);
@@ -297,9 +297,9 @@ export const loader = () => {
297
297
 
298
298
  上述两种方式,都会为开发者带来一些心智负担。在真实的业务中,我们发现大多数的 Node / Web 代码混用都出现在数据请求中。
299
299
 
300
- 因此,Modern.js 基于[嵌套路由](/guides/basic-features/routes)开发设计了[更简单的方案](/guides/basic-features/data-fetch)来分离 CSR 和 SSR 的代码。
300
+ 因此,Modern.js 基于[嵌套路由](/guides/basic-features/routes)开发设计了[更简单的方案](/guides/basic-features/data/data-fetch)来分离 CSR 和 SSR 的代码。
301
301
 
302
- 我们可以通过独立文件来分离**数据请求**与**组件代码**。在 `routes/page.tsx` 中编写组件逻辑,在 `routes/page.loader.ts` 中编写数据请求逻辑。
302
+ 我们可以通过独立文件来分离**数据请求**与**组件代码**。在 `routes/page.tsx` 中编写组件逻辑,在 `routes/page.data.ts` 中编写数据请求逻辑。
303
303
 
304
304
  ```ts title="routes/page.tsx"
305
305
  export default Page = () => {
@@ -307,9 +307,9 @@ export default Page = () => {
307
307
  }
308
308
  ```
309
309
 
310
- ```ts title="routes/page.loader.tsx"
310
+ ```ts title="routes/page.data.tsx"
311
311
  import fse from 'fs-extra';
312
- export default () => {
312
+ export const loader = () => {
313
313
  const file = fse.readFileSync('./myfile');
314
314
  return {
315
315
  ...
@@ -319,7 +319,7 @@ export default () => {
319
319
 
320
320
  ## 接口请求
321
321
 
322
- 在 SSR 中发起接口请求时,开发者有时自己封装了同构的请求工具。部分接口需要传递用户 Cookie,开发者可以通过 [`useRuntimeContext`](/guides/basic-features/data-fetch#route-loader) API 获取到请求头来实现。
322
+ 在 SSR 中发起接口请求时,开发者有时自己封装了同构的请求工具。部分接口需要传递用户 Cookie,开发者可以通过 [`useRuntimeContext`](/guides/basic-features/data/data-fetch#route-loader) API 获取到请求头来实现。
323
323
 
324
324
  需要注意的是,此时获取到的是 HTML 请求的请求头,不一定适用于接口请求,因此**千万不能**透传所有请求头。并且,一些后端接口,或是通用网关,会根据请求头中的信息做校验,全量透传容易出现各种难以排查的问题,推荐**按需透传**。
325
325
 
@@ -349,7 +349,7 @@ Modern.js 的流式渲染基于 React Router 实现,主要涉及 API 有:
349
349
 
350
350
  ### 异步获取数据
351
351
 
352
- ```ts title="page.loader.ts"
352
+ ```ts title="page.data.ts"
353
353
  import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
354
354
 
355
355
  interface User {
@@ -361,7 +361,7 @@ export interface Data {
361
361
  data: User;
362
362
  }
363
363
 
364
- export default ({ params }: LoaderFunctionArgs) => {
364
+ export const loader = ({ params }: LoaderFunctionArgs) => {
365
365
  const userId = params.id;
366
366
 
367
367
  const user = new Promise<User>(resolve => {
@@ -382,10 +382,10 @@ export default ({ params }: LoaderFunctionArgs) => {
382
382
 
383
383
  `defer` 还可以同时接收异步数据和同步数据。例如:
384
384
 
385
- ```ts title="page.loader.ts"
385
+ ```ts title="page.data.ts"
386
386
  // 省略部分代码
387
387
 
388
- export default ({ params }: LoaderFunctionArgs) => {
388
+ export const loader = ({ params }: LoaderFunctionArgs) => {
389
389
  const userId = params.id;
390
390
 
391
391
  const user = new Promise<User>(resolve => {
@@ -419,7 +419,7 @@ export default ({ params }: LoaderFunctionArgs) => {
419
419
  ```tsx title="page.tsx"
420
420
  import { Await, useLoaderData } from '@modern-js/runtime/router';
421
421
  import { Suspense } from 'react';
422
- import type { Data } from './page.loader';
422
+ import type { Data } from './page.data';
423
423
 
424
424
  const Page = () => {
425
425
  const data = useLoaderData() as Data;
@@ -452,7 +452,7 @@ export default Page;
452
452
  :::warning 注意
453
453
  从 Data Loader 文件导入类型时,需要使用 import type 语法,保证只导入类型信息,这样可以避免 Data Loader 的代码打包到前端产物的 bundle 文件中。
454
454
 
455
- 所以,这里的导入方式为:`import type { Data } from './page.loader'`;
455
+ 所以,这里的导入方式为:`import type { Data } from './page.data'`;
456
456
 
457
457
  :::
458
458
 
@@ -0,0 +1,4 @@
1
+ {
2
+ "label": "数据管理",
3
+ "position": 3
4
+ }
@@ -11,16 +11,22 @@ Modern.js 中提供了开箱即用的数据获取能力,开发者可以通过
11
11
 
12
12
  ## Data Loader(推荐)
13
13
 
14
- Modern.js 推荐使用约定式路由做路由的管理,通过 Modern.js 的[约定式(嵌套)路由](/guides/basic-features/routes#约定式路由),每个路由组件(`layout.ts` 或 `page.ts`)可以有一个同名的 `loader` 文件,该 `loader` 文件需要导出一个函数,函数会在组件渲染之前执行,为路由组件提供数据。
14
+ Modern.js 推荐使用约定式路由做路由的管理,通过 Modern.js 的[约定式(嵌套)路由](/guides/basic-features/routes#约定式路由),每个路由组件(`layout.ts` 或 `page.ts`)可以有一个同名的 `data` 文件,该 `data` 文件可以导出一个 `loader` 函数,函数会在组件渲染之前执行,为路由组件提供数据。
15
15
 
16
16
  :::info
17
17
  Modern.js v1 支持通过 [useLoader](#useloader(旧版)) 获取数据,这已经不是我们推荐的用法,除迁移过程外,不推荐两者混用。
18
18
 
19
19
  :::
20
20
 
21
+ :::warning
22
+ - 在之前的版本中,Modern.js Data Loader 是定义在 `loader` 文件中的,在之后的版本中,我们推荐定义在 `data` 文件中,同时我们会保持对 `loader` 文件的兼容。
23
+ - 在 `data` 文件中,对应的 `loader` 需要具名导出。
24
+
25
+ :::
26
+
21
27
  ### 基础示例
22
28
 
23
- 路由组件如 `layout.ts` 或 `page.ts`,可以定义同名的 `loader` 文件,`loader` 文件中导出一个函数,该函数提供组件所需的数据,然后在路由组件中通过 `useLoaderData` 函数获取数据,如下面示例:
29
+ 路由组件如 `layout.ts` 或 `page.ts`,可以定义同名的 `data` 文件,`data` 文件中导出一个 `loader` 函数,该函数提供组件所需的数据,然后在路由组件中通过 `useLoaderData` 函数获取数据,如下面示例:
24
30
 
25
31
  ```bash
26
32
  .
@@ -28,16 +34,16 @@ Modern.js v1 支持通过 [useLoader](#useloader(旧版)) 获取数据,这
28
34
  ├── layout.tsx
29
35
  └── user
30
36
  ├── layout.tsx
31
- ├── layout.loader.ts
37
+ ├── layout.data.ts
32
38
  ├── page.tsx
33
- └── page.loader.ts
39
+ └── page.data.ts
34
40
  ```
35
41
 
36
42
  在文件中定义以下代码:
37
43
 
38
44
  ```ts title="routes/user/page.tsx"
39
45
  import { useLoaderData } from '@modern-js/runtime/router';
40
- import type { ProfileData } from './page.loader.ts';
46
+ import type { ProfileData } from './page.data.ts';
41
47
 
42
48
  export default function UserPage() {
43
49
  const profileData = useLoaderData() as ProfileData;
@@ -45,19 +51,19 @@ export default function UserPage() {
45
51
  }
46
52
  ```
47
53
 
48
- ```ts title="routes/user/page.loader.ts"
54
+ ```ts title="routes/user/page.data.ts"
49
55
  export type ProfileData = {
50
56
  /* some types */
51
57
  };
52
58
 
53
- export default async (): Promise<ProfileData> => {
59
+ export const loader = async (): Promise<ProfileData> => {
54
60
  const res = await fetch('https://api/user/profile');
55
61
  return await res.json();
56
62
  };
57
63
  ```
58
64
 
59
65
  :::caution
60
- 这里路由组件和 `loader` 文件共享类型,要使用 `import type` 语法。
66
+ 这里路由组件和 `data` 文件共享类型,要使用 `import type` 语法。
61
67
 
62
68
  :::
63
69
 
@@ -81,10 +87,10 @@ export default async (): Promise<ProfileData> => {
81
87
  当路由文件通过 `[]` 时,会作为[动态路由](/guides/basic-features/routes#动态路由),动态路由片段会作为参数传入 `loader` 函数:
82
88
 
83
89
  ```tsx
84
- // routes/user/[id]/page.loader.ts
90
+ // routes/user/[id]/page.data.ts
85
91
  import { LoaderFunctionArgs } from '@modern-js/runtime/router';
86
92
 
87
- export default async ({ params }: LoaderFunctionArgs) => {
93
+ export const loader = async ({ params }: LoaderFunctionArgs) => {
88
94
  const { id } = params;
89
95
  const res = await fetch(`https://api/user/${id}`);
90
96
  return res.json();
@@ -100,10 +106,10 @@ export default async ({ params }: LoaderFunctionArgs) => {
100
106
  一个常见的使用场景是通过 `request` 获取查询参数:
101
107
 
102
108
  ```tsx
103
- // routes/user/[id]/page.loader.ts
109
+ // routes/user/[id]/page.data.ts
104
110
  import { LoaderFunctionArgs } from '@modern-js/runtime/router';
105
111
 
106
- export default async ({ request }: LoaderFunctionArgs) => {
112
+ export const loader = async ({ request }: LoaderFunctionArgs) => {
107
113
  const url = new URL(request.url);
108
114
  const userId = url.searchParams.get('id');
109
115
  return queryUser(userId);
@@ -142,7 +148,7 @@ const loader = async (): Promise<ProfileData> => {
142
148
  Modern.js 对 `fetch` API 做了 polyfill, 用于发起请求,该 API 与浏览器的 `fetch` API 一致,但是在服务端也能使用该 API 发起请求,这意味着不管是 CSR 还是 SSR,都可以使用统一的 `fetch` API 进行数据获取:
143
149
 
144
150
  ```tsx
145
- async function loader() {
151
+ export async function loader() {
146
152
  const res = await fetch('https://api/user/profile');
147
153
  }
148
154
  ```
@@ -152,8 +158,8 @@ async function loader() {
152
158
  在 `loader` 函数中,可以通过 `throw error` 或者 `throw response` 的方式处理错误,当 `loader` 函数中有错误被抛出时,Modern.js 会停止执行当前 `loader` 中的代码,并将前端 UI 切换到定义的 [`ErrorBoundary`](/guides/basic-features/routes#错误处理) 组件:
153
159
 
154
160
  ```tsx
155
- // routes/user/profile/page.loader.ts
156
- export default async function loader() {
161
+ // routes/user/profile/page.data.ts
162
+ export async function loader() {
157
163
  const res = await fetch('https://api/user/profile');
158
164
  if (!res.ok) {
159
165
  throw res;
@@ -184,8 +190,8 @@ export default ErrorBoundary;
184
190
  // routes/user/profile/page.tsx
185
191
  import { useRouteLoaderData } from '@modern-js/runtime/router';
186
192
 
187
- export default function UserLayout() {
188
- // 获取 routes/user/layout.loader.ts 中 `loader` 返回的数据
193
+ export function UserLayout() {
194
+ // 获取 routes/user/layout.data.ts 中 `loader` 返回的数据
189
195
  const data = useRouteLoaderData('user/layout');
190
196
  return (
191
197
  <div>
@@ -220,12 +226,12 @@ export default function UserLayout() {
220
226
 
221
227
  :::
222
228
 
223
- 创建 `user/layout.loader.ts`,并添加以下代码:
229
+ 创建 `user/layout.data.ts`,并添加以下代码:
224
230
 
225
- ```ts title="routes/user/layout.loader.ts"
231
+ ```ts title="routes/user/layout.data.ts"
226
232
  import { defer } from '@modern-js/runtime/router';
227
233
 
228
- const loader = () =>
234
+ export const loader = () =>
229
235
  defer({
230
236
  userInfo: new Promise(resolve => {
231
237
  setTimeout(() => {
@@ -236,8 +242,6 @@ const loader = () =>
236
242
  }, 1000);
237
243
  }),
238
244
  });
239
-
240
- export default loader;
241
245
  ```
242
246
 
243
247
  在 `user/layout.tsx` 中添加以下代码:
@@ -298,12 +302,12 @@ export default () => {
298
302
 
299
303
  ```ts
300
304
  // This won't work!
301
- export default async () => {
305
+ export const loader = async () => {
302
306
  const res = fetch('https://api/user/profile');
303
307
  return res.json();
304
308
  };
305
309
 
306
- import loader from './page.loader.ts';
310
+ import { loader } from './page.data.ts';
307
311
  export default function RouteComp() {
308
312
  const data = loader();
309
313
  }
@@ -315,7 +319,7 @@ export default function RouteComp() {
315
319
  // Not allowed
316
320
  // routes/layout.tsx
317
321
  import { useLoaderData } from '@modern-js/runtime/router';
318
- import { ProfileData } from './page.loader.ts'; // should use "import type" instead
322
+ import { ProfileData } from './page.data.ts'; // should use "import type" instead
319
323
 
320
324
  export const fetch = wrapFetch(fetch);
321
325
 
@@ -324,13 +328,13 @@ export default function UserPage() {
324
328
  return <div>{profileData}</div>;
325
329
  }
326
330
 
327
- // routes/layout.loader.ts
331
+ // routes/layout.data.ts
328
332
  import { fetch } from './layout.tsx'; // should not be imported from the routing component
329
333
  export type ProfileData = {
330
334
  /* some types */
331
335
  };
332
336
 
333
- export default async (): Promise<ProfileData> => {
337
+ export const loader = async (): Promise<ProfileData> => {
334
338
  const res = await fetch('https://api/user/profile');
335
339
  return await res.json();
336
340
  };