@modern-js/main-doc 0.0.0-next-1681194540734 → 0.0.0-next-1681214624937
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 +9 -2
- package/docs/en/apis/app/runtime/core/use-loader.mdx +4 -0
- package/docs/en/configure/app/usage.mdx +65 -23
- package/docs/en/guides/advanced-features/ssr.mdx +45 -8
- package/docs/en/guides/basic-features/routes.mdx +22 -0
- package/docs/zh/apis/app/runtime/core/use-loader.mdx +4 -0
- package/docs/zh/configure/app/usage.mdx +66 -24
- package/docs/zh/guides/advanced-features/ssr.mdx +59 -16
- package/docs/zh/guides/basic-features/routes.mdx +40 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
# @modern-js/main-doc
|
|
2
2
|
|
|
3
|
-
## 0.0.0-next-
|
|
3
|
+
## 0.0.0-next-1681214624937
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
+
- e91ec97bd0: feat(app-tools): export mergeConfig function
|
|
8
|
+
|
|
9
|
+
feat(app-tools): 导出 mergeConfig 函数
|
|
10
|
+
|
|
11
|
+
- 42700c13bd: chore: improve ssr docs, add more use case for node/web code split
|
|
12
|
+
chore: 优化 ssr 文档,为 node/web 代码分割添加更多使用场景
|
|
7
13
|
- Updated dependencies [1feacdc7d6]
|
|
8
14
|
- Updated dependencies [348306d413]
|
|
9
|
-
|
|
15
|
+
- Updated dependencies [42700c13bd]
|
|
16
|
+
- @modern-js/builder-doc@0.0.0-next-1681214624937
|
|
10
17
|
|
|
11
18
|
## 2.12.0
|
|
12
19
|
|
|
@@ -5,6 +5,10 @@ title: useLoader
|
|
|
5
5
|
|
|
6
6
|
Isomorphic API, usually used to make asynchronous requests. When SSR, the server level uses `useLoader` to prefetch the data, and the browser side also reuses this part of the data.
|
|
7
7
|
|
|
8
|
+
:::tip
|
|
9
|
+
When using Rspack as the bundler, the useLoader API is not currently supported.
|
|
10
|
+
:::
|
|
11
|
+
|
|
8
12
|
## Usage
|
|
9
13
|
|
|
10
14
|
```ts
|
|
@@ -19,17 +19,33 @@ Server runtime configuration can be configured in the `modern.server-runtime.con
|
|
|
19
19
|
|
|
20
20
|
## Configure in the configuration file
|
|
21
21
|
|
|
22
|
-
Modern.js configuration files are defined in the root path of the project, and supports `.
|
|
22
|
+
Modern.js configuration files are defined in the root path of the project, and supports `.ts`, `.js` and `.mjs` formats:
|
|
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 (recommended)
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
We recommend using configuration files in `.ts` format, which provides friendly TypeScript type hints to help you avoid configuration errors.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Import the `defineConfig` tool function from `@modern-js/app-tools`, which will help you with configuration type derivation and type completion:
|
|
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
|
+
```
|
|
45
|
+
|
|
46
|
+
### modern.config.js
|
|
47
|
+
|
|
48
|
+
If you are developing a non-TypeScript project, you can use the configuration file in .js format:
|
|
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 (recommended)
|
|
55
|
-
|
|
56
|
-
We recommend using configuration files in `.ts` format, which provides friendly TypeScript type hints to help you avoid configuration errors.
|
|
57
|
-
|
|
58
|
-
Import the `defineConfig` tool function from `@modern-js/app-tools`, which will help you with configuration type derivation and type completion:
|
|
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
|
### Export Configuration Function
|
|
73
71
|
|
|
74
72
|
Modern.js supports exporting a function in the configuration file, and you can dynamically compute the configuration in the function and return it to Modern.js.
|
|
@@ -200,3 +198,47 @@ modern.config.local.ts
|
|
|
200
198
|
modern.config.local.js
|
|
201
199
|
modern.config.local.mjs
|
|
202
200
|
```
|
|
201
|
+
|
|
202
|
+
## Merge Multiple Configurations
|
|
203
|
+
|
|
204
|
+
In some cases, you may need to merge multiple configurations into one configuration. You can use the `mergeConfig` util to merge multiple configurations.
|
|
205
|
+
|
|
206
|
+
The `mergeConfig` function accepts an array as a parameter, and each item in the array is a configuration object. `mergeConfig` will deeply merge each configuration object in the array, automatically merge multiple functions into an array, and returns a merged configuration object.
|
|
207
|
+
|
|
208
|
+
### Example
|
|
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
|
+
In the above example, the merged configuration object is:
|
|
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
|
+
```
|
|
@@ -246,6 +246,21 @@ if (process.env.MODERN_TARGET === 'browser') {
|
|
|
246
246
|
}
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
+
After packaging in the development environment, SSR and CSR artifacts will be compiled into the following content. Therefore, there will be no more errors due to Web API in the SSR environment:
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
// SSR production
|
|
253
|
+
if (false) {
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// CSR production
|
|
257
|
+
if (true) {
|
|
258
|
+
document.addEventListener('load', () => {
|
|
259
|
+
console.log('document load');
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
249
264
|
:::note
|
|
250
265
|
For more information, see [environment variables](/guides/basic-features/env-vars).
|
|
251
266
|
|
|
@@ -253,7 +268,9 @@ For more information, see [environment variables](/guides/basic-features/env-var
|
|
|
253
268
|
|
|
254
269
|
### Use File Suffix
|
|
255
270
|
|
|
256
|
-
|
|
271
|
+
However, in the second case, for example, when `fs-extra` is imported into the code, it internally uses the Node API with side effects. If it is directly referenced in the component, it will cause CSR loading errors.
|
|
272
|
+
|
|
273
|
+
Env vars is not effective in this situation. Modern.js also supports distinguishing SSR Bundle and CSR Bundle packaging files through files with the `.node.` suffix.
|
|
257
274
|
|
|
258
275
|
For example, the import of `fs-extra` in the code, when it is directly referenced to the component, will cause the CSR to load an error. You can create `.ts` and `.node.ts` files of the same name as a layer of proxy:
|
|
259
276
|
|
|
@@ -280,7 +297,27 @@ export const loader = () => {
|
|
|
280
297
|
|
|
281
298
|
### Independent File
|
|
282
299
|
|
|
283
|
-
Both of the above methods
|
|
300
|
+
Both of the above methods can bring some mental burden to developers. In real business scenarios, we found that most of the mixed Node/Web code occurs in data requests.
|
|
301
|
+
|
|
302
|
+
Therefore, Modern.js developed a [Data Fetch](/guides/basic-features/data-fetch) to separate CSR and SSR code based on [Nested Routing](/guides/basic-features/routes).
|
|
303
|
+
|
|
304
|
+
We can separate **data request** and **component code** by using independent files. Write component logic in `routes/page.tsx` and data request logic in `routes/page.loader.ts`.
|
|
305
|
+
|
|
306
|
+
```ts title="routes/page.tsx"
|
|
307
|
+
export default Page = () => {
|
|
308
|
+
return <div>Hello World<div>
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
```ts title="routes/page.loader.tsx"
|
|
313
|
+
import fse from 'fs-extra';
|
|
314
|
+
export default () => {
|
|
315
|
+
const file = fse.readFileSync('./myfile');
|
|
316
|
+
return {
|
|
317
|
+
...
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
```
|
|
284
321
|
|
|
285
322
|
## Remote Request
|
|
286
323
|
|
|
@@ -317,7 +354,7 @@ The streaming SSR of Modern.js is implemented based on React Router, and the mai
|
|
|
317
354
|
|
|
318
355
|
### Return async data
|
|
319
356
|
|
|
320
|
-
```ts title=
|
|
357
|
+
```ts title="page.loader.ts"
|
|
321
358
|
import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
|
|
322
359
|
|
|
323
360
|
interface User {
|
|
@@ -351,7 +388,7 @@ therefore, the parameter passed to `defer` is `{data: user}`.
|
|
|
351
388
|
|
|
352
389
|
`defer` can also receive asynchronous data and synchronous data at the same time. For example:
|
|
353
390
|
|
|
354
|
-
```ts title=
|
|
391
|
+
```ts title="page.loader.ts"
|
|
355
392
|
|
|
356
393
|
// skip some codes
|
|
357
394
|
|
|
@@ -388,7 +425,7 @@ export default ({ params }: LoaderFunctionArgs) => {
|
|
|
388
425
|
|
|
389
426
|
Use the `Await` component to render the data returned asynchronously from the Data Loader. For example:
|
|
390
427
|
|
|
391
|
-
```tsx title=
|
|
428
|
+
```tsx title="page.tsx"
|
|
392
429
|
import { Await, useLoaderData } from '@modern-js/runtime/router';
|
|
393
430
|
import { Suspense } from 'react';
|
|
394
431
|
import type { Data } from './page.loader';
|
|
@@ -430,7 +467,7 @@ So, here we import like this: `import type { Data } from './page.loader'`;
|
|
|
430
467
|
|
|
431
468
|
You can also get the asynchronous data returned by Data Loader through `useAsyncValue`. For example:
|
|
432
469
|
|
|
433
|
-
```tsx title=
|
|
470
|
+
```tsx title="page.tsx"
|
|
434
471
|
import { useAsyncValue } from '@modern-js/runtime/router';
|
|
435
472
|
|
|
436
473
|
// skip some codes
|
|
@@ -468,7 +505,7 @@ export default Page;
|
|
|
468
505
|
The `errorElement` property of the `Await` component can be used to handle errors thrown when the Data Loader executes or when a child component renders.
|
|
469
506
|
For example, we intentionally throw an error in the Data Loader function:
|
|
470
507
|
|
|
471
|
-
```ts title=
|
|
508
|
+
```ts title="page.loader.ts"
|
|
472
509
|
import { defer } from '@modern-js/runtime/router';
|
|
473
510
|
|
|
474
511
|
export default () => {
|
|
@@ -484,7 +521,7 @@ export default () => {
|
|
|
484
521
|
|
|
485
522
|
Then use `useAsyncError` to get the error, and assign the component used to render the error to the `errorElement` property of the `Await` component:
|
|
486
523
|
|
|
487
|
-
```tsx title=
|
|
524
|
+
```tsx title="page.ts"
|
|
488
525
|
import { Await, useAsyncError, useLoaderData } from '@modern-js/runtime/router';
|
|
489
526
|
import { Suspense } from 'react';
|
|
490
527
|
|
|
@@ -345,6 +345,28 @@ export const config = (): AppConfig => {
|
|
|
345
345
|
};
|
|
346
346
|
```
|
|
347
347
|
|
|
348
|
+
### Prefetch
|
|
349
|
+
|
|
350
|
+
When using convention-based routing, Modern.js will automatically split chunks according to the route, and when the user accesses a specific route, the corresponding resources will be loaded automatically, which can effectively reduce the first screen loading time. However, this also brings a problem, when the user accesses a route, if the asset corresponding to that route is not yet loaded, a white screen will appear.
|
|
351
|
+
|
|
352
|
+
In this case you can show a custom `loading` component by defining a `loading` component before the static resource is loaded.
|
|
353
|
+
|
|
354
|
+
To further improve the user experience and reduce time of loading, Modern.js supports defining the `prefetch` property on the Link component to load static resources and data in advance, with three optional values for the `prefetch` property:
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
<Link prefetch="intent" to="page">
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
:::info
|
|
361
|
+
- This feature is currently only supported in Webpack projects, not in Rspack projects.
|
|
362
|
+
- Prefetching of data will only prefetch the data returned from the data loader of the SSR project.
|
|
363
|
+
|
|
364
|
+
:::
|
|
365
|
+
|
|
366
|
+
- `none`, the default value, will not do prefetch, no additional behavior.
|
|
367
|
+
- `intent`, the value we recommend for most scenarios, will automatically start loading the corresponding resources and the data defined in the data loader when you mouse over the Link, and will automatically unload it when the mouse is moved away. In our tests, even a direct click can reduce the loading time by about 200ms.
|
|
368
|
+
- `render`, when the Link component renders, it will load the corresponding resources and the data defined in the data loader.
|
|
369
|
+
|
|
348
370
|
## Self-controlled routing
|
|
349
371
|
|
|
350
372
|
With `src/App.tsx` as the agreed entry, Modern.js will not do additional operations with multiple routes, developers can use the React Router 6 API for development by themselves, for example:
|
|
@@ -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
|
+
```
|
|
@@ -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 加载报错。
|
|
271
|
+
|
|
272
|
+
环境变量的方式并不能在这种情况下生效,Modern.js 也支持通过 `.node.` 后缀的文件来区分 SSR Bundle 和 CSR Bundle 产物的打包文件。
|
|
250
273
|
|
|
251
|
-
|
|
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
|
|
|
@@ -307,7 +350,7 @@ Modern.js 的流式渲染基于 React Router 实现,主要涉及 API 有:
|
|
|
307
350
|
|
|
308
351
|
### 异步获取数据
|
|
309
352
|
|
|
310
|
-
```ts title=
|
|
353
|
+
```ts title="page.loader.ts"
|
|
311
354
|
import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
|
|
312
355
|
|
|
313
356
|
interface User {
|
|
@@ -336,11 +379,11 @@ export default ({ params }: LoaderFunctionArgs) => {
|
|
|
336
379
|
```
|
|
337
380
|
|
|
338
381
|
`user` 是一个 Promise 类型的对象,表示需要异步获取的数据,通过 `defer` 处理需要异步获取的 `user`。注意,`defer` 必须接收一个对象类型的参数,
|
|
339
|
-
因此, 传入 `defer`
|
|
382
|
+
因此, 传入 `defer` 的参数为:`{ data: user }`
|
|
340
383
|
|
|
341
384
|
`defer` 还可以同时接收异步数据和同步数据。例如:
|
|
342
385
|
|
|
343
|
-
```ts title=
|
|
386
|
+
```ts title="page.loader.ts"
|
|
344
387
|
// 省略部分代码
|
|
345
388
|
|
|
346
389
|
export default ({ params }: LoaderFunctionArgs) => {
|
|
@@ -374,7 +417,7 @@ export default ({ params }: LoaderFunctionArgs) => {
|
|
|
374
417
|
|
|
375
418
|
通过 `Await` 组件,可以获取到 Data Loader 中异步返回的数据,然后进行渲染。例如:
|
|
376
419
|
|
|
377
|
-
```tsx title=
|
|
420
|
+
```tsx title="page.tsx"
|
|
378
421
|
import { Await, useLoaderData } from '@modern-js/runtime/router';
|
|
379
422
|
import { Suspense } from 'react';
|
|
380
423
|
import type { Data } from './page.loader';
|
|
@@ -454,7 +497,7 @@ export default Page;
|
|
|
454
497
|
`Await` 组件的 `errorElement` 属性,可以用来处理当 Data Loader 执行时,或者子组件渲染时抛出的错误。
|
|
455
498
|
例如,我们故意在 Data Loader 函数中抛出错误:
|
|
456
499
|
|
|
457
|
-
```ts title=
|
|
500
|
+
```ts title="page.loader.ts"
|
|
458
501
|
import { defer } from '@modern-js/runtime/router';
|
|
459
502
|
|
|
460
503
|
export default () => {
|
|
@@ -470,7 +513,7 @@ export default () => {
|
|
|
470
513
|
|
|
471
514
|
然后通过 `useAsyncError` 获取错误,并将用于渲染错误信息的组件赋值给 `Await` 组件的 `errorElement` 属性:
|
|
472
515
|
|
|
473
|
-
```tsx title=
|
|
516
|
+
```tsx title="page.ts"
|
|
474
517
|
import { Await, useAsyncError, useLoaderData } from '@modern-js/runtime/router';
|
|
475
518
|
import { Suspense } from 'react';
|
|
476
519
|
|
|
@@ -424,6 +424,29 @@ export const init = (context: RuntimeContext) => {
|
|
|
424
424
|
};
|
|
425
425
|
```
|
|
426
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
|
+
|
|
449
|
+
|
|
427
450
|
## 自控式路由
|
|
428
451
|
|
|
429
452
|
以 `src/App.tsx` 为约定的入口,Modern.js 不会多路由做额外的操作,开发者可以自行使用 React Router 6 的 API 进行开发,例如:
|
|
@@ -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
|
+
|
package/package.json
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"modern",
|
|
12
12
|
"modern.js"
|
|
13
13
|
],
|
|
14
|
-
"version": "0.0.0-next-
|
|
14
|
+
"version": "0.0.0-next-1681214624937",
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"registry": "https://registry.npmjs.org/",
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@modern-js/builder-doc": "0.0.0-next-
|
|
20
|
+
"@modern-js/builder-doc": "0.0.0-next-1681214624937"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"classnames": "^2",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"fs-extra": "^10",
|
|
30
30
|
"@types/node": "^16",
|
|
31
31
|
"@types/fs-extra": "^9",
|
|
32
|
-
"@modern-js/builder-doc": "0.0.0-next-
|
|
33
|
-
"@modern-js/doc-tools": "0.0.0-next-
|
|
34
|
-
"@modern-js/doc-plugin-auto-sidebar": "0.0.0-next-
|
|
32
|
+
"@modern-js/builder-doc": "0.0.0-next-1681214624937",
|
|
33
|
+
"@modern-js/doc-tools": "0.0.0-next-1681214624937",
|
|
34
|
+
"@modern-js/doc-plugin-auto-sidebar": "0.0.0-next-1681214624937"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"dev": "modern dev",
|