@modern-js/main-doc 2.12.0 → 2.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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';
|