@modern-js/main-doc 0.0.0-nightly-20240909025816 → 0.0.0-nightly-20240910170704

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.
Files changed (51) hide show
  1. package/docs/en/apis/app/hooks/api/lambda.mdx +4 -48
  2. package/docs/en/apis/app/hooks/api/middleware.mdx +11 -0
  3. package/docs/en/components/enable-bff.mdx +19 -2
  4. package/docs/en/components/extend-bff-function.mdx +5 -0
  5. package/docs/en/configure/app/auto-load-plugin.mdx +4 -0
  6. package/docs/en/guides/advanced-features/_meta.json +7 -4
  7. package/docs/en/guides/advanced-features/bff/_meta.json +1 -1
  8. package/docs/en/guides/advanced-features/bff/extend-server.mdx +154 -0
  9. package/docs/en/guides/advanced-features/bff/frameworks.mdx +52 -123
  10. package/docs/en/guides/advanced-features/bff/function.mdx +108 -80
  11. package/docs/en/guides/advanced-features/bff/sdk.mdx +40 -51
  12. package/docs/en/guides/advanced-features/build-performance.mdx +2 -2
  13. package/docs/en/guides/advanced-features/page-performance/_meta.json +1 -0
  14. package/docs/en/guides/basic-features/render/streaming-ssr.mdx +1 -1
  15. package/docs/en/guides/basic-features/routes.mdx +1 -1
  16. package/docs/en/guides/basic-features/static-assets.mdx +1 -1
  17. package/docs/zh/apis/app/hooks/api/lambda.mdx +5 -48
  18. package/docs/zh/apis/app/hooks/api/middleware.mdx +11 -0
  19. package/docs/zh/components/enable-bff.mdx +19 -2
  20. package/docs/zh/components/extend-bff-function.mdx +5 -0
  21. package/docs/zh/configure/app/auto-load-plugin.mdx +4 -0
  22. package/docs/zh/guides/advanced-features/_meta.json +7 -4
  23. package/docs/zh/guides/advanced-features/bff/_meta.json +1 -1
  24. package/docs/zh/guides/advanced-features/bff/extend-server.mdx +156 -0
  25. package/docs/zh/guides/advanced-features/bff/frameworks.mdx +51 -117
  26. package/docs/zh/guides/advanced-features/bff/function.mdx +69 -59
  27. package/docs/zh/guides/advanced-features/bff/sdk.mdx +27 -36
  28. package/docs/zh/guides/advanced-features/build-performance.mdx +2 -2
  29. package/docs/zh/guides/advanced-features/page-performance/_meta.json +1 -0
  30. package/docs/zh/guides/advanced-features/rsbuild-plugin.mdx +1 -1
  31. package/docs/zh/guides/advanced-features/rspack-start.mdx +2 -2
  32. package/docs/zh/guides/basic-features/alias.mdx +5 -11
  33. package/docs/zh/guides/basic-features/env-vars.mdx +1 -1
  34. package/docs/zh/guides/basic-features/static-assets.mdx +1 -1
  35. package/i18n.json +4 -0
  36. package/package.json +4 -4
  37. package/rspress.config.ts +1 -3
  38. package/docs/en/apis/app/hooks/api/api.mdx +0 -80
  39. package/docs/en/apis/app/hooks/api/app.mdx +0 -12
  40. package/docs/en/guides/advanced-features/bff/type.mdx +0 -46
  41. package/docs/zh/apis/app/hooks/api/api.mdx +0 -81
  42. package/docs/zh/apis/app/hooks/api/app.mdx +0 -12
  43. package/docs/zh/guides/advanced-features/bff/type.mdx +0 -46
  44. /package/docs/en/guides/advanced-features/{bff/index.mdx → bff.mdx} +0 -0
  45. /package/docs/en/guides/advanced-features/{code-split.mdx → page-performance/code-split.mdx} +0 -0
  46. /package/docs/en/guides/advanced-features/{inline-assets.mdx → page-performance/inline-assets.mdx} +0 -0
  47. /package/docs/en/guides/advanced-features/{optimize-bundle.mdx → page-performance/optimize-bundle.mdx} +0 -0
  48. /package/docs/zh/guides/advanced-features/{bff/index.mdx → bff.mdx} +0 -0
  49. /package/docs/zh/guides/advanced-features/{code-split.mdx → page-performance/code-split.mdx} +0 -0
  50. /package/docs/zh/guides/advanced-features/{inline-assets.mdx → page-performance/inline-assets.mdx} +0 -0
  51. /package/docs/zh/guides/advanced-features/{optimize-bundle.mdx → page-performance/optimize-bundle.mdx} +0 -0
@@ -6,13 +6,16 @@
6
6
  "label": "use-bff",
7
7
  "collapsed": true
8
8
  },
9
- "code-split",
9
+ {
10
+ "type": "dir",
11
+ "name": "page-performance",
12
+ "label": "page-performance",
13
+ "collapsed": true
14
+ },
15
+ "build-performance",
10
16
  "compatibility",
11
17
  "eslint",
12
18
  "low-level",
13
19
  "source-build",
14
- "build-performance",
15
- "inline-assets",
16
- "optimize-bundle",
17
20
  "web-server"
18
21
  ]
@@ -1 +1 @@
1
- ["function", "type", "frameworks", "sdk"]
1
+ ["function", "frameworks", "extend-server", "sdk"]
@@ -0,0 +1,156 @@
1
+ # 扩展 BFF Server
2
+
3
+ 部分应用中,开发者可能希望对所有 BFF 函数做统一的处理,例如鉴权、日志、数据处理等。
4
+
5
+ Modern.js 提供了两种方式,允许开发者根据运行时框架自由扩展 BFF Server。
6
+
7
+ ## 中间件
8
+
9
+ 开发者可以在编写 `api/_app.ts` 文件中编写中间件,用来扩展 BFF Server。以 Express 作为运行时框架为例,介绍如何手写一个中间件,添加权限校验:
10
+
11
+ ```ts title="api/_app.ts"
12
+ import { hook } from '@modern-js/runtime/server';
13
+ import { Request, Response, NextFunction } from 'express';
14
+
15
+ export default hook(({ addMiddleware }) => {
16
+ addMiddleware(async (req: Request, res: Response, next: NextFunction) => {
17
+ if (req.url !== '/api/login') {
18
+ const sid = req?.cookies?.sid;
19
+ if (!sid) {
20
+ res.status(400);
21
+ res.json({ code: -1, message: 'need login' });
22
+ } else {
23
+ next();
24
+ }
25
+ } else {
26
+ next();
27
+ }
28
+ });
29
+ });
30
+ ```
31
+
32
+
33
+ 然后添加一个普通的 BFF 函数 `api/lambda/hello.ts`:
34
+
35
+ ```ts title="api/lambda/hello.ts"
36
+ export default async () => {
37
+ return 'Hello Modern.js';
38
+ };
39
+ ```
40
+
41
+ 接下来,在前端 `src/routes/page.tsx` 添加接口的访问代码,直接使用一体化的方式调用:
42
+
43
+ ```ts title="src/routes/page.tsx"
44
+ import { useState, useEffect } from 'react';
45
+ import { get as hello } from '@api/hello';
46
+
47
+ export default () => {
48
+ const [text, setText] = useState('');
49
+
50
+ useEffect(() => {
51
+ async function fetchMyApi() {
52
+ const { message } = await hello();
53
+ setText(message);
54
+ }
55
+
56
+ fetchMyApi();
57
+ }, []);
58
+
59
+ return <p>{text}</p>;
60
+ };
61
+ ```
62
+
63
+ 现在运行 `dev` 命令启动项目,访问 `http://localhost:8080/` 会发现 `/api/hello` 的请求被拦截了:
64
+
65
+ ![Network](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/network2.png)
66
+
67
+ 最后修改前端代码 `src/routes/page.tsx`,在访问 `/api/hello` 前先调用登录接口:
68
+
69
+ :::note
70
+ 此处没有真实实现登录接口,代码仅作为演示。
71
+ :::
72
+
73
+ ```ts
74
+ import { useState, useEffect } from 'react';
75
+ import { get as hello } from '@api/hello';
76
+ import { post as login } from '@api/login';
77
+
78
+ export default () => {
79
+ const [text, setText] = useState('');
80
+
81
+ useEffect(() => {
82
+ async function fetchAfterLogin() {
83
+ const { code } = await login();
84
+ if (code === 0) {
85
+ const { message } = await hello();
86
+ setText(message);
87
+ }
88
+ }
89
+ fetchAfterLogin();
90
+ }, []);
91
+
92
+ return <p>{text}</p>;
93
+ };
94
+ ```
95
+
96
+ 刷新页面,可以看到 `/api/hello` 访问成功:
97
+
98
+ ![Network](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/network3.png)
99
+
100
+ 以上代码模拟了在 `/api/_app.ts` 中添加中间件的方式,实现了简易的登录功能。同样,可以在这个钩子文件中实现其他功能来扩展 BFF Server。
101
+
102
+ :::info
103
+ 不同运行时框架中,中间件的写法不一定相同,详情可见[运行时框架](/guides/advanced-features/bff/frameworks)。
104
+
105
+ :::
106
+
107
+ ## 定义 Server 实例
108
+
109
+ 除了中间件之外,还可以通过 `api/app.ts` 文件来定义 BFF Server 的实例。开发者需要默认导出一个能被运行时框架插件识别的实例。这里简单展示一下 Koa 和 Express 如何定义 Server 实例。
110
+
111
+ import { Tabs, Tab as TabItem } from "@theme";
112
+
113
+ <Tabs>
114
+ <TabItem value="express" label="Express.js" default>
115
+
116
+ ```ts title="api/app.ts"
117
+ import express from 'express';
118
+
119
+ const app = express();
120
+ app.use(async (req, res, next) => {
121
+ console.info(`access url: ${req.url}`);
122
+ next();
123
+ });
124
+
125
+ export default app;
126
+ ```
127
+
128
+ </TabItem>
129
+ <TabItem value="koa" label="Koa.js">
130
+
131
+ ```ts title="api/app.ts"
132
+ import koa from 'koa';
133
+
134
+ const app = new Koa();
135
+ app.use(async (ctx, next) => {
136
+ console.info(`access url: ${ctx.url}`);
137
+ await next();
138
+ });
139
+
140
+ export default app;
141
+ ```
142
+
143
+ </TabItem>
144
+ </Tabs>
145
+
146
+ 在复杂的 BFF 逻辑中,定义 Server 实例可以更方便通过一个入口来组织代码逻辑,设计目录结构。在这个文件中,可以执行初始化逻辑,添加全局中间件,声明路由,甚至扩展原有框架。
147
+
148
+ BFF 函数定义的路由会在 `app.ts` 文件定义的路由之后注册,所以在这里你也可以拦截 BFF 函数定义的路由,进行预处理或是提前响应。
149
+
150
+ :::note
151
+ 此时,如果应用中同时存在 `api/_app.ts`,则定义的中间件会被放在 `api/app.ts` 导出实例的最后执行。多数情况下,中间件就能覆盖大多数 BFF 函数的定制需求。只有当应用的服务端逻辑比较复杂时,才需要自定义 Server 实例。
152
+ :::
153
+
154
+ :::caution
155
+ 当应用中没有 `api/app.ts` 的时候,Modern.js 默认会添加 [koa-body](https://www.npmjs.com/package/koa-body)。当应用中存在 `api/app.ts` 时,如果开发者希望使用带有 Body 的 BFF 函数,需要**自行添加** `koa-body`。
156
+ :::
@@ -2,151 +2,85 @@
2
2
  sidebar_position: 3
3
3
  title: 运行时框架
4
4
  ---
5
+
5
6
  # 运行时框架
6
7
 
7
8
  Modern.js 的 BFF 支持不同的运行时框架,当前 Modern.js 的 BFF 支持两种运行时框架 [Express.js](https://expressjs.com/) 和 [Koa.js](https://koajs.com/)。
8
9
 
9
- ## 函数写法
10
+ 使用不同运行时框架时,有部分 API 会存在差异。
10
11
 
11
- 在函数写法下,各类运行时框架仅中间件写法存在差异,其他实现基本相同。这里以 Express 为例,介绍如何在 `api/_app.ts` 中,手写一个中间件,添加权限校验:
12
+ ## 获取请求上下文
12
13
 
13
- ```ts
14
- import { hook } from '@modern-js/runtime/server';
15
- import { Request, Response, NextFunction } from 'express';
14
+ 在 BFF 函数中,有时需要获取请求上下文,来处理更多逻辑。此时,根据不同的运行时框架,你需要从通过不同的 API 来获取:
16
15
 
17
- export default hook(({ addMiddleware }) => {
18
- addMiddleware(async (req: Request, res: Response, next: NextFunction) => {
19
- if (req.url !== '/api/login') {
20
- const sid = req?.cookies?.sid;
21
- if (!sid) {
22
- res.status(400);
23
- res.json({ code: -1, message: 'need login' });
24
- } else {
25
- next();
26
- }
27
- } else {
28
- next();
29
- }
30
- });
31
- });
32
- ```
16
+ import { Tabs, Tab as TabItem } from "@theme";
33
17
 
34
- 然后添加一个普通的 BFF 函数 `/api/hello.ts`:
18
+ <Tabs>
19
+ <TabItem value="express" label="Express.js" default>
35
20
 
36
- ```ts
37
- export default async () => {
38
- return 'Hello Modern.js';
21
+ ```ts title="api/lambda/hello.ts"
22
+ import { useContext } from '@modern-js/runtime/express'
23
+ export const get = async () => {
24
+ const { req } = useContext();
25
+ console.info(`access url: ${req.url}`);
26
+ return 'Hello Modern.js'
39
27
  };
40
28
  ```
41
29
 
42
- 最后在前端 `src/App.tsx` 添加接口的访问代码,直接使用一体化的方式调用:
30
+ </TabItem>
31
+ <TabItem value="koa" label="Koa.js">
43
32
 
44
- ```ts
45
- import { useState, useEffect } from 'react';
46
- import { get as hello } from '@api/hello';
47
-
48
- export default () => {
49
- const [text, setText] = useState('');
50
-
51
- useEffect(() => {
52
- async function fetchMyApi() {
53
- const { message } = await hello();
54
- setText(message);
55
- }
56
-
57
- fetchMyApi();
58
- }, []);
59
-
60
- return <p>{text}</p>;
33
+ ```ts title="api/lambda/hello.ts"
34
+ import { useContext } from '@modern-js/runtime/koa'
35
+ export const get = async () => {
36
+ const ctx = useContext();
37
+ console.info(`access url: ${req.url}`);
38
+ return 'Hello Modern.js'
61
39
  };
62
40
  ```
63
41
 
64
- 然后 `pnpm run dev` 启动项目,访问 `http://localhost:8080/` 会发现 `/api/hello` 的请求被拦截了:
42
+ </TabItem>
43
+ </Tabs>
65
44
 
66
- ![Network](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/network2.png)
45
+ :::info
46
+ 详细内容可以参考 [useContext](/apis/app/runtime/bff/use-context)。
47
+ :::
67
48
 
68
- 最后再修改前端代码 `src/App.tsx`,在访问 `/api/hello` 前先调用登录接口:
49
+ ## 使用中间件
69
50
 
70
- ```ts
71
- import { useState, useEffect } from 'react';
72
- import { get as hello } from '@api/hello';
73
- import { post as login } from '@api/login';
51
+ 在 BFF 函数中,有时需要使用中间件,来处理更多逻辑。此时,根据不同的运行时框架,你需要从通过不同的 API 来获取:
74
52
 
75
- export default () => {
76
- const [text, setText] = useState('');
53
+ <Tabs>
54
+ <TabItem value="express" label="Express.js" default>
77
55
 
78
- useEffect(() => {
79
- async function fetchAfterLogin() {
80
- const { code } = await login();
81
- if (code === 0) {
82
- const { message } = await hello();
83
- setText(message);
84
- }
85
- }
86
- fetchAfterLogin();
87
- }, []);
56
+ ```ts title="api/_app.ts"
57
+ import { hook } from '@modern-js/runtime/express';
88
58
 
89
- return <p>{text}</p>;
90
- };
59
+ export default hook(({ addMiddleware }) => {
60
+ addMiddleware((req, res, next) => {
61
+ req.query.id = 'koa';
62
+ next();
63
+ });
64
+ });
91
65
  ```
92
66
 
93
- 刷新页面,可以看到 `/api/hello` 访问成功:
94
-
95
- ![Network](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/network3.png)
96
-
97
- 以上代码模拟了在 `/api/_app.ts` 中添加中间件的方式,实现了简易的登录功能。同样,可以在这个钩子文件中实现其他功能来扩展 BFF Server。
98
-
99
- ## 框架写法
100
-
101
- 框架写法下,Modern.js 不会收集 `api/_app.ts` 中的中间件,运行流程由插件自行控制。
102
-
103
- ### Express
67
+ </TabItem>
68
+ <TabItem value="koa" label="Koa.js">
104
69
 
105
- Express 的框架写法支持可在 `api/app.[tj]s` 定义 API Server 的启动逻辑,执行应用的初始化工作,添加全局中间件,声明路由,甚至扩展原有框架等。
70
+ ```ts title="api/_app.ts"
71
+ import { hook } from '@modern-js/runtime/koa';
106
72
 
107
- BFF 函数定义的路由会在 `app.ts` 文件定义的路由之后注册,所以在这里你也可以拦截 BFF 函数定义的路由,进行预处理或是提前响应。
108
-
109
- ```ts title="api/app.ts"
110
- import express from 'express';
111
-
112
- const app = express();
113
-
114
- app.put('/user', function (req, res) {
115
- res.send('Got a PUT request at /user');
116
- });
117
-
118
- app.use(async (req, res, next) => {
119
- console.info(`access url: ${req.url}`);
120
- next();
73
+ export default hook(({ addMiddleware }) => {
74
+ addMiddleware(async (ctx, next) => {
75
+ ctx.req.query.id = 'koa';
76
+ await next();
77
+ });
121
78
  });
122
-
123
- export default app;
124
79
  ```
125
80
 
126
- ### Koa
127
-
128
- Koa 框架写法与 Express 类似,支持在 `app.[tj]s` 定义 API Server 的启动逻辑,执行应用的初始化工作,添加全局中间件,声明路由,扩展原有框架等。
129
-
130
- BFF 函数定义的路由会在 `app.ts` 文件定义的路由之后注册,所以在这里你也可以拦截 BFF 函数定义的路由,进行预处理或是提前响应。
131
-
132
- :::caution 注意
133
- 在框架写法下,当没有 `app.ts` 的时候,Modern.js 默认会添加 `koa-body`;当有 `app.ts` 时,如果开发者希望使用带有 Body 的 BFF 函数,需要确保 `koa-body` 中间件已经添加。
81
+ </TabItem>
82
+ </Tabs>
134
83
 
84
+ :::info
85
+ 详细内容可以参考 [hook](/apis/app/runtime/bff/hook)。
135
86
  :::
136
-
137
- ```ts title=api/app.ts
138
- import koa from 'koa';
139
-
140
- const app = new Koa();
141
-
142
- app.put('/user', function (req, res) {
143
- res.send('Got a PUT request at /user');
144
- });
145
-
146
- app.use(async (ctx, next) => {
147
- console.info(`access url: ${ctx.url}`);
148
- await next();
149
- });
150
-
151
- export default app;
152
- ```
@@ -1,10 +1,8 @@
1
- ---
2
- sidebar_position: 1
3
- title: 基础用法
4
- ---
5
1
  # 基础用法
6
2
 
7
- 通过 Modern.js 开发的应用,可以在 `api/` 目录下定义接口函数,前端可以调用这些接口函数,即可发起请求,无需写前后端胶水层代码,同时保证前后端类型安全。
3
+ Modern.js 应用中,开发者可以在 `api/lambda` 目录下定义接口文件,并导出接口函数。在前端代码中,可以用文件引用的方式,直接调用这些接口函数,发起接口请求。
4
+
5
+ 这种调用方式我们称为**一体化调用**,开发者无需编写前后端胶水层代码,并天然地保证前后端类型安全。
8
6
 
9
7
  ## 启用 BFF
10
8
 
@@ -14,20 +12,15 @@ import EnableBFF from "@site-docs/components/enable-bff"
14
12
 
15
13
  ## BFF 函数
16
14
 
17
- 允许通过一体化调用的函数,称为 **BFF 函数**。这里写一个最简单的 BFF 函数,创建 `api/hello.ts` 文件:
18
-
19
- :::caution
20
- 如果是框架模式(有 `api/lambda` 目录),需要创建 `api/lambda/hello.ts`
21
-
22
- :::
15
+ 允许使用一体化调用的函数,我们称为 **BFF 函数**。这里写一个最简单的 BFF 函数,首先创建 `api/lambda/hello.ts` 文件:
23
16
 
24
- ```ts title="api/hello.ts"
17
+ ```ts title="api/lambda/hello.ts"
25
18
  export const get = async () => 'Hello Modern.js';
26
19
  ```
27
20
 
28
- 接着在 `src/App.tsx` 中直接引入函数并调用:
21
+ 接着在 `src/routes/page.tsx` 中直接引入函数并调用:
29
22
 
30
- ```tsx title=src/App.tsx
23
+ ```tsx title="src/routes/page.tsx"
31
24
  import { useState, useEffect } from 'react';
32
25
  import { get as hello } from '@api/hello';
33
26
 
@@ -41,56 +34,49 @@ export default () => {
41
34
  };
42
35
  ```
43
36
 
44
- :::info
45
- Modern.js 生成器已经在 `tsconfig.json` 中配置 `@api` 别名,因此可以直接通过别名的方式引入函数。
37
+ :::tip
38
+ 在调用 `new` 命令后,Modern.js 生成器会自动在 `tsconfig.json` 中配置 `@api` 别名,因此可以直接通过别名的方式引入函数。
46
39
 
47
40
  :::
48
41
 
49
- 在 `src/App.tsx` 中引入的函数,会自动转换成接口调用,不需要再去通过 fetch 去调用接口。
42
+ 在 `src/routes/page.tsx` 中引入的函数,会自动转换成接口调用,不需要再通过请求 SDK 或 Web Fetch 调用接口。
50
43
 
51
- 执行 `pnpm run dev` 打开 `http://localhost:8080/` 可以看到页面已经展示了 BFF 函数返回的内容,在 Network 中可以看到页面向 `http://localhost:8080/api/hello` 发送了请求:
44
+ 执行 `pnpm run dev` 后,打开 `http://localhost:8080/` 可以看到页面已经展示了 BFF 函数返回的内容,在 Network 中可以看到页面向 `http://localhost:8080/api/hello` 发送了请求:
52
45
 
53
46
  ![Network](https://p6-piu.byteimg.com/tos-cn-i-8jisjyls3a/fd41750f8d414179a9b4ecb519919b36~tplv-8jisjyls3a-3:0:0:q75.png)
54
47
 
55
48
  ## 函数路由
56
49
 
57
- Modern.js 中,BFF 函数对应的路由系统是基于文件系统实现的,也是一种**约定式路由**。
58
-
59
- 函数写法下 `api/` 下的所有文件中的每个 BFF 函数都会映射为一个接口。框架写法下 `api/lambda` 下的所有文件中的每个 BFF 函数都会映射为一个接口。
60
-
61
- :::note
62
- 函数写法和框架写法会在下一节详细介绍。
63
-
64
- :::
50
+ Modern.js 中,BFF 函数对应的路由系统是基于文件系统实现的,也是一种**约定式路由**。`api/lambda` 下的所有文件中的每个 BFF 函数都会映射为一个接口,下面介绍几种路由的约定。
65
51
 
52
+ :::info
66
53
  所有 BFF 函数生成的路由都带有统一的前缀,默认值为 `/api`。可以通过 [bff.prefix](/configure/app/bff/prefix) 设置公共路由的前缀。
67
-
68
- 下面介绍几种路由的约定。
54
+ :::
69
55
 
70
56
  ### 默认路由
71
57
 
72
58
  以 `index.[jt]s` 命名的文件会被映射到上一层目录。
73
59
 
74
- - `api/index.ts` -> `{prefix}/`
75
- - `api/user/index.ts` -> `{prefix}/user`
60
+ - `api/lambda/index.ts` -> `{prefix}/`
61
+ - `api/lambda/user/index.ts` -> `{prefix}/user`
76
62
 
77
63
  ### 多层路由
78
64
 
79
65
  支持解析嵌套的文件,如果创建嵌套文件夹结构,文件仍会以相同方式自动解析路由。
80
66
 
81
- - `api/hello.ts` -> `{prefix}/hello`
82
- - `api/user/list.ts` -> `{prefix}/user/list`
67
+ - `api/lambda/hello.ts` -> `{prefix}/hello`
68
+ - `api/lambda/user/list.ts` -> `{prefix}/user/list`
83
69
 
84
70
  ### 动态路由
85
71
 
86
- 同样的,创建命名带有 `[xxx]` 的文件夹或者文件,支持动态的命名路由参数。动态路由的函数参数规则可以看 [dynamac-path](/guides/advanced-features/bff/function#dynamic-path)
72
+ 同样的,创建命名带有 `[xxx]` 的文件夹或者文件,支持动态的命名路由参数。动态路由的函数参数规则可以看 [dynamac-path](/guides/advanced-features/bff/function#dynamic-path)
87
73
 
88
- - `api/user/[username]/info.ts` -> `{prefix}/user/:username/info`
89
- - `api/user/username/[action].ts` -> `{prefix}/user/username/:action`
74
+ - `api/lambda/user/[username]/info.ts` -> `{prefix}/user/:username/info`
75
+ - `api/lambda/user/username/[action].ts` -> `{prefix}/user/username/:action`
90
76
 
91
77
  ### 白名单
92
78
 
93
- 默认 `api/` 目录下所有文件都会当作 BFF 函数文件去解析,但以下文件不会被解析:
79
+ 默认 `api/lambda/` 目录下所有文件都会当作 BFF 函数文件去解析,但以下文件不会被解析:
94
80
 
95
81
  - 命名以 `_` 开头的文件。例如:`_utils.ts`。
96
82
  - 命名以 `_` 开头的文件夹下所有文件。例如:`_utils/index.ts`、`_utils/cp.ts`。
@@ -100,20 +86,18 @@ Modern.js 中,BFF 函数对应的路由系统是基于文件系统实现的,
100
86
 
101
87
  ## RESTful API
102
88
 
103
- Modern.js 的 BFF 函数需要遵循 RESTful API 标准来定义, 遵循 HTTP Method 规范,并且不允许自由定义参数。
104
-
105
- :::info
106
- 假设函数允许自由定义参数,产出的路由必然由**私有协议**进行调用(原因是无法区分请求参数与请求体),而无法实现任意的 RESTful API。
89
+ Modern.js 的 BFF 函数需要遵循 RESTful API 标准来定义,开发者需要按照一系列规则来定义 BFF 函数。
107
90
 
108
- 如果服务仅用于应用本身不存在问题,但它**不标准的接口定义**无法融入更大的体系。 在多个系统共同工作的情况下(例如 BFF 低码搭建),会导致其他系统也需要遵循**私有协议**。
91
+ :::tip 设计原则
92
+ BFF 函数不仅会在项目中被调用,也应该允许其他项目通过请求 SDK 或 Web fetch 调用。因此 Modern.js 没有在一体化调用时定义**私有协议**,而是通过标准的 HTTP Method,以及 `params`、`query`、`body` 等通用的 HTTP 请求参数来定义函数。
109
93
 
110
94
  :::
111
95
 
112
- ### 函数具名导出
96
+ ### 函数导出规则
113
97
 
114
- Modern.js BFF 函数的导出名决定了函数对应接口的 Method,如 `get`,`post` 等。
98
+ #### HTTP Method 具名函数
115
99
 
116
- 例如,按照以下例子,可导出一个 GET 接口。
100
+ Modern.js BFF 函数的导出名决定了函数对应接口的 HTTP Method,如 `get`,`post` 等。例如导出一个 GET 接口:
117
101
 
118
102
  ```ts
119
103
  export const get = async () => {
@@ -124,7 +108,7 @@ export const get = async () => {
124
108
  };
125
109
  ```
126
110
 
127
- 按照以下例子,则可导出一个 `POST` 接口
111
+ 按照以下例子,则可导出一个 `POST` 接口:
128
112
 
129
113
  ```ts
130
114
  export const post = async () => {
@@ -139,24 +123,46 @@ export const post = async () => {
139
123
 
140
124
  - 名字是大小不敏感的,如果是 `GET`,写成 `get`、`Get`、`GEt`、`GET`,都可以准确识别。而默认导出,即 `export default xxx` 则会被映射为 `Get`。
141
125
 
142
- - 可以在一个文件中定义多个不同 Method 的函数,但如果定义多个相同 Method 的函数,则只有第一个会生效。
126
+ #### 使用 Async 函数
143
127
 
144
- :::info
145
- 需要注意的是,定义的函数都应该是异步的,与函数调用时类型有关,后面会提到。
128
+ Modern.js 推荐将 BFF 函数定义为 Async 异步函数,即使函数中不存在异步流程,例如:
146
129
 
147
- :::
130
+ ```ts
131
+ export const get = async () => {
132
+ return {
133
+ name: 'Modern.js',
134
+ desc: '现代 web 工程方案',
135
+ };
136
+ };
137
+ ```
148
138
 
149
- ### 函数参数规则
139
+ 这是因为在前端调用时,BFF 函数会自动转换成 HTTP 接口调用,而 HTTP 接口调用时异步的,在前端通常会这样使用:
140
+
141
+ ```tsx title="src/routes/page.tsx"
142
+ import { useState, useEffect } from 'react';
143
+ import { get as hello } from '@api/hello';
144
+
145
+ export default () => {
146
+ const [text, setText] = useState('');
147
+
148
+ useEffect(() => {
149
+ hello().then(setText);
150
+ }, []);
151
+ return <div>{text}</div>;
152
+ };
153
+ ```
150
154
 
151
- 如上所述,为了满足 RESTful API 的设计标准,因此 Modern.js 中 BFF 函数需要遵循一定的入参规则。
155
+ 因此,为了保持类型定义与实际调用体验统一,我们推荐在定义 BFF 函数时将它设置为异步函数。
152
156
 
153
- 函数参数分为两块,分别是请求路径中的动态部分和请求选项 `RequestOption`。
157
+ ### 函数参数规则
158
+
159
+ 函数参数规则分为两块,分别是请求路径中的动态路由(Dynamic Path)和请求选项(RequestOption)。
154
160
 
155
161
  #### Dynamic Path
156
162
 
157
- 动态路由会作为函数第一部分的入参,每个入参对应一段动态路由。例如以下示例,uid 会作为前两个参数传递到函数中:
163
+ 动态路由会作为 BFF 函数第一部分的入参,每个入参对应一段动态路由。例如以下示例,`level` 和 `id` 会作为前两个参数传递到函数中:
158
164
 
159
- ```ts title="api/[level]/[id].ts"
165
+ ```ts title="api/lambda/[level]/[id].ts"
160
166
  export default async (level: number, id: number) => {
161
167
  const userData = await queryUser(level, uid);
162
168
  return userData;
@@ -165,7 +171,7 @@ export default async (level: number, id: number) => {
165
171
 
166
172
  在调用时直接传入动态参数:
167
173
 
168
- ```ts title="App.tsx"
174
+ ```tsx title="src/routes/page.tsx"
169
175
  import { useState, useEffect } from 'react';
170
176
  import { get as getUser } from '@api/[level]/[id]';
171
177
 
@@ -186,7 +192,7 @@ Dynamic Path 之后的参数是包含 querystring、request body 的对象 `Requ
186
192
 
187
193
  在不存在动态路由的普通函数中,可以从第一个入参中获取传入的 `data` 和 `query`,例如:
188
194
 
189
- ```ts title="api/hello.ts"
195
+ ```ts title="api/lambda/hello.ts"
190
196
  import type { RequestOption } from '@modern-js/runtime/server';
191
197
 
192
198
  export async function post({
@@ -216,7 +222,7 @@ export async function post({ query, data }: { query: IQuery; data: IData }) {
216
222
 
217
223
  当函数文件使用动态路由规则时,动态路由会在 `RequestOption` 对象参数前。
218
224
 
219
- ```ts title="api/[sku]/[id]/item.ts"
225
+ ```ts title="api/lambda/[sku]/[id]/item.ts"
220
226
  export async function post(
221
227
  sku: string,
222
228
  id: string,
@@ -231,7 +237,7 @@ export async function post(
231
237
 
232
238
  调用时也按照函数定义,传入对应的参数即可:
233
239
 
234
- ```ts title="App.tsx"
240
+ ```ts title="src/routes/page.tsx"
235
241
  import { post } from '@api/[sku]/[id]/item';
236
242
 
237
243
  export default () => {
@@ -250,4 +256,8 @@ export default () => {
250
256
  };
251
257
  ```
252
258
 
253
- 之前提到,定义的函数都应该是异步的,是因为在前端调用时会自动转换成 HTTP 接口调用,所以为了保持类型定义与实际调用体验统一,需要在定义 BFF 函数时将它设置为异步。
259
+ ## 扩展 BFF 函数
260
+
261
+ import ExtendBFF from "@site-docs/components/extend-bff-function"
262
+
263
+ <ExtendBFF/>