@modern-js/main-doc 2.6.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +17 -0
- package/README.md +2 -2
- package/en/apis/app/commands.mdx +2 -0
- package/en/apis/app/hooks/config/upload.mdx +10 -0
- package/en/apis/app/runtime/model/connect.mdx +1 -1
- package/en/apis/app/runtime/model/model_.mdx +1 -1
- package/en/apis/app/runtime/model/use-model.mdx +1 -1
- package/en/apis/app/runtime/web-server/hook.mdx +2 -2
- package/en/apis/app/runtime/web-server/middleware.mdx +33 -9
- package/en/components/enable-bff.mdx +4 -4
- package/en/components/init-rspack-app.mdx +7 -0
- package/en/configure/app/bff/enable-handle-web.mdx +24 -0
- package/en/configure/app/server/enable-framework-ext.mdx +1 -1
- package/en/configure/app/server/ssr.mdx +18 -0
- package/en/guides/advanced-features/bff/_category_.json +1 -1
- package/en/guides/advanced-features/eslint.mdx +30 -32
- package/en/guides/advanced-features/low-level.mdx +1 -1
- package/en/guides/advanced-features/rspack-start.mdx +13 -17
- package/en/guides/advanced-features/ssr.mdx +210 -5
- package/en/guides/advanced-features/web-server.mdx +87 -20
- package/en/guides/{css/tailwindcss.mdx → basic-features/css.mdx} +60 -3
- package/en/guides/basic-features/env-vars.mdx +4 -0
- package/en/guides/concept/builder.mdx +1 -1
- package/en/guides/topic-detail/framework-plugin/extend.mdx +20 -19
- package/en/guides/topic-detail/framework-plugin/hook-list.mdx +156 -155
- package/en/guides/topic-detail/framework-plugin/hook.mdx +58 -43
- package/en/guides/topic-detail/framework-plugin/implement.mdx +47 -49
- package/en/guides/topic-detail/framework-plugin/introduction.mdx +22 -23
- package/en/guides/topic-detail/framework-plugin/plugin-api.mdx +13 -13
- package/en/guides/topic-detail/framework-plugin/relationship.mdx +30 -30
- package/en/guides/topic-detail/generator/plugin/develop.mdx +1 -1
- package/en/guides/topic-detail/micro-frontend/c01-introduction.mdx +17 -17
- package/en/guides/topic-detail/micro-frontend/c02-development.mdx +76 -78
- package/en/guides/topic-detail/micro-frontend/c03-main-app.mdx +57 -51
- package/en/guides/topic-detail/micro-frontend/c04-communicate.mdx +11 -11
- package/en/guides/topic-detail/micro-frontend/c05-mixed-stack.mdx +13 -13
- package/en/guides/topic-detail/model/auto-actions.mdx +18 -21
- package/en/guides/topic-detail/model/computed-state.mdx +27 -25
- package/en/guides/topic-detail/model/define-model.mdx +14 -14
- package/en/guides/topic-detail/model/faq.mdx +12 -13
- package/en/guides/topic-detail/model/manage-effects.mdx +43 -52
- package/en/guides/topic-detail/model/model-communicate.mdx +47 -45
- package/en/guides/topic-detail/model/performance.mdx +22 -23
- package/en/guides/topic-detail/model/quick-start.mdx +29 -28
- package/en/guides/topic-detail/model/redux-integration.mdx +7 -7
- package/en/guides/topic-detail/model/test-model.mdx +11 -11
- package/en/guides/topic-detail/model/typescript-best-practice.mdx +16 -15
- package/en/guides/topic-detail/model/use-model.mdx +40 -45
- package/en/guides/topic-detail/model/use-out-of-modernjs.mdx +16 -16
- package/en/guides/troubleshooting/cli.mdx +2 -2
- package/en/tutorials/first-app/c03-css.mdx +1 -1
- package/package.json +5 -5
- package/zh/apis/app/commands.mdx +2 -0
- package/zh/apis/app/hooks/config/upload.mdx +12 -2
- package/zh/apis/app/runtime/model/connect.mdx +1 -1
- package/zh/apis/app/runtime/model/model_.mdx +1 -1
- package/zh/apis/app/runtime/model/use-model.mdx +1 -1
- package/zh/apis/app/runtime/web-server/hook.mdx +2 -4
- package/zh/apis/app/runtime/web-server/middleware.mdx +30 -10
- package/zh/apis/monorepo/commands/gen-release-note.mdx +3 -3
- package/zh/components/enable-bff.mdx +4 -4
- package/zh/components/init-rspack-app.mdx +7 -0
- package/zh/components/release-note.mdx +1 -1
- package/zh/configure/app/bff/enable-handle-web.mdx +24 -0
- package/zh/configure/app/server/enable-framework-ext.mdx +1 -1
- package/zh/configure/app/server/ssr.mdx +19 -1
- package/zh/guides/advanced-features/bff/_category_.json +1 -1
- package/zh/guides/advanced-features/rspack-start.mdx +13 -17
- package/zh/guides/advanced-features/ssr.mdx +210 -4
- package/zh/guides/advanced-features/web-server.mdx +81 -16
- package/zh/guides/{css/tailwindcss.mdx → basic-features/css.mdx} +60 -3
- package/zh/guides/basic-features/env-vars.mdx +4 -0
- package/zh/guides/concept/builder.mdx +1 -1
- package/zh/guides/topic-detail/changesets/github.mdx +2 -2
- package/zh/guides/topic-detail/changesets/release-note.mdx +1 -1
- package/zh/guides/topic-detail/framework-plugin/plugin-api.mdx +2 -2
- package/zh/guides/topic-detail/generator/plugin/develop.mdx +1 -1
- package/zh/guides/topic-detail/model/faq.mdx +1 -1
- package/zh/guides/topic-detail/model/manage-effects.mdx +1 -1
- package/zh/guides/topic-detail/model/model-communicate.mdx +1 -1
- package/zh/guides/topic-detail/model/performance.mdx +1 -1
- package/zh/guides/topic-detail/model/quick-start.mdx +2 -2
- package/zh/guides/topic-detail/model/use-model.mdx +3 -3
- package/zh/tutorials/first-app/c03-css.mdx +1 -1
- package/en/guides/css/_category_.json +0 -5
- package/en/guides/css/css-in-js.mdx +0 -40
- package/en/guides/css/css-modules.mdx +0 -87
- package/en/guides/css/less-sass.mdx +0 -17
- package/en/guides/css/postcss.mdx +0 -79
- package/zh/guides/css/_category_.json +0 -5
- package/zh/guides/css/css-in-js.mdx +0 -40
- package/zh/guides/css/css-modules.mdx +0 -87
- package/zh/guides/css/less-sass.mdx +0 -17
- package/zh/guides/css/postcss.mdx +0 -80
@@ -26,13 +26,23 @@ sidebar_position: 4
|
|
26
26
|
|
27
27
|
## 更多用法
|
28
28
|
|
29
|
-
|
29
|
+
在 React 组件中,可以通过[内置环境变量](/guides/basic-features/env-vars.html#asset_prefix)来添加该前缀:
|
30
|
+
|
31
|
+
```tsx
|
32
|
+
export default () => {
|
33
|
+
return (
|
34
|
+
<img src={`${process.env.ASSET_PREFIX}/upload/banner.png`}></img>
|
35
|
+
);
|
36
|
+
};
|
37
|
+
```
|
38
|
+
|
39
|
+
另外,不论是在[自定义 HTML](/guides/basic-features/html) 中,或是在 [`config/public/`](/apis/app/hooks/config/public) 下的任意 HTML 文件中,都可以直接使用 HTML 标签引用 `config/upload/` 目录下的资源:
|
30
40
|
|
31
41
|
```html
|
32
42
|
<script src="/upload/index.js"></script>
|
33
43
|
```
|
34
44
|
|
35
|
-
如果设置了 [`output.assetPrefix`](/configure/app/output/asset-prefix) 前缀,也可以直接使用模板语法添加该前缀:
|
45
|
+
如果设置了 [`dev.assetPrefix`](/configure/app/dev/asset-prefix) 或 [`output.assetPrefix`](/configure/app/output/asset-prefix) 前缀,也可以直接使用模板语法添加该前缀:
|
36
46
|
|
37
47
|
```html
|
38
48
|
<script src="<%=assetPrefix %>/upload/index.js"></script>
|
@@ -9,7 +9,7 @@ import ReduckTip from "@site-docs/components/reduck-tip"
|
|
9
9
|
<ReduckTip />
|
10
10
|
|
11
11
|
:::tip 提示
|
12
|
-
Reduck 原始类型较为复杂,以下涉及类型定义的地方,展示的是简化后的类型信息。原始类型见 [**connect**](https://github.com/
|
12
|
+
Reduck 原始类型较为复杂,以下涉及类型定义的地方,展示的是简化后的类型信息。原始类型见 [**connect**](https://github.com/web-infra-dev/reduck/blob/main/packages/react/src/connect.ts)。
|
13
13
|
|
14
14
|
:::
|
15
15
|
|
@@ -9,7 +9,7 @@ import ReduckTip from "@site-docs/components/reduck-tip"
|
|
9
9
|
<ReduckTip />
|
10
10
|
|
11
11
|
:::tip 提示
|
12
|
-
Reduck 原始类型较为复杂,以下涉及类型定义的地方,展示的是简化后的类型信息。原始类型见 [**model**](https://github.com/
|
12
|
+
Reduck 原始类型较为复杂,以下涉及类型定义的地方,展示的是简化后的类型信息。原始类型见 [**model**](https://github.com/web-infra-dev/reduck/blob/main/packages/store/src/model/model.ts)。
|
13
13
|
|
14
14
|
:::
|
15
15
|
|
@@ -9,7 +9,7 @@ import ReduckTip from "@site-docs/components/reduck-tip"
|
|
9
9
|
<ReduckTip />
|
10
10
|
|
11
11
|
:::tip 提示
|
12
|
-
Reduck 原始类型较为复杂,以下涉及类型定义的地方,展示的是简化后的类型信息。原始类型见 [model](https://github.com/
|
12
|
+
Reduck 原始类型较为复杂,以下涉及类型定义的地方,展示的是简化后的类型信息。原始类型见 [model](https://github.com/web-infra-dev/reduck/blob/main/packages/store/src/model/useModel.ts)。
|
13
13
|
|
14
14
|
:::
|
15
15
|
|
@@ -3,11 +3,10 @@ title: Hook
|
|
3
3
|
---
|
4
4
|
# Hook
|
5
5
|
|
6
|
-
用于拓展 Modern.js 内置的 Web Server
|
6
|
+
用于拓展 Modern.js 内置的 Web Server,所有的页面请求都会经过 Hook。
|
7
7
|
|
8
8
|
:::note
|
9
9
|
更多内容可以查看[自定义 Web Server](/guides/advanced-features/web-server)。
|
10
|
-
|
11
10
|
:::
|
12
11
|
|
13
12
|
## 使用姿势
|
@@ -27,7 +26,6 @@ pnpm run new
|
|
27
26
|
? 请选择你想要的操作 创建工程元素
|
28
27
|
? 新建「自定义 Web Server」源码目录
|
29
28
|
```
|
30
|
-
|
31
29
|
:::
|
32
30
|
|
33
31
|
## 函数签名
|
@@ -64,7 +62,7 @@ type HookContext = {
|
|
64
62
|
function Hook(context: HookContext, next: NextFunction): Promsie<void> | void;
|
65
63
|
```
|
66
64
|
|
67
|
-
另外,不同 Hook 额外提供了不同的上下文。目前 Modern.js 支持 `
|
65
|
+
另外,不同 Hook 额外提供了不同的上下文。目前 Modern.js 支持 `AfterMatch` 和 `AfterRender` 两个 Hook。
|
68
66
|
|
69
67
|
```ts
|
70
68
|
type AfterMatchContext = HookContext & {
|
@@ -3,13 +3,10 @@ title: Middleware
|
|
3
3
|
---
|
4
4
|
# Middleware
|
5
5
|
|
6
|
-
用于拓展 Modern.js 内置的 Web Server
|
7
|
-
|
8
|
-
与 [Hook](/apis/app/runtime/web-server/hook) 不同的是,Middleware 可以使用 Server 运行时框架拓展。
|
6
|
+
用于拓展 Modern.js 内置的 Web Server,与 [Hook](/apis/app/runtime/web-server/hook) 不同的是,Middleware 可以直接操作 Node 原生的请求、响应对象,并且可以使用框架拓展。
|
9
7
|
|
10
8
|
:::note
|
11
9
|
更多内容可以查看[自定义 Web Server](/guides/advanced-features/web-server)。
|
12
|
-
|
13
10
|
:::
|
14
11
|
|
15
12
|
## 使用姿势
|
@@ -38,6 +35,11 @@ pnpm run new
|
|
38
35
|
## 函数签名
|
39
36
|
|
40
37
|
```ts
|
38
|
+
type Middleware = (
|
39
|
+
context: MiddlewareContext,
|
40
|
+
next: NextFunction,
|
41
|
+
) => Promise<void> | void;
|
42
|
+
|
41
43
|
type MiddlewareContext = {
|
42
44
|
response: {
|
43
45
|
set: (key: string, value: string) => void;
|
@@ -71,11 +73,6 @@ type MiddlewareContext = {
|
|
71
73
|
res: ServerResponse;
|
72
74
|
};
|
73
75
|
};
|
74
|
-
|
75
|
-
type RequestHandler = (
|
76
|
-
context: Context,
|
77
|
-
next: NextFunction,
|
78
|
-
) => Promise<void> | void;
|
79
76
|
```
|
80
77
|
|
81
78
|
### 参数
|
@@ -86,6 +83,10 @@ type RequestHandler = (
|
|
86
83
|
- `source`:提供 Node.js 原生的 `req` 与 `res` 对象。
|
87
84
|
- `next`:执行当前 Hook 的下一个监听函数(不影响整体服务端流程)。
|
88
85
|
|
86
|
+
:::warning
|
87
|
+
`next` 函数的执行不影响后续内置流程,只控制下一个中间件是否执行。只有当响应被写入时,后续渲染流程才会中断。
|
88
|
+
:::
|
89
|
+
|
89
90
|
## 示例
|
90
91
|
|
91
92
|
### 服务端耗时打点
|
@@ -107,5 +108,24 @@ Modern.js 提供了 `res.locals` 属性用来存放当前请求的局部变量
|
|
107
108
|
export const Middleware = () => async (ctx, next) => {
|
108
109
|
ctx.res.locals.id = 'Modern.js';
|
109
110
|
ctx.res.locals.rpc = createRpcInstance();
|
110
|
-
}
|
111
|
+
};
|
111
112
|
```
|
113
|
+
|
114
|
+
### 框架拓展
|
115
|
+
|
116
|
+
Middleware 还可以和 BFF 一样,使用运行时框架拓展。Modern.js 约定,当使用框架运行时拓展时,类型信息从 `@modern-js/runtime/{namespace}` 下导出,Middleware 可以使用框架语法,例如框架中间件写法,以下是伪代码:
|
117
|
+
|
118
|
+
```ts
|
119
|
+
import { SomeType } from '@modern-js/runtime/{namespace}';
|
120
|
+
|
121
|
+
export const middleware: SomeType = (ctx, next) => {
|
122
|
+
console.log(ctx.url);
|
123
|
+
next();
|
124
|
+
};
|
125
|
+
```
|
126
|
+
|
127
|
+
默认情况下,在安装框架拓展插件后,Web Server 的框架拓展能力是关闭的。如果希望使用框架拓展,可以通过 [`server.enableFrameworkExt`](/configure/app/server/enable-framework-ext.html) 开启。
|
128
|
+
|
129
|
+
:::info
|
130
|
+
框架拓展导出的类型名不一定为 Middleware,命名由框架拓展插件。
|
131
|
+
:::
|
@@ -9,7 +9,7 @@ Usage: modern gen-release-note [options]
|
|
9
9
|
根据当前仓库 changeset 文件生成 Release Note
|
10
10
|
|
11
11
|
Options:
|
12
|
-
--repo <repo> 仓库名称,用于生成 Pull Request 链接, 例如:
|
12
|
+
--repo <repo> 仓库名称,用于生成 Pull Request 链接, 例如: web-infra-dev/modern.js
|
13
13
|
--custom <cumtom> 自定义 Release Note 生成函数
|
14
14
|
-h, --help display help for command
|
15
15
|
```
|
@@ -24,9 +24,9 @@ Options:
|
|
24
24
|
```markdown
|
25
25
|
## Features:
|
26
26
|
|
27
|
-
[[#1160](https://github.com/
|
27
|
+
[[#1160](https://github.com/web-infra-dev/modern.js/pull/1160)] export ExecaError type
|
28
28
|
|
29
29
|
## Bug Fix:
|
30
30
|
|
31
|
-
[[#1264](https://github.com/
|
31
|
+
[[#1264](https://github.com/web-infra-dev/modern.js/pull/1264)] fix: conventional router app use App.init not work
|
32
32
|
```
|
@@ -7,8 +7,8 @@ import { Tabs, Tab as TabItem } from "@theme";
|
|
7
7
|
<TabItem value="express" label="Express.js" default>
|
8
8
|
|
9
9
|
```ts title="edenx.config.ts"
|
10
|
-
import expressPlugin from '@
|
11
|
-
import bffPlugin from '@
|
10
|
+
import expressPlugin from '@modern-js/plugin-express';
|
11
|
+
import bffPlugin from '@modern-js/plugin-bff';
|
12
12
|
|
13
13
|
export default defineConfig({
|
14
14
|
plugins: [expressPlugin(), bffPlugin()],
|
@@ -19,8 +19,8 @@ export default defineConfig({
|
|
19
19
|
<TabItem value="koa" label="Koa.js">
|
20
20
|
|
21
21
|
```ts title="edenx.config.ts"
|
22
|
-
import koaPlugin from '@
|
23
|
-
import bffPlugin from '@
|
22
|
+
import koaPlugin from '@modern-js/plugin-koa';
|
23
|
+
import bffPlugin from '@modern-js/plugin-bff';
|
24
24
|
|
25
25
|
export default defineConfig({
|
26
26
|
plugins: [koaPlugin(), bffPlugin()],
|
@@ -1 +1 @@
|
|
1
|
-
根据官网 [Release Note](https://github.com/
|
1
|
+
根据官网 [Release Note](https://github.com/web-infra-dev/modern.js/releases),开发者也可以手动将项目升级到想要的版本。
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
title: enableHandleWeb
|
3
|
+
---
|
4
|
+
|
5
|
+
# bff.enableHandleWeb
|
6
|
+
|
7
|
+
- **类型:** `boolean`
|
8
|
+
- **默认值:** `false`
|
9
|
+
|
10
|
+
:::caution 注意
|
11
|
+
请先在当前应用项目根目录使用【[new](/apis/app/commands#modern-new)】 启用 BFF 功能。
|
12
|
+
:::
|
13
|
+
|
14
|
+
默认情况下,BFF 服务只能处理 BFF API 的请求。
|
15
|
+
|
16
|
+
当该值设置为 `true` 时,页面请求流量也会经过 BFF,并且 Modern.js 内置的页面渲染的逻辑默认会作为 BFF 服务的最后一个中间件运行。
|
17
|
+
|
18
|
+
```ts title="modern.config.ts"
|
19
|
+
export default defineConfig({
|
20
|
+
bff: {
|
21
|
+
enableHandleWeb: true,
|
22
|
+
},
|
23
|
+
});
|
24
|
+
```
|
@@ -35,7 +35,7 @@ export const middleware: Middleware = (ctx, next) => {
|
|
35
35
|
开启后,Middleware 类型将从其他命名空间下导出,并且可以使用框架拓展的语法:
|
36
36
|
|
37
37
|
```ts title="server/index.ts"
|
38
|
-
import { SomeType } from '@modern-js/runtime/{
|
38
|
+
import { SomeType } from '@modern-js/runtime/{namespace}';
|
39
39
|
|
40
40
|
export const middleware: SomeType = (...args) => {
|
41
41
|
console.log(args[0].url);
|
@@ -4,11 +4,13 @@ sidebar_label: ssr
|
|
4
4
|
|
5
5
|
# server.ssr
|
6
6
|
|
7
|
-
- **类型:** `boolean`
|
7
|
+
- **类型:** `boolean` | `Object`
|
8
8
|
- **默认值:** `false`
|
9
9
|
|
10
10
|
SSR 开关以及相关设置。
|
11
11
|
|
12
|
+
### Boolean 类型
|
13
|
+
|
12
14
|
当值类型为 `boolean` 时,表示是否开启 SSR 部署模式,默认 `false` 不开启。
|
13
15
|
|
14
16
|
```ts title="modern.config.ts"
|
@@ -18,3 +20,19 @@ export default defineConfig({
|
|
18
20
|
},
|
19
21
|
});
|
20
22
|
```
|
23
|
+
|
24
|
+
### Object 类型
|
25
|
+
|
26
|
+
当值类型为 `Object` 时,可以配置如下属性:
|
27
|
+
|
28
|
+
- `mode`:`string = 'string'`,默认为使用 `renderToString` 渲染。配置为 'stream' 开启流式渲染。
|
29
|
+
- `forceCSR`:`boolean = false`,默认关闭强制 CSR 渲染。配置为 `true` 后,在页面访问时添加 `?csr=true` 即可强制 CSR。
|
30
|
+
|
31
|
+
```ts title="modern.config.ts"
|
32
|
+
export default defineConfig({
|
33
|
+
server: {
|
34
|
+
forceCSR: true,
|
35
|
+
mode: 'stream',
|
36
|
+
},
|
37
|
+
});
|
38
|
+
```
|
@@ -1,11 +1,11 @@
|
|
1
1
|
---
|
2
2
|
title: 使用 Rspack
|
3
|
-
sidebar_position:
|
3
|
+
sidebar_position: 1
|
4
4
|
---
|
5
5
|
|
6
6
|
# 使用 Rspack
|
7
7
|
|
8
|
-
[Rspack](https://www.rspack.
|
8
|
+
[Rspack](https://www.rspack.dev/) 是字节跳动 Web Infra 团队自研的 Rust Bundler,核心架构对齐 webpack 实现,并对社区常用的构建能力做了开箱即用的支持。
|
9
9
|
|
10
10
|
Rspack 通过以下方式来提升编译性能:
|
11
11
|
|
@@ -13,22 +13,18 @@ Rspack 通过以下方式来提升编译性能:
|
|
13
13
|
- 充分利用多核优势,整个编译过程充分进行多线程优化。
|
14
14
|
- 基于请求的按需编译(Lazy Compilation),减小每次编译的模块数目,以提升冷启动的速度。
|
15
15
|
|
16
|
-
## 初始化
|
16
|
+
## 初始化 Rspack 项目
|
17
17
|
|
18
|
-
Modern.js
|
18
|
+
Modern.js 生成器会提供一个可交互的问答界面,只需将构建工具选择为 **Rspack**,即可创建一个 Rspack 项目:
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
? 请选择开发语言 TS
|
24
|
-
? 请选择包管理工具 pnpm
|
25
|
-
? 请选择构建工具 rspack
|
26
|
-
```
|
20
|
+
import InitRspackApp from "@site-docs/components/init-rspack-app"
|
21
|
+
|
22
|
+
<InitRspackApp />
|
27
23
|
|
28
24
|
项目创建完成后,在项目中执行 `pnpm run dev` 即可体验项目。更多项目信息可参考[快速上手](/guides/get-started/quick-start.html)。
|
29
25
|
|
30
26
|
:::tip
|
31
|
-
使用
|
27
|
+
使用 Rspack 作为打包工具时,以下功能暂不支持使用:
|
32
28
|
|
33
29
|
- 服务端渲染(SSR)
|
34
30
|
- 静态站点生成(SSG)
|
@@ -37,17 +33,17 @@ $ npx @modern-js/create myapp
|
|
37
33
|
|
38
34
|
:::
|
39
35
|
|
40
|
-
## 从 webpack 迁移至
|
36
|
+
## 从 webpack 迁移至 Rspack
|
41
37
|
|
42
|
-
通过执行 `pnpm run new` 可启用
|
38
|
+
通过执行 `pnpm run new` 可启用 Rspack 构建:
|
43
39
|
|
44
40
|
```bash
|
45
41
|
$ pnpm run new
|
46
42
|
? 请选择你想要的操作 启用可选功能
|
47
|
-
? 启用可选功能 启用「
|
43
|
+
? 启用可选功能 启用「Rspack 构建」
|
48
44
|
```
|
49
45
|
|
50
|
-
执行命令后,在 modern.config.ts 中开启
|
46
|
+
执行命令后,在 modern.config.ts 中开启 Rspack 构建:
|
51
47
|
|
52
48
|
```ts title=modern.config.ts
|
53
49
|
import appTools, { defineConfig } from '@modern-js/app-tools';
|
@@ -65,5 +61,5 @@ export default defineConfig<'rspack'>({
|
|
65
61
|
```
|
66
62
|
|
67
63
|
:::tip
|
68
|
-
从 webpack 迁移至
|
64
|
+
从 webpack 迁移至 Rspack,会有一些构建和配置能力上的差异,详情可参考:[配置差异](https://modernjs.dev/builder/guide/advanced/rspack-start.html#从-webpack-迁移到-rspack)
|
69
65
|
:::
|
@@ -43,10 +43,13 @@ Modern.js 打破传统的 SSR 开发模式,提供了用户无感的 SSR 开发
|
|
43
43
|
不过,开发者仍然需要关注数据的兜底处理,例如 `null` 值或不符合预期的数据返回。避免在 SSR 时产生 React 渲染错误或是返回凌乱的渲染结果。
|
44
44
|
|
45
45
|
:::info 补充信息
|
46
|
-
|
46
|
+
1. 当以客户端路由的方式请求页面时,Modern.js 会发送一个 HTTP 请求,服务端接收到请求后执行页面对应的 Data Loader 函数,然后将执行结果作为请求的响应返回浏览器。
|
47
47
|
|
48
|
+
2. 使用 Data Loader 时,数据获取发生在渲染前,Modern.js 也仍然支持在组件渲染时获取数据。更多相关内容可以查看[数据获取](/guides/basic-features/data-fetch)。
|
48
49
|
:::
|
49
50
|
|
51
|
+
|
52
|
+
|
50
53
|
## 保持渲染一致
|
51
54
|
|
52
55
|
有些业务中,通常需要根据当前的运行容器环境特征做不同的 UI 展示,例如 [UA](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) 信息。如果处理不够仔细,此时很有可能出现不符合预期的渲染结果。
|
@@ -277,7 +280,7 @@ export const loader = () => {
|
|
277
280
|
|
278
281
|
## 流式渲染
|
279
282
|
|
280
|
-
Modern.js 支持了 React 18
|
283
|
+
Modern.js 支持了 React 18 的流式渲染,可以通过如下配置启用:
|
281
284
|
|
282
285
|
```json
|
283
286
|
{
|
@@ -289,7 +292,210 @@ Modern.js 支持了 React 18 的流式渲染,可以通过如下配置修改默
|
|
289
292
|
}
|
290
293
|
```
|
291
294
|
|
292
|
-
|
293
|
-
|
295
|
+
Modern.js 的流式渲染基于 React Router 实现,主要涉及 API 有:
|
296
|
+
|
297
|
+
- [`defer`](https://reactrouter.com/en/main/utils/defer):在 Data Loader 中使用,用于支持异步获取数据。
|
298
|
+
- [`Await`](https://reactrouter.com/en/main/components/await):用于渲染 Data Loader 返回的异步数据。
|
299
|
+
- [`useAsyncValue`](https://reactrouter.com/en/main/hooks/use-async-value):用于从最近的父级 `Await` 组件中获取数据。
|
300
|
+
|
301
|
+
|
302
|
+
### 异步获取数据
|
303
|
+
|
304
|
+
```ts title='page.loader.ts'
|
305
|
+
import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
|
306
|
+
|
307
|
+
interface User {
|
308
|
+
name: string;
|
309
|
+
age: number;
|
310
|
+
}
|
311
|
+
|
312
|
+
export interface Data {
|
313
|
+
data: User;
|
314
|
+
}
|
315
|
+
|
316
|
+
export default ({ params }: LoaderFunctionArgs) => {
|
317
|
+
const userId = params.id;
|
318
|
+
|
319
|
+
const user = new Promise<User>(resolve => {
|
320
|
+
setTimeout(() => {
|
321
|
+
resolve({
|
322
|
+
name: `user-${userId}`,
|
323
|
+
age: 18,
|
324
|
+
});
|
325
|
+
}, 200);
|
326
|
+
});
|
327
|
+
|
328
|
+
return defer({ data: user });
|
329
|
+
};
|
330
|
+
|
331
|
+
```
|
332
|
+
|
333
|
+
`user` 是一个 Promise 类型的对象,表示需要异步获取的数据,通过 `defer` 处理需要异步获取的 `user`。注意,`defer` 必须接收一个对象类型的参数,
|
334
|
+
因此, 传入 `defer` 的参数为 `{data: user}`。
|
335
|
+
|
336
|
+
`defer` 还可以同时接收异步数据和同步数据。例如:
|
337
|
+
|
338
|
+
```ts title='page.loader.ts'
|
339
|
+
|
340
|
+
// 省略部分代码
|
341
|
+
|
342
|
+
export default ({ params }: LoaderFunctionArgs) => {
|
343
|
+
const userId = params.id;
|
344
|
+
|
345
|
+
const user = new Promise<User>(resolve => {
|
346
|
+
setTimeout(() => {
|
347
|
+
resolve({
|
348
|
+
name: `user-${userId}`,
|
349
|
+
age: 18,
|
350
|
+
});
|
351
|
+
}, 200);
|
352
|
+
});
|
353
|
+
|
354
|
+
const otherData = new Promise<string>(resolve => {
|
355
|
+
setTimeout(() => {
|
356
|
+
resolve('some sync data');
|
357
|
+
}, 200);
|
358
|
+
});
|
359
|
+
|
360
|
+
return defer({
|
361
|
+
data: user,
|
362
|
+
other: await otherData
|
363
|
+
});
|
364
|
+
};
|
365
|
+
|
366
|
+
```
|
367
|
+
|
368
|
+
`otherData` 前加了 `await`,所以是同步获取的数据,它可以和异步获取的数据 `user` 同时传入 `defer`。
|
369
|
+
|
370
|
+
|
371
|
+
### 渲染异步数据
|
372
|
+
|
373
|
+
通过 `Await` 组件,可以获取到 Data Loader 中异步返回的数据,然后进行渲染。例如:
|
374
|
+
|
375
|
+
```ts title='page.tsx'
|
376
|
+
import { Await, useLoaderData } from '@modern-js/runtime/router';
|
377
|
+
import { Suspense } from 'react';
|
378
|
+
import type { Data } from './page.loader';
|
379
|
+
|
380
|
+
const Page = () => {
|
381
|
+
const data = useLoaderData() as Data;
|
382
|
+
|
383
|
+
return (
|
384
|
+
<div>
|
385
|
+
User info:
|
386
|
+
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
387
|
+
<Await resolve={data.data}>
|
388
|
+
{(user) => {
|
389
|
+
return (
|
390
|
+
<div id="data">
|
391
|
+
name: {user.name}, age: {user.age}
|
392
|
+
</div>
|
393
|
+
);
|
394
|
+
}}
|
395
|
+
</Await>
|
396
|
+
</Suspense>
|
397
|
+
</div>
|
398
|
+
);
|
399
|
+
};
|
294
400
|
|
401
|
+
export default Page;
|
402
|
+
```
|
403
|
+
|
404
|
+
`Await` 需要包裹在 `Suspense` 组件内部,`Await` 的 `resolve` 传入的是 Data Loader 异步获取的数据,当数据获取完成后,
|
405
|
+
通过 [Render Props](https://reactjs.org/docs/render-props.html) 模式,渲染获取到的数据。在数据的获取阶段,将展示
|
406
|
+
`Suspense` 组件 `fallback` 属性设置的内容。
|
407
|
+
|
408
|
+
:::warning 注意
|
409
|
+
从 Data Loader 文件导入类型时,需要使用 import type 语法,保证只导入类型信息,这样可以避免 Data Loader 的代码打包到前端产物的 bundle 文件中。
|
410
|
+
|
411
|
+
所以,这里的导入方式为:`import type { Data } from './page.loader'`;
|
412
|
+
:::
|
413
|
+
|
414
|
+
也可以通过 `useAsyncValue` 获取 Data Loader 返回的异步数据。例如:
|
415
|
+
|
416
|
+
```
|
417
|
+
```ts title='page.tsx'
|
418
|
+
import { useAsyncValue } from '@modern-js/runtime/router';
|
419
|
+
|
420
|
+
// 省略部分代码
|
421
|
+
|
422
|
+
const UserInfo = () => {
|
423
|
+
const user = useAsyncValue();
|
424
|
+
|
425
|
+
return (
|
426
|
+
<div>
|
427
|
+
name: {user.name}, age: {user.age}
|
428
|
+
</div>
|
429
|
+
)
|
430
|
+
}
|
431
|
+
|
432
|
+
const Page = () => {
|
433
|
+
const data = useLoaderData() as Data;
|
434
|
+
|
435
|
+
return (
|
436
|
+
<div>
|
437
|
+
User info:
|
438
|
+
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
439
|
+
<Await resolve={data.data}>
|
440
|
+
<UserInfo />
|
441
|
+
</Await>
|
442
|
+
</Suspense>
|
443
|
+
</div>
|
444
|
+
);
|
445
|
+
};
|
446
|
+
|
447
|
+
export default Page;
|
448
|
+
```
|
449
|
+
|
450
|
+
### 错误处理
|
451
|
+
|
452
|
+
`Await` 组件的 `errorElement` 属性,可以用来处理当 Data Loader 执行时,或者子组件渲染时抛出的错误。
|
453
|
+
例如,我们故意在 Data Loader 函数中抛出错误:
|
454
|
+
|
455
|
+
```ts title='page.loader.ts'
|
456
|
+
import { defer } from '@modern-js/runtime/router';
|
457
|
+
|
458
|
+
export default () => {
|
459
|
+
const data = new Promise((resolve, reject) => {
|
460
|
+
setTimeout(() => {
|
461
|
+
reject(new Error('error occurs'));
|
462
|
+
}, 200);
|
463
|
+
});
|
464
|
+
|
465
|
+
return defer({ data });
|
466
|
+
};
|
467
|
+
```
|
468
|
+
|
469
|
+
然后通过 `useAsyncError` 获取错误,并将用于渲染错误信息的组件赋值给 `Await` 组件的 `errorElement` 属性:
|
470
|
+
|
471
|
+
```ts title='page.ts'
|
472
|
+
import { Await, useAsyncError, useLoaderData } from '@modern-js/runtime/router';
|
473
|
+
import { Suspense } from 'react';
|
474
|
+
|
475
|
+
export default function Page() {
|
476
|
+
const data = useLoaderData();
|
477
|
+
|
478
|
+
return (
|
479
|
+
<div>
|
480
|
+
Error page
|
481
|
+
<Suspense fallback={<div>loading ...</div>}>
|
482
|
+
<Await resolve={data.data} errorElement={<ErrorElement />}>
|
483
|
+
{(data: any) => {
|
484
|
+
return <div>never displayed</div>;
|
485
|
+
}}
|
486
|
+
</Await>
|
487
|
+
</Suspense>
|
488
|
+
</div>
|
489
|
+
);
|
490
|
+
}
|
491
|
+
|
492
|
+
function ErrorElement() {
|
493
|
+
const error = useAsyncError() as Error;
|
494
|
+
return <p>Something went wrong! {error.message}</p>;
|
495
|
+
}
|
496
|
+
```
|
497
|
+
|
498
|
+
:::info 补充信息
|
499
|
+
1. [Deferred Data](https://reactrouter.com/en/main/guides/deferred)
|
500
|
+
2. [New Suspense SSR Architecture in React 18](https://github.com/reactwg/react-18/discussions/37)
|
295
501
|
:::
|