@modern-js/main-doc 2.12.0 → 2.13.0
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.
- package/CHANGELOG.md +15 -0
- package/docs/en/apis/app/commands.mdx +1 -1
- package/docs/en/apis/app/hooks/api/framework/lambda.mdx +2 -2
- package/docs/en/apis/app/hooks/api/functions/api.mdx +3 -3
- package/docs/en/apis/app/hooks/api/functions/app.mdx +1 -1
- package/docs/en/apis/app/hooks/api/test.mdx +2 -1
- package/docs/en/apis/app/hooks/config/mock.mdx +1 -1
- package/docs/en/apis/app/hooks/config/upload.mdx +1 -1
- package/docs/en/apis/app/hooks/server/test.mdx +1 -1
- package/docs/en/apis/app/hooks/src/app.mdx +1 -1
- package/docs/en/apis/app/hooks/src/pages.mdx +1 -1
- package/docs/en/apis/app/runtime/app/define-config.mdx +1 -1
- package/docs/en/apis/app/runtime/core/bootstrap.mdx +2 -2
- package/docs/en/apis/app/runtime/core/use-loader.mdx +6 -2
- package/docs/en/apis/app/runtime/model/auto-actions.mdx +6 -6
- package/docs/en/apis/app/runtime/model/connect.mdx +2 -1
- package/docs/en/apis/app/runtime/model/create-app.mdx +1 -1
- package/docs/en/apis/app/runtime/model/handle-effect.mdx +3 -3
- package/docs/en/apis/app/runtime/model/model_.mdx +2 -2
- package/docs/en/apis/app/runtime/model/use-model.mdx +2 -2
- package/docs/en/apis/app/runtime/model/use-static-model.mdx +2 -2
- package/docs/en/apis/app/runtime/model/use-store.mdx +1 -1
- package/docs/en/apis/app/runtime/router/router.mdx +1 -0
- package/docs/en/apis/app/runtime/ssr/pre-render.mdx +2 -2
- package/docs/en/apis/app/runtime/testing/render.mdx +1 -1
- package/docs/en/components/enable-bff.mdx +2 -2
- package/docs/en/configure/app/html/script-loading.mdx +13 -0
- package/docs/en/configure/app/output/ssg.mdx +1 -1
- package/docs/en/configure/app/plugins.mdx +3 -3
- package/docs/en/configure/app/source/disable-entry-dirs.mdx +1 -1
- package/docs/en/configure/app/tools/swc.mdx +1 -1
- package/docs/en/configure/app/usage.mdx +65 -23
- package/docs/en/guides/advanced-features/bff/function.mdx +3 -3
- package/docs/en/guides/advanced-features/code-split.mdx +2 -2
- package/docs/en/guides/advanced-features/eslint.mdx +1 -1
- package/docs/en/guides/advanced-features/ssg.mdx +2 -2
- package/docs/en/guides/advanced-features/ssr.mdx +45 -8
- package/docs/en/guides/basic-features/alias.mdx +1 -1
- package/docs/en/guides/basic-features/env-vars.mdx +5 -5
- package/docs/en/guides/basic-features/html.mdx +0 -4
- package/docs/en/guides/basic-features/routes.mdx +48 -28
- package/docs/en/guides/concept/entries.mdx +3 -3
- package/docs/en/guides/get-started/introduction.mdx +21 -1
- package/docs/en/guides/get-started/quick-start.mdx +6 -23
- package/docs/en/guides/get-started/upgrade.mdx +7 -7
- package/docs/en/guides/topic-detail/framework-plugin/hook.mdx +1 -1
- package/docs/en/guides/topic-detail/generator/codesmith/api/json.mdx +3 -3
- package/docs/en/guides/topic-detail/generator/config/app.mdx +1 -1
- package/docs/en/guides/topic-detail/generator/config/monorepo.mdx +1 -1
- package/docs/en/guides/topic-detail/generator/plugin/api/file/addHelper.mdx +1 -1
- package/docs/en/guides/topic-detail/model/manage-effects.mdx +2 -1
- package/docs/en/guides/topic-detail/model/model-communicate.mdx +1 -1
- package/docs/en/tutorials/foundations/introduction.mdx +0 -2
- package/docs/zh/apis/app/hooks/src/pages.mdx +1 -1
- package/docs/zh/apis/app/runtime/core/use-loader.mdx +4 -0
- package/docs/zh/apis/app/runtime/model/handle-effect.mdx +2 -1
- package/docs/zh/apis/app/runtime/model/model_.mdx +4 -2
- package/docs/zh/blog/updates/2022-0708-updates.md +1 -2
- package/docs/zh/components/enable-bff.mdx +2 -2
- package/docs/zh/configure/app/html/script-loading.mdx +13 -0
- package/docs/zh/configure/app/source/disable-entry-dirs.mdx +2 -2
- package/docs/zh/configure/app/usage.mdx +66 -24
- package/docs/zh/guides/advanced-features/ssr.mdx +74 -36
- package/docs/zh/guides/basic-features/html.mdx +0 -4
- package/docs/zh/guides/basic-features/routes.mdx +64 -24
- package/docs/zh/guides/concept/entries.mdx +4 -4
- package/docs/zh/guides/get-started/introduction.mdx +21 -1
- package/docs/zh/guides/get-started/upgrade.mdx +3 -3
- package/docs/zh/guides/topic-detail/changesets/release.mdx +2 -2
- package/package.json +5 -5
- package/src/components/SolutionCards/index.module.scss +62 -0
- package/src/components/SolutionCards/index.tsx +26 -0
@@ -2,9 +2,10 @@
|
|
2
2
|
sidebar_position: 1
|
3
3
|
title: model
|
4
4
|
---
|
5
|
+
|
5
6
|
# model
|
6
7
|
|
7
|
-
import ReduckTip from
|
8
|
+
import ReduckTip from '@site-docs/components/reduck-tip';
|
8
9
|
|
9
10
|
<ReduckTip />
|
10
11
|
|
@@ -61,7 +62,8 @@ const fooModel = model('foo').define({
|
|
61
62
|
- context: Context,Reduck 上下文对象,可以获取底层的 `store` 对象。`store` 除支持 Redux Store 的所有 [API](https://redux.js.org/api/store) 以外,还挂载了用于消费 Model 的 `use` 的方法,和用于卸载 Model 的 `unmount` 方法。
|
62
63
|
- utils: Utils,定义 Model 时,常用的工具函数:`use`、`onMount`。`use` 作用同 `store` 对象上的 `use`,`onMount` 是 Model 挂载后的钩子函数。
|
63
64
|
|
64
|
-
{/* TODO: @anchao 调整类型
|
65
|
+
{/* TODO: @anchao 调整类型 */}
|
66
|
+
|
65
67
|
```ts
|
66
68
|
interface Utils {
|
67
69
|
use: UseModel;
|
@@ -39,8 +39,7 @@ npx @modern-js/upgrade
|
|
39
39
|
|
40
40
|
新的模块工程项目,不仅支持对产物做 bundless 构建,也支持 bundle 构建。通过配置 `buildConfig` 下的 [`buildType`](https://modernjs.dev/v1/docs/apis/module/config/output/build-config/build-type) ,即可进行 bundle 构建:
|
41
41
|
|
42
|
-
```ts
|
43
|
-
// modern.config.ts
|
42
|
+
```ts title="modern.config.ts"
|
44
43
|
import { defineConfig } from '@modern-js/module-tools';
|
45
44
|
|
46
45
|
export default defineConfig({
|
@@ -6,7 +6,7 @@ import { Tabs, Tab as TabItem } from "@theme";
|
|
6
6
|
<Tabs>
|
7
7
|
<TabItem value="express" label="Express.js" default>
|
8
8
|
|
9
|
-
```ts title="
|
9
|
+
```ts title="modern.config.ts"
|
10
10
|
import expressPlugin from '@modern-js/plugin-express';
|
11
11
|
import bffPlugin from '@modern-js/plugin-bff';
|
12
12
|
|
@@ -18,7 +18,7 @@ export default defineConfig({
|
|
18
18
|
</TabItem>
|
19
19
|
<TabItem value="koa" label="Koa.js">
|
20
20
|
|
21
|
-
```ts title="
|
21
|
+
```ts title="modern.config.ts"
|
22
22
|
import koaPlugin from '@modern-js/plugin-koa';
|
23
23
|
import bffPlugin from '@modern-js/plugin-bff';
|
24
24
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
---
|
2
|
+
sidebar_label: scriptLoading
|
3
|
+
---
|
4
|
+
|
5
|
+
# html.scriptLoading
|
6
|
+
|
7
|
+
:::tip
|
8
|
+
该配置由 Modern.js Builder 提供,更多信息可参考 [html.scriptLoading](https://modernjs.dev/builder/api/config-html.html#htmlscriptloading)。
|
9
|
+
:::
|
10
|
+
|
11
|
+
import Main from '@modern-js/builder-doc/docs/zh/config/html/scriptLoading.md';
|
12
|
+
|
13
|
+
<Main />
|
@@ -7,14 +7,14 @@ sidebar_label: disableEntryDirs
|
|
7
7
|
- **类型:** `string[]`
|
8
8
|
- **默认值:** `[]`
|
9
9
|
|
10
|
-
默认会根据 `src`
|
10
|
+
Modern.js 默认会根据 `src` 目录识别应用入口,你可以通过该选项禁止某些目录被识别为应用入口。
|
11
11
|
|
12
12
|
例如,当配置与目录结构如下时:
|
13
13
|
|
14
14
|
```ts title="modern.config.ts"
|
15
15
|
export default defineConfig({
|
16
16
|
source: {
|
17
|
-
disableEntryDirs: './src/one',
|
17
|
+
disableEntryDirs: ['./src/one'],
|
18
18
|
},
|
19
19
|
});
|
20
20
|
```
|
@@ -19,17 +19,33 @@ Modern.js 不支持同时在 package.json 中和 modern.config.ts 中配置同
|
|
19
19
|
|
20
20
|
## 在配置文件中配置
|
21
21
|
|
22
|
-
Modern.js 的配置文件定义在项目的根目录下,支持 `.
|
22
|
+
Modern.js 的配置文件定义在项目的根目录下,支持 `.ts`, `.js` 和 `.mjs` 格式:
|
23
23
|
|
24
|
-
- `modern.config.js`
|
25
24
|
- `modern.config.ts`
|
25
|
+
- `modern.config.js`
|
26
26
|
- `modern.config.mjs`
|
27
27
|
|
28
|
-
### modern.config.
|
28
|
+
### modern.config.ts(推荐)
|
29
|
+
|
30
|
+
我们推荐使用 .ts 格式的配置文件,它提供了友好的 TypeScript 类型提示,从而帮助你避免配置中的错误。
|
31
|
+
|
32
|
+
从 `@modern-js/app-tools` 中导入 `defineConfig` 工具函数, 它会帮助你进行配置的类型推导和类型补全:
|
33
|
+
|
34
|
+
```ts title="modern.config.ts"
|
35
|
+
import { defineConfig } from '@modern-js/app-tools';
|
36
|
+
|
37
|
+
export default defineConfig({
|
38
|
+
source: {
|
39
|
+
alias: {
|
40
|
+
'@common': './src/common',
|
41
|
+
},
|
42
|
+
},
|
43
|
+
});
|
44
|
+
```
|
29
45
|
|
30
|
-
|
46
|
+
### modern.config.js
|
31
47
|
|
32
|
-
|
48
|
+
如果你在开发一个非 TypeScript 项目,可以使用 .js 格式的配置文件:
|
33
49
|
|
34
50
|
```js title="modern.config.js"
|
35
51
|
export default {
|
@@ -51,24 +67,6 @@ export default {
|
|
51
67
|
};
|
52
68
|
```
|
53
69
|
|
54
|
-
### modern.config.ts(推荐)
|
55
|
-
|
56
|
-
我们推荐使用 .ts 格式的配置文件,它提供了友好的 TypeScript 类型提示,从而帮助你避免配置中的错误。
|
57
|
-
|
58
|
-
从 `@modern-js/app-tools` 中导入 `defineConfig` 工具函数, 它会帮助你进行配置的类型推导和类型补全:
|
59
|
-
|
60
|
-
```ts title="modern.config.ts"
|
61
|
-
import { defineConfig } from '@modern-js/app-tools';
|
62
|
-
|
63
|
-
export default defineConfig({
|
64
|
-
source: {
|
65
|
-
alias: {
|
66
|
-
'@common': './src/common',
|
67
|
-
},
|
68
|
-
},
|
69
|
-
});
|
70
|
-
```
|
71
|
-
|
72
70
|
### 导出配置函数
|
73
71
|
|
74
72
|
Modern.js 支持在配置文件中导出一个函数,你可以在函数中动态计算配置,并返回给 Modern.js。
|
@@ -134,7 +132,7 @@ $ modern build -c modern.prod.config.js
|
|
134
132
|
|
135
133
|
## 在 package.json 中配置(不推荐)
|
136
134
|
|
137
|
-
|
135
|
+
除了配置文件外,你也可以在 `package.json` 中的 `modernConfig` 字段下设置配置项,如:
|
138
136
|
|
139
137
|
```json title="package.json"
|
140
138
|
{
|
@@ -200,3 +198,47 @@ modern.config.local.ts
|
|
200
198
|
modern.config.local.js
|
201
199
|
modern.config.local.mjs
|
202
200
|
```
|
201
|
+
|
202
|
+
## 合并多份配置
|
203
|
+
|
204
|
+
在某些情况下,你可能需要将多份配置合并为一份配置,此时你可以使用 `mergeConfig` 工具函数来合并多个配置。
|
205
|
+
|
206
|
+
`mergeConfig` 函数接受一个数组作为参数,数组中的每一项都是一个配置对象,`mergeConfig` 会将数组中的每一项配置对象进行深层合并,自动将多个函数项合并为数组,最终返回一个合并后的配置对象。
|
207
|
+
|
208
|
+
### 示例
|
209
|
+
|
210
|
+
```ts title="modern.config.ts"
|
211
|
+
import { mergeConfig } from '@modern-js/app-tools';
|
212
|
+
|
213
|
+
const config1 = {
|
214
|
+
dev: {
|
215
|
+
port: 3000,
|
216
|
+
},
|
217
|
+
tools: {
|
218
|
+
postcss: () => console.log('config1');
|
219
|
+
},
|
220
|
+
};
|
221
|
+
const config2 = {
|
222
|
+
dev: {
|
223
|
+
port: 3001,
|
224
|
+
},
|
225
|
+
tools: {
|
226
|
+
postcss: () => console.log('config2');
|
227
|
+
},
|
228
|
+
};
|
229
|
+
|
230
|
+
const mergedConfig = mergeConfig([config1, config2]);
|
231
|
+
```
|
232
|
+
|
233
|
+
在以上示例中,合并后的配置对象为:
|
234
|
+
|
235
|
+
```ts
|
236
|
+
const mergedConfig = {
|
237
|
+
dev: {
|
238
|
+
port: 3001,
|
239
|
+
},
|
240
|
+
tools: {
|
241
|
+
postcss: [() => console.log('config1'), () => console.log('config2')],
|
242
|
+
},
|
243
|
+
};
|
244
|
+
```
|
@@ -2,6 +2,7 @@
|
|
2
2
|
title: 服务端渲染(SSR)
|
3
3
|
sidebar_position: 3
|
4
4
|
---
|
5
|
+
|
5
6
|
# 服务端渲染(SSR)
|
6
7
|
|
7
8
|
在 Modern.js 中,SSR 也是开箱即用的。开发者无需为 SSR 编写复杂的服务端逻辑,也无需关心 SSR 的运维,或是创建单独的服务。Modern.js 拥有完备的 SSR 降级策略,保证页面能够安全运行。
|
@@ -12,10 +13,10 @@ sidebar_position: 3
|
|
12
13
|
import { defineConfig } from '@modern-js/app-tools';
|
13
14
|
|
14
15
|
export default defineConfig({
|
15
|
-
|
16
|
-
|
16
|
+
server: {
|
17
|
+
ssr: true,
|
17
18
|
},
|
18
|
-
})
|
19
|
+
});
|
19
20
|
```
|
20
21
|
|
21
22
|
## SSR 时的数据获取
|
@@ -33,7 +34,7 @@ export default () => {
|
|
33
34
|
在组件中可以通过 Hooks API 的方式获取 `loader` 函数返回的数据:
|
34
35
|
|
35
36
|
```tsx
|
36
|
-
import { useLoaderData } from '@modern-js/runtime/router'
|
37
|
+
import { useLoaderData } from '@modern-js/runtime/router';
|
37
38
|
export default () => {
|
38
39
|
const data = useLoaderData();
|
39
40
|
return <div>{data.message}</div>;
|
@@ -45,14 +46,13 @@ Modern.js 打破传统的 SSR 开发模式,提供了用户无感的 SSR 开发
|
|
45
46
|
不过,开发者仍然需要关注数据的兜底处理,例如 `null` 值或不符合预期的数据返回。避免在 SSR 时产生 React 渲染错误或是返回凌乱的渲染结果。
|
46
47
|
|
47
48
|
:::info 补充信息
|
49
|
+
|
48
50
|
1. 当以客户端路由的方式请求页面时,Modern.js 会发送一个 HTTP 请求,服务端接收到请求后执行页面对应的 Data Loader 函数,然后将执行结果作为请求的响应返回浏览器。
|
49
51
|
|
50
52
|
2. 使用 Data Loader 时,数据获取发生在渲染前,Modern.js 也仍然支持在组件渲染时获取数据。更多相关内容可以查看[数据获取](/guides/basic-features/data-fetch)。
|
51
53
|
|
52
54
|
:::
|
53
55
|
|
54
|
-
|
55
|
-
|
56
56
|
## 保持渲染一致
|
57
57
|
|
58
58
|
有些业务中,通常需要根据当前的运行容器环境特征做不同的 UI 展示,例如 [UA](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) 信息。如果处理不够仔细,此时很有可能出现不符合预期的渲染结果。
|
@@ -215,16 +215,23 @@ const App = () => {
|
|
215
215
|
export default App;
|
216
216
|
```
|
217
217
|
|
218
|
-
在组件文件中引入 Node API,通常情况下是因为使用了
|
218
|
+
在组件文件中引入 Node API,通常情况下是因为使用了 `useLoader`,例如:
|
219
219
|
|
220
220
|
```ts
|
221
221
|
import fse from 'fs-extra';
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
222
|
+
import { useLoader } from '@modern-js/runtime'
|
223
|
+
|
224
|
+
const App = () => {
|
225
|
+
const { data } = useLoader(async () => {
|
226
|
+
const file = fse.readFileSync('./myfile');
|
227
|
+
return {
|
228
|
+
...
|
229
|
+
};
|
230
|
+
})
|
231
|
+
|
232
|
+
return <div>Hello World</div>;
|
227
233
|
};
|
234
|
+
export default App;
|
228
235
|
```
|
229
236
|
|
230
237
|
### 环境变量区分
|
@@ -239,16 +246,32 @@ if (process.env.MODERN_TARGET === 'browser') {
|
|
239
246
|
}
|
240
247
|
```
|
241
248
|
|
249
|
+
开发环境打包后,SSR 产物和 CSR 产物会被编译成以下内容。因此 SSR 环境中不会再因为 Web API 报错:
|
250
|
+
|
251
|
+
```ts
|
252
|
+
// SSR 产物
|
253
|
+
if (false) {
|
254
|
+
}
|
255
|
+
|
256
|
+
// CSR 产物
|
257
|
+
if (true) {
|
258
|
+
document.addEventListener('load', () => {
|
259
|
+
console.log('document load');
|
260
|
+
});
|
261
|
+
}
|
262
|
+
```
|
263
|
+
|
242
264
|
:::note
|
243
265
|
更多内容可以查看[环境变量](/guides/basic-features/env-vars)。
|
244
|
-
|
245
266
|
:::
|
246
267
|
|
247
268
|
### 文件后缀区分
|
248
269
|
|
249
|
-
|
270
|
+
但例如第二种情况,在代码中引入了 `fs-extra`,它内部有使用了 Node API 的副作用,如果直接引用到组件中,会造成 CSR 加载报错。
|
250
271
|
|
251
|
-
|
272
|
+
环境变量的方式并不能在这种情况下生效,Modern.js 也支持通过 `.node.` 后缀的文件来区分 SSR Bundle 和 CSR Bundle 产物的打包文件。
|
273
|
+
|
274
|
+
可以创建同名的 `.ts` 和 `.node.ts` 文件做一层代理:
|
252
275
|
|
253
276
|
```ts title="compat.ts"
|
254
277
|
export const readFileSync: any = () => {};
|
@@ -273,7 +296,27 @@ export const loader = () => {
|
|
273
296
|
|
274
297
|
### 独立文件
|
275
298
|
|
276
|
-
|
299
|
+
上述两种方式,都会为开发者带来一些心智负担。在真实的业务中,我们发现大多数的 Node / Web 代码混用都出现在数据请求中。
|
300
|
+
|
301
|
+
因此,Modern.js 基于[嵌套路由](/guides/basic-features/routes)开发设计了[更简单的方案](/guides/basic-features/data-fetch)来分离 CSR 和 SSR 的代码。
|
302
|
+
|
303
|
+
我们可以通过独立文件来分离**数据请求**与**组件代码**。在 `routes/page.tsx` 中编写组件逻辑,在 `routes/page.loader.ts` 中编写数据请求逻辑。
|
304
|
+
|
305
|
+
```ts title="routes/page.tsx"
|
306
|
+
export default Page = () => {
|
307
|
+
return <div>Hello World<div>
|
308
|
+
}
|
309
|
+
```
|
310
|
+
|
311
|
+
```ts title="routes/page.loader.tsx"
|
312
|
+
import fse from 'fs-extra';
|
313
|
+
export default () => {
|
314
|
+
const file = fse.readFileSync('./myfile');
|
315
|
+
return {
|
316
|
+
...
|
317
|
+
};
|
318
|
+
}
|
319
|
+
```
|
277
320
|
|
278
321
|
## 接口请求
|
279
322
|
|
@@ -291,12 +334,12 @@ Modern.js 支持了 React 18 的流式渲染,可以通过如下配置启用:
|
|
291
334
|
import { defineConfig } from '@modern-js/app-tools';
|
292
335
|
|
293
336
|
export default defineConfig({
|
294
|
-
|
295
|
-
|
296
|
-
|
337
|
+
server: {
|
338
|
+
ssr: {
|
339
|
+
mode: 'stream',
|
297
340
|
},
|
298
341
|
},
|
299
|
-
})
|
342
|
+
});
|
300
343
|
```
|
301
344
|
|
302
345
|
Modern.js 的流式渲染基于 React Router 实现,主要涉及 API 有:
|
@@ -305,10 +348,9 @@ Modern.js 的流式渲染基于 React Router 实现,主要涉及 API 有:
|
|
305
348
|
- [`Await`](https://reactrouter.com/en/main/components/await):用于渲染 Data Loader 返回的异步数据。
|
306
349
|
- [`useAsyncValue`](https://reactrouter.com/en/main/hooks/use-async-value):用于从最近的父级 `Await` 组件中获取数据。
|
307
350
|
|
308
|
-
|
309
351
|
### 异步获取数据
|
310
352
|
|
311
|
-
```ts title=
|
353
|
+
```ts title="page.loader.ts"
|
312
354
|
import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
|
313
355
|
|
314
356
|
interface User {
|
@@ -334,16 +376,14 @@ export default ({ params }: LoaderFunctionArgs) => {
|
|
334
376
|
|
335
377
|
return defer({ data: user });
|
336
378
|
};
|
337
|
-
|
338
379
|
```
|
339
380
|
|
340
381
|
`user` 是一个 Promise 类型的对象,表示需要异步获取的数据,通过 `defer` 处理需要异步获取的 `user`。注意,`defer` 必须接收一个对象类型的参数,
|
341
|
-
因此, 传入 `defer`
|
382
|
+
因此, 传入 `defer` 的参数为:`{ data: user }`
|
342
383
|
|
343
384
|
`defer` 还可以同时接收异步数据和同步数据。例如:
|
344
385
|
|
345
|
-
```ts title=
|
346
|
-
|
386
|
+
```ts title="page.loader.ts"
|
347
387
|
// 省略部分代码
|
348
388
|
|
349
389
|
export default ({ params }: LoaderFunctionArgs) => {
|
@@ -366,20 +406,18 @@ export default ({ params }: LoaderFunctionArgs) => {
|
|
366
406
|
|
367
407
|
return defer({
|
368
408
|
data: user,
|
369
|
-
other: await otherData
|
409
|
+
other: await otherData,
|
370
410
|
});
|
371
411
|
};
|
372
|
-
|
373
412
|
```
|
374
413
|
|
375
414
|
`otherData` 前加了 `await`,所以是同步获取的数据,它可以和异步获取的数据 `user` 同时传入 `defer`。
|
376
415
|
|
377
|
-
|
378
416
|
### 渲染异步数据
|
379
417
|
|
380
418
|
通过 `Await` 组件,可以获取到 Data Loader 中异步返回的数据,然后进行渲染。例如:
|
381
419
|
|
382
|
-
```tsx title=
|
420
|
+
```tsx title="page.tsx"
|
383
421
|
import { Await, useLoaderData } from '@modern-js/runtime/router';
|
384
422
|
import { Suspense } from 'react';
|
385
423
|
import type { Data } from './page.loader';
|
@@ -392,7 +430,7 @@ const Page = () => {
|
|
392
430
|
User info:
|
393
431
|
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
394
432
|
<Await resolve={data.data}>
|
395
|
-
{
|
433
|
+
{user => {
|
396
434
|
return (
|
397
435
|
<div id="data">
|
398
436
|
name: {user.name}, age: {user.age}
|
@@ -430,11 +468,11 @@ const UserInfo = () => {
|
|
430
468
|
const user = useAsyncValue();
|
431
469
|
|
432
470
|
return (
|
433
|
-
|
471
|
+
<div>
|
434
472
|
name: {user.name}, age: {user.age}
|
435
473
|
</div>
|
436
|
-
)
|
437
|
-
}
|
474
|
+
);
|
475
|
+
};
|
438
476
|
|
439
477
|
const Page = () => {
|
440
478
|
const data = useLoaderData() as Data;
|
@@ -459,7 +497,7 @@ export default Page;
|
|
459
497
|
`Await` 组件的 `errorElement` 属性,可以用来处理当 Data Loader 执行时,或者子组件渲染时抛出的错误。
|
460
498
|
例如,我们故意在 Data Loader 函数中抛出错误:
|
461
499
|
|
462
|
-
```ts title=
|
500
|
+
```ts title="page.loader.ts"
|
463
501
|
import { defer } from '@modern-js/runtime/router';
|
464
502
|
|
465
503
|
export default () => {
|
@@ -475,7 +513,7 @@ export default () => {
|
|
475
513
|
|
476
514
|
然后通过 `useAsyncError` 获取错误,并将用于渲染错误信息的组件赋值给 `Await` 组件的 `errorElement` 属性:
|
477
515
|
|
478
|
-
```tsx title=
|
516
|
+
```tsx title="page.ts"
|
479
517
|
import { Await, useAsyncError, useLoaderData } from '@modern-js/runtime/router';
|
480
518
|
import { Suspense } from 'react';
|
481
519
|
|
@@ -174,10 +174,6 @@ Modern.js 也支持 HTML 语法。默认情况下,Modern.js 的应用工程中
|
|
174
174
|
<%= meta %>
|
175
175
|
<title><%= title %></title>
|
176
176
|
<%= topTemplate %>
|
177
|
-
|
178
|
-
<script>
|
179
|
-
window.__assetPrefix__ = '<%= assetPrefix %>';
|
180
|
-
</script>
|
181
177
|
<%= headTemplate %>
|
182
178
|
{/* webpack inject css */}
|
183
179
|
</head>
|
@@ -2,6 +2,7 @@
|
|
2
2
|
title: 路由
|
3
3
|
sidebar_position: 1
|
4
4
|
---
|
5
|
+
|
5
6
|
# 路由
|
6
7
|
|
7
8
|
Modern.js 的路由基于 [React Router 6](https://reactrouter.com/en/main),并提供了多种类型的路由模式。根据不同 [入口](/guides/concept/entries) 类型,将路由分为三种模式,分别是**约定式路由**,**自控式路由**和**其他路由方案**。
|
@@ -77,8 +78,8 @@ Modern.js 支持了业界流行的约定式路由模式:**嵌套路由**,使
|
|
77
78
|
```tsx
|
78
79
|
<Layout>
|
79
80
|
<UserLayout>
|
80
|
-
<UserPage
|
81
|
-
|
81
|
+
<UserPage />
|
82
|
+
</UserLayout>
|
82
83
|
</Layout>
|
83
84
|
```
|
84
85
|
|
@@ -138,8 +139,8 @@ export default () => {
|
|
138
139
|
```tsx
|
139
140
|
<Layout>
|
140
141
|
<UserLayout>
|
141
|
-
<UserPage
|
142
|
-
|
142
|
+
<UserPage />
|
143
|
+
</UserLayout>
|
143
144
|
</Layout>
|
144
145
|
```
|
145
146
|
|
@@ -177,6 +178,7 @@ export default () => {
|
|
177
178
|
:::
|
178
179
|
|
179
180
|
例如以下目录结构:
|
181
|
+
|
180
182
|
```
|
181
183
|
└── routes
|
182
184
|
├── $.tsx
|
@@ -186,11 +188,12 @@ export default () => {
|
|
186
188
|
```
|
187
189
|
|
188
190
|
当访问任何匹配不到的路径时,都会渲染 `routes/$.tsx` 组件,同样,`$.tsx` 中可以使用 [useParams](/apis/app/runtime/router/router#useparams) 捕获 url 的剩余部分。
|
191
|
+
|
189
192
|
```ts title="$.tsx"
|
190
193
|
import { useParams } from '@modern-js/runtime/router';
|
191
194
|
// 当 path 是 `/aaa/bbb` 时
|
192
195
|
const params = useParams();
|
193
|
-
params['*']
|
196
|
+
params['*']; // => 'aaa/bbb'
|
194
197
|
```
|
195
198
|
|
196
199
|
`$.tsx` 可以加入到 `routes` 目录下的任意目录中,一个常见的使用示例是添加 `routes/$.tsx` 文件去定制任意层级的 404 页面。
|
@@ -260,8 +263,8 @@ Modern.js 会生成 `/login` 和 `/sign` 两条路由,`__auth/layout.tsx` 组
|
|
260
263
|
|
261
264
|
```tsx title="当路由为 / 时"
|
262
265
|
<Layout>
|
263
|
-
<Suspense fallback={<Loading/>}>
|
264
|
-
<Page
|
266
|
+
<Suspense fallback={<Loading />}>
|
267
|
+
<Page />
|
265
268
|
</Suspense>
|
266
269
|
</Layout>
|
267
270
|
```
|
@@ -297,15 +300,15 @@ Modern.js 建议必须有根 layout 和根 loading。
|
|
297
300
|
可以通过创建 [`data loader`](/guides/basic-features/data-fetch) 文件做路由的重定向,如有文件 `routes/user/page.tsx`,想对这个文件对应的路由做重定向,可以创建 `routes/user/page.loader.ts` 文件:
|
298
301
|
|
299
302
|
```ts title="routes/user/page.loader.ts"
|
300
|
-
import { redirect } from '@modern-js/runtime/router'
|
303
|
+
import { redirect } from '@modern-js/runtime/router';
|
301
304
|
|
302
305
|
export default () => {
|
303
306
|
const user = await getUser();
|
304
|
-
if(!user){
|
307
|
+
if (!user) {
|
305
308
|
return redirect('/login');
|
306
309
|
}
|
307
310
|
return null;
|
308
|
-
}
|
311
|
+
};
|
309
312
|
```
|
310
313
|
|
311
314
|
### 错误处理
|
@@ -338,7 +341,7 @@ export default ErrorBoundary;
|
|
338
341
|
|
339
342
|
在每个根 `Layout` 组件中(`routes/layout.ts`),可以动态地定义应用运行时配置:
|
340
343
|
|
341
|
-
```
|
344
|
+
```tsx title="src/routes/layout.tsx"
|
342
345
|
// 定义运行时配置
|
343
346
|
import type { AppConfig } from '@modern-js/runtime';
|
344
347
|
|
@@ -348,13 +351,13 @@ export const config = (): AppConfig => {
|
|
348
351
|
createRoutes() {
|
349
352
|
return [
|
350
353
|
{
|
351
|
-
path: '
|
352
|
-
element: <div>
|
354
|
+
path: 'modern',
|
355
|
+
element: <div>modern</div>,
|
353
356
|
},
|
354
357
|
];
|
355
358
|
},
|
356
359
|
},
|
357
|
-
}
|
360
|
+
};
|
358
361
|
};
|
359
362
|
```
|
360
363
|
|
@@ -378,15 +381,13 @@ export const init = (context: RuntimeContext) => {
|
|
378
381
|
:::
|
379
382
|
|
380
383
|
```ts title="src/routes/layout.tsx"
|
381
|
-
import {
|
382
|
-
RuntimeContext,
|
383
|
-
} from '@modern-js/runtime';
|
384
|
+
import { RuntimeContext } from '@modern-js/runtime';
|
384
385
|
|
385
386
|
export const init = (context: RuntimeContext) => {
|
386
387
|
return {
|
387
388
|
message: 'Hello World',
|
388
|
-
}
|
389
|
-
}
|
389
|
+
};
|
390
|
+
};
|
390
391
|
```
|
391
392
|
|
392
393
|
```tsx title="src/routes/page.tsx"
|
@@ -397,7 +398,7 @@ export default () => {
|
|
397
398
|
const { message } = context.getInitData();
|
398
399
|
|
399
400
|
return <div>{message}</div>;
|
400
|
-
}
|
401
|
+
};
|
401
402
|
```
|
402
403
|
|
403
404
|
配合 SSR 功能时,浏览器端可以获取到 SSR 时 `init` 返回的数据,开发者可以自行判断是否要在浏览器端重新获取数据来覆盖 SSR 数据,例如:
|
@@ -409,20 +410,42 @@ export const init = (context: RuntimeContext) => {
|
|
409
410
|
if (process.env.MODERN_TARGET === 'node') {
|
410
411
|
return {
|
411
412
|
message: 'Hello World By Server',
|
412
|
-
}
|
413
|
+
};
|
413
414
|
} else {
|
414
415
|
const { context } = runtimeContext;
|
415
416
|
const data = context.getInitData();
|
416
417
|
// 如果没有获取到期望的数据
|
417
418
|
if (!data.message) {
|
418
419
|
return {
|
419
|
-
message: 'Hello World By Client'
|
420
|
-
}
|
420
|
+
message: 'Hello World By Client',
|
421
|
+
};
|
421
422
|
}
|
422
423
|
}
|
423
|
-
}
|
424
|
+
};
|
424
425
|
```
|
425
426
|
|
427
|
+
### Prefetch
|
428
|
+
|
429
|
+
在约定式路由下, Modern.js 会根据路由,自动地对路由进行分片,当用户访问具体的路由时,会自动加载对应的分片,这样可以有效地减少首屏加载的时间。但这也带来了一个问题,当用户访问一个路由时,如果该路由对应的分片还未加载完成,就会出现白屏的情况。
|
430
|
+
这种情况下你可以通过定义 `loading` 组件,在静态资源加载完成前,展示一个自定义的 `loading` 组件。
|
431
|
+
|
432
|
+
为了进一步提升用户体验,减少 loading 的时间,Modern.js 支持在 Link 组件上定义 `prefetch` 属性,可以提前对静态资源和数据进行加载, `prefetch` 属性有三个可选值:
|
433
|
+
|
434
|
+
```
|
435
|
+
<Link prefetch="intent" to="page">
|
436
|
+
```
|
437
|
+
|
438
|
+
:::info
|
439
|
+
- 该功能目前仅在 Webpack 项目中支持,Rspack 项目暂不支持。
|
440
|
+
- 对数据的预加载目前只会预加载 SSR 项目中 [data loader](/guides/basic-features/data-fetch) 中返回的数据。
|
441
|
+
|
442
|
+
:::
|
443
|
+
|
444
|
+
- `none`, 默认值,不会做 prefetch,没有任何额外的行为。
|
445
|
+
- `intent`,这是我们推荐大多数场景下使用的值,当你把鼠标放在 Link 上时,会自动开始加载对应的分片和 data loader 中定义的数据,当鼠标移开时,会自动取消加载。在我们的测试中,即使是直接点击,也能减少大约 200ms 的加载时间。
|
446
|
+
- `render`,当 Link 组件渲染时,就会加载对应的分片和 data loader 中定义的数据。
|
447
|
+
|
448
|
+
|
426
449
|
|
427
450
|
## 自控式路由
|
428
451
|
|
@@ -469,3 +492,20 @@ export default defineConfig({
|
|
469
492
|
},
|
470
493
|
});
|
471
494
|
```
|
495
|
+
|
496
|
+
### 常见问题
|
497
|
+
|
498
|
+
1. 产物中的代码是 es2015+ 的,期望产物中是 es5 的代码
|
499
|
+
|
500
|
+
react-router^6 的目前默认产物是 es2020 的,如果需要产物中是 es5 的代码,可以配置 `source.include`,让 react-router 相关包经过 bundler 编译 :
|
501
|
+
|
502
|
+
```
|
503
|
+
source: {
|
504
|
+
source: {
|
505
|
+
include: [/@remix-run\/router/, /react-router-dom/, /react-router/],
|
506
|
+
}
|
507
|
+
}
|
508
|
+
```
|
509
|
+
|
510
|
+
|
511
|
+
|
@@ -2,7 +2,7 @@
|
|
2
2
|
sidebar_position: 1
|
3
3
|
---
|
4
4
|
|
5
|
-
#
|
5
|
+
# 入口
|
6
6
|
|
7
7
|
通过本章节,你可以了解到 Modern.js 中的入口约定,以及如何自定义入口。
|
8
8
|
|
@@ -132,9 +132,9 @@ Modern.js 生成的文件内容如下:
|
|
132
132
|
```js
|
133
133
|
import React from 'react';
|
134
134
|
import ReactDOM from 'react-dom/client';
|
135
|
-
import customBootstrap from '@
|
136
|
-
import App from '@
|
137
|
-
import { router, state } from '@
|
135
|
+
import customBootstrap from '@_modern_js_src/index.tsx';
|
136
|
+
import App from '@_modern_js_src/App';
|
137
|
+
import { router, state } from '@modern-js/runtime/plugins';
|
138
138
|
|
139
139
|
const IS_BROWSER = typeof window !== 'undefined' && window.name !== 'nodejs';
|
140
140
|
const MOUNT_ID = 'root';
|