@modern-js/main-doc 2.58.3 → 2.59.0
Sign up to get free protection for your applications and to get access to all the features.
- package/docs/en/apis/app/runtime/core/use-loader.mdx +1 -1
- package/docs/en/components/init-app.mdx +0 -1
- package/docs/en/components/init-rspack-app.mdx +0 -1
- package/docs/en/components/ssr-monitor.mdx +3 -0
- package/docs/en/configure/app/output/ssg.mdx +52 -141
- package/docs/en/configure/app/tools/tailwindcss.mdx +1 -1
- package/docs/en/guides/advanced-features/_meta.json +0 -8
- package/docs/en/guides/advanced-features/rsbuild-plugin.mdx +2 -2
- package/docs/en/guides/advanced-features/rspack-start.mdx +6 -14
- package/docs/en/guides/basic-features/_meta.json +31 -9
- package/docs/en/guides/basic-features/css/_meta.json +1 -0
- package/docs/en/guides/basic-features/css/css-in-js.mdx +34 -0
- package/docs/en/guides/basic-features/{css-modules.mdx → css/css-modules.mdx} +0 -4
- package/docs/en/guides/basic-features/css/css.mdx +25 -0
- package/docs/en/guides/basic-features/{css.mdx → css/tailwindcss.mdx} +5 -66
- package/docs/en/guides/basic-features/data/data-fetch.mdx +134 -235
- package/docs/en/guides/basic-features/data/data-write.mdx +66 -77
- package/docs/en/guides/basic-features/debug/_meta.json +1 -0
- package/docs/en/guides/basic-features/debug/rsdoctor.mdx +57 -0
- package/docs/en/guides/{advanced-features → basic-features/debug}/using-storybook.mdx +2 -0
- package/docs/en/guides/basic-features/render/_meta.json +1 -0
- package/docs/en/guides/basic-features/render/ssg.mdx +208 -0
- package/docs/en/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +38 -50
- package/docs/en/guides/basic-features/render/ssr.mdx +301 -0
- package/docs/en/guides/basic-features/render/streaming-ssr.mdx +230 -0
- package/docs/en/guides/basic-features/routes.mdx +275 -263
- package/docs/en/guides/basic-features/static-assets/_meta.json +1 -0
- package/docs/en/guides/basic-features/static-assets.mdx +1 -1
- package/docs/en/guides/basic-features/testing/_meta.json +1 -0
- package/docs/en/guides/basic-features/testing/cypress.mdx +95 -0
- package/docs/en/guides/basic-features/testing/jest.mdx +148 -0
- package/docs/en/guides/basic-features/testing/playwright.mdx +111 -0
- package/docs/en/guides/basic-features/testing/vitest.mdx +100 -0
- package/docs/en/guides/concept/entries.mdx +9 -2
- package/docs/en/guides/get-started/quick-start.mdx +1 -1
- package/docs/en/guides/get-started/tech-stack.mdx +4 -4
- package/docs/en/guides/topic-detail/generator/create/config.mdx +0 -10
- package/docs/en/guides/topic-detail/generator/create/use.mdx +0 -1
- package/docs/en/tutorials/first-app/c03-css.mdx +1 -1
- package/docs/zh/apis/app/runtime/core/use-loader.mdx +1 -1
- package/docs/zh/components/init-app.mdx +0 -1
- package/docs/zh/components/init-rspack-app.mdx +0 -1
- package/docs/zh/components/ssr-monitor.mdx +3 -0
- package/docs/zh/configure/app/output/ssg.mdx +49 -139
- package/docs/zh/configure/app/tools/tailwindcss.mdx +1 -1
- package/docs/zh/guides/advanced-features/_meta.json +0 -8
- package/docs/zh/guides/advanced-features/rsbuild-plugin.mdx +2 -2
- package/docs/zh/guides/advanced-features/rspack-start.mdx +7 -16
- package/docs/zh/guides/basic-features/_meta.json +31 -9
- package/docs/zh/guides/basic-features/css/_meta.json +1 -0
- package/docs/zh/guides/basic-features/css/css-in-js.mdx +34 -0
- package/docs/zh/guides/basic-features/css/css.mdx +25 -0
- package/docs/zh/guides/basic-features/{css.mdx → css/tailwindcss.mdx} +3 -64
- package/docs/zh/guides/basic-features/data/data-fetch.mdx +96 -211
- package/docs/zh/guides/basic-features/data/data-write.mdx +54 -55
- package/docs/zh/guides/basic-features/debug/_meta.json +1 -0
- package/docs/zh/guides/basic-features/debug/rsdoctor.mdx +57 -0
- package/docs/zh/guides/{advanced-features → basic-features/debug}/using-storybook.mdx +1 -1
- package/docs/zh/guides/basic-features/render/_meta.json +1 -0
- package/docs/zh/guides/basic-features/render/ssg.mdx +210 -0
- package/docs/zh/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +16 -26
- package/docs/zh/guides/basic-features/render/ssr.mdx +309 -0
- package/docs/zh/guides/{advanced-features/ssr/stream.mdx → basic-features/render/streaming-ssr.mdx} +22 -37
- package/docs/zh/guides/basic-features/routes.mdx +252 -237
- package/docs/zh/guides/basic-features/static-assets/_meta.json +1 -0
- package/docs/zh/guides/basic-features/static-assets.mdx +2 -6
- package/docs/zh/guides/basic-features/testing/_meta.json +1 -0
- package/docs/zh/guides/basic-features/testing/cypress.mdx +95 -0
- package/docs/zh/guides/basic-features/testing/jest.mdx +148 -0
- package/docs/zh/guides/basic-features/testing/playwright.mdx +112 -0
- package/docs/zh/guides/basic-features/testing/vitest.mdx +100 -0
- package/docs/zh/guides/concept/entries.mdx +6 -3
- package/docs/zh/guides/get-started/quick-start.mdx +1 -1
- package/docs/zh/guides/get-started/tech-stack.mdx +8 -8
- package/docs/zh/guides/topic-detail/generator/create/config.mdx +0 -10
- package/docs/zh/guides/topic-detail/generator/create/use.mdx +0 -1
- package/docs/zh/tutorials/first-app/c03-css.mdx +1 -1
- package/i18n.json +16 -4
- package/package.json +6 -6
- package/docs/en/apis/app/hooks/config/storybook.mdx +0 -37
- package/docs/en/guides/advanced-features/ssg.mdx +0 -116
- package/docs/en/guides/advanced-features/ssr/_meta.json +0 -1
- package/docs/en/guides/advanced-features/ssr/index.mdx +0 -23
- package/docs/en/guides/advanced-features/ssr/stream.mdx +0 -248
- package/docs/en/guides/advanced-features/ssr/usage.mdx +0 -341
- package/docs/en/guides/advanced-features/ssr.mdx +0 -555
- package/docs/zh/apis/app/hooks/config/storybook.mdx +0 -38
- package/docs/zh/guides/advanced-features/ssg.mdx +0 -116
- package/docs/zh/guides/advanced-features/ssr/_meta.json +0 -1
- package/docs/zh/guides/advanced-features/ssr/index.mdx +0 -23
- package/docs/zh/guides/advanced-features/ssr/usage.mdx +0 -329
- /package/docs/en/guides/basic-features/{mock.mdx → debug/mock.mdx} +0 -0
- /package/docs/en/guides/basic-features/{proxy.mdx → debug/proxy.mdx} +0 -0
- /package/docs/en/guides/basic-features/{json-files.mdx → static-assets/json-files.mdx} +0 -0
- /package/docs/en/guides/basic-features/{svg-assets.mdx → static-assets/svg-assets.mdx} +0 -0
- /package/docs/en/guides/basic-features/{wasm-assets.mdx → static-assets/wasm-assets.mdx} +0 -0
- /package/docs/zh/guides/basic-features/{css-modules.mdx → css/css-modules.mdx} +0 -0
- /package/docs/zh/guides/basic-features/{mock.mdx → debug/mock.mdx} +0 -0
- /package/docs/zh/guides/basic-features/{proxy.mdx → debug/proxy.mdx} +0 -0
- /package/docs/zh/guides/basic-features/{json-files.mdx → static-assets/json-files.mdx} +0 -0
- /package/docs/zh/guides/basic-features/{svg-assets.mdx → static-assets/svg-assets.mdx} +0 -0
- /package/docs/zh/guides/basic-features/{wasm-assets.mdx → static-assets/wasm-assets.mdx} +0 -0
package/package.json
CHANGED
@@ -15,20 +15,20 @@
|
|
15
15
|
"modern",
|
16
16
|
"modern.js"
|
17
17
|
],
|
18
|
-
"version": "2.
|
18
|
+
"version": "2.59.0",
|
19
19
|
"publishConfig": {
|
20
20
|
"registry": "https://registry.npmjs.org/",
|
21
21
|
"access": "public",
|
22
22
|
"provenance": true
|
23
23
|
},
|
24
24
|
"dependencies": {
|
25
|
-
"@modern-js/sandpack-react": "2.
|
25
|
+
"@modern-js/sandpack-react": "2.59.0"
|
26
26
|
},
|
27
27
|
"peerDependencies": {
|
28
|
-
"@modern-js/builder-doc": "^2.
|
28
|
+
"@modern-js/builder-doc": "^2.59.0"
|
29
29
|
},
|
30
30
|
"devDependencies": {
|
31
|
-
"@rspress/shared": "1.
|
31
|
+
"@rspress/shared": "1.28.2",
|
32
32
|
"@types/fs-extra": "9.0.13",
|
33
33
|
"@types/node": "^16",
|
34
34
|
"classnames": "^2",
|
@@ -36,10 +36,10 @@
|
|
36
36
|
"fs-extra": "^10",
|
37
37
|
"react": "^18",
|
38
38
|
"react-dom": "^18",
|
39
|
-
"rspress": "1.
|
39
|
+
"rspress": "1.28.2",
|
40
40
|
"ts-node": "^10.9.1",
|
41
41
|
"typescript": "^5",
|
42
|
-
"@modern-js/builder-doc": "2.
|
42
|
+
"@modern-js/builder-doc": "2.59.0"
|
43
43
|
},
|
44
44
|
"scripts": {
|
45
45
|
"dev": "rspress dev",
|
@@ -1,37 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: storybook/
|
3
|
-
sidebar_position: 7
|
4
|
-
---
|
5
|
-
|
6
|
-
# storybook/
|
7
|
-
|
8
|
-
Modern.js supports debugging using Storybook. When configuring Storybook, you need to configure it in the `config/storybook` directory of the project.
|
9
|
-
|
10
|
-
Please refer to [Storybook Configuration](https://storybook.js.org/docs/react/configure/overview) for Storybook configuration.
|
11
|
-
|
12
|
-
:::info
|
13
|
-
Enabling the Storybook function requires running the new command to enable it under the project first.
|
14
|
-
:::
|
15
|
-
|
16
|
-
#### Example
|
17
|
-
|
18
|
-
For the webpack configuration of the Storybook Manager app section, you can configure it by adding the `./config/storybook/main.js` file to configure it.
|
19
|
-
|
20
|
-
```js
|
21
|
-
// ./config/storybook/main.js
|
22
|
-
|
23
|
-
module.exports = {
|
24
|
-
// it controls the Storybook manager app
|
25
|
-
managerWebpack: async (config, options) => {
|
26
|
-
// update config here
|
27
|
-
return config;
|
28
|
-
},
|
29
|
-
};
|
30
|
-
```
|
31
|
-
|
32
|
-
### Limitation
|
33
|
-
|
34
|
-
There are some limitations when using the `config/storybook` directory for configuration:
|
35
|
-
|
36
|
-
- The location where the Story file is stored cannot be modified, that is, the `stories` configuration cannot be modified in the `main.js` file.
|
37
|
-
- It is not supported to modify Webpack and Babel related configurations in `main.js`, related requirements can be passed through [`tools.webpack`](/configure/app/tools/webpack.html) /[`tools.babel`](/configure/app/tools/babel.html) modify.
|
@@ -1,116 +0,0 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 5
|
3
|
-
---
|
4
|
-
|
5
|
-
# Static Site Generation
|
6
|
-
|
7
|
-
SSG (Static Site Generation) is a solution based on data and templates that renders complete static web pages during the build process.
|
8
|
-
|
9
|
-
First need to execute `pnpm run new` to enable the SSG feature:
|
10
|
-
|
11
|
-
```bash
|
12
|
-
? Please select the operation you want: Enable features
|
13
|
-
? Please select the feature name: Enable SSG
|
14
|
-
```
|
15
|
-
|
16
|
-
Register the SSG plugin in `modern.config.ts` after executing the command:
|
17
|
-
|
18
|
-
```ts title="modern.config.ts"
|
19
|
-
import { ssgPlugin } from '@modern-js/plugin-ssg';
|
20
|
-
|
21
|
-
export default defineConfig({
|
22
|
-
output: {
|
23
|
-
ssg: true,
|
24
|
-
},
|
25
|
-
plugins: [..., ssgPlugin()],
|
26
|
-
});
|
27
|
-
```
|
28
|
-
|
29
|
-
The usage of SSG differs between the **Conventional Routing** and **Self-controlled Routing**.
|
30
|
-
|
31
|
-
### Using with Conventional Routing
|
32
|
-
|
33
|
-
In the Conventional Routing of Modern.js, the framework generates routes based on the file structure under the entry point, so it can collect complete routing information.
|
34
|
-
|
35
|
-
For example, here is a project directory structure that uses conventional routing:
|
36
|
-
|
37
|
-
```bash
|
38
|
-
.
|
39
|
-
├── src
|
40
|
-
│ └── routes
|
41
|
-
│ ├── layout.tsx
|
42
|
-
│ ├── page.tsx
|
43
|
-
│ └── user
|
44
|
-
│ ├── layout.tsx
|
45
|
-
│ ├── page.tsx
|
46
|
-
│ └── profile
|
47
|
-
│ └── page.tsx
|
48
|
-
```
|
49
|
-
|
50
|
-
The above file directory will generate the following three routes:
|
51
|
-
|
52
|
-
- `/`
|
53
|
-
- `/user`
|
54
|
-
- `/user/profile`
|
55
|
-
|
56
|
-
:::note
|
57
|
-
If you are not familiar with the rules of Conventional Routing, you can first check [Routing](/guides/basic-features/routes).
|
58
|
-
|
59
|
-
:::
|
60
|
-
|
61
|
-
Add component code in `src/routes/page.tsx`:
|
62
|
-
|
63
|
-
```jsx title="src/routes/page.tsx"
|
64
|
-
export default () => {
|
65
|
-
return <div>Index Page</div>;
|
66
|
-
};
|
67
|
-
```
|
68
|
-
|
69
|
-
SSG also renders pages in a Node.js environment, so we can **enable SSR during development** to expose code issues and validate SSG rendering effects in advance:
|
70
|
-
|
71
|
-
```ts title="modern.config.ts"
|
72
|
-
export default defineConfig({
|
73
|
-
server: {
|
74
|
-
ssr: process.env.NODE_ENV === 'development',
|
75
|
-
}
|
76
|
-
}
|
77
|
-
```
|
78
|
-
|
79
|
-
Execute the `pnpm run dev` command in the project to view the `dist/` directory, and only generate an HTML file `main/index.html`.
|
80
|
-
|
81
|
-
Execute the `pnpm run build` command in the root path of the project. After the construction is completed, view the `dist/` directory, and generate `main/index.html`, `main/user/index.html` and `main/user/profile/index.html` three HTML files, the content corresponds to the above three routes.
|
82
|
-
|
83
|
-
Each route in the **Conventional Routing** will generate a separate HTML file. By viewing `main/index.html`, you can find the text content that includes the `Index Page`, which is exactly the effect of SSG.
|
84
|
-
|
85
|
-
After executing `pnpm run serve` to start the project, visit the page in the Network, view the document returned by the request. The document contains the complete page content rendered by the component.
|
86
|
-
|
87
|
-
### Using with Self-controlled Routing
|
88
|
-
|
89
|
-
**Self-controlled routing** is a routing through component code, which requires the application to run to obtain accurate routing information. Therefore, the SSG function cannot be used out of the box. At this time, users needs to inform the Modern.js framework in advance which routes need to enable SSG.
|
90
|
-
|
91
|
-
For example, there is the following code which contains multiple routes. When setting `output.ssg` to `true`, only the entry route '/' will be rendered by default:
|
92
|
-
|
93
|
-
import SelfRouteExample from '@site-docs/components/self-route-example';
|
94
|
-
|
95
|
-
<SelfRouteExample />
|
96
|
-
|
97
|
-
We can configure `output.ssg` to inform Modern.js to enable SSG for specific routes, such as `/about`:
|
98
|
-
|
99
|
-
```ts title="modern.config.ts"
|
100
|
-
export default defineConfig({
|
101
|
-
output: {
|
102
|
-
ssg: {
|
103
|
-
routes: ['/', '/about'],
|
104
|
-
},
|
105
|
-
},
|
106
|
-
});
|
107
|
-
```
|
108
|
-
|
109
|
-
After executing `pnpm run build` and `pnpm run serve`, you can access `http://localhost:8080/about` to see the rendered page in preview.
|
110
|
-
|
111
|
-
You can check the bundle file that a new `main/about/index.html` file has been added to the `dist/` directory.
|
112
|
-
|
113
|
-
:::info
|
114
|
-
Above only introduces the case of single entry, for more related content please refer to the [SSG API](/configure/app/output/ssg).
|
115
|
-
|
116
|
-
:::
|
@@ -1 +0,0 @@
|
|
1
|
-
["usage", "stream", "cache"]
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# SSR
|
2
|
-
|
3
|
-
By rendering the HTML content of web pages into complete web pages on the server side, and then sending the generated web pages to the client, the client only needs to display the web pages without further rendering.
|
4
|
-
|
5
|
-
Its main advantages are:
|
6
|
-
|
7
|
-
- Improve first screen load speed: SSR can generate complete websites on the server side, the client only needs to download the content of the website, no additional renderings are required, thus improving the first screen load speed.
|
8
|
-
- Improve user experience: SSR can improve the responsiveness of web pages, thereby enhancing the user experience.
|
9
|
-
- Good for SEO: SSR can generate complete HTML content. Search engines can directly index HTML content to improve website ranking.
|
10
|
-
|
11
|
-
If you have the following scenarios, developers can consider using SSR to render your pages:
|
12
|
-
|
13
|
-
1. Websites with higher first-screen loading speed requirements, such as e-commerce websites, news websites, etc.
|
14
|
-
2. Websites with higher user experience requirements, such as social networking sites, gaming sites, etc.
|
15
|
-
3. Websites with higher SEO requirements, such as corporate websites, blogs, etc.
|
16
|
-
|
17
|
-
In Modern.js, SSR is also out-of-the-box. Developers do not need to write complex server-side logic for SSR, nor do they need to worry about the operation and maintenance of SSR, or create separate services.
|
18
|
-
|
19
|
-
In addition to the out-of-the-box SSR service, to ensure the developer's development experience, we also have:
|
20
|
-
|
21
|
-
- A complete SSR downgrade strategy to ensure that the page can run safely.
|
22
|
-
- Automatically split sub-routes to load on demand, reducing the size of the first screen resources.
|
23
|
-
- Built-in caching system to solve the problem of high server-side load.
|
@@ -1,248 +0,0 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 2
|
3
|
-
title: Streaming SSR
|
4
|
-
---
|
5
|
-
|
6
|
-
# Streaming SSR
|
7
|
-
|
8
|
-
## Overview
|
9
|
-
|
10
|
-
Stream rendering is a new way of rendering, which can update the page content in real time when the user interacts with the page, thereby improving the user experience.
|
11
|
-
|
12
|
-
In traditional rendering, the rendering of the page is completed at once, while in stream rendering, the rendering of the page is gradually completed. When the user interacts with the page, data is loaded gradually instead of loading all at once.
|
13
|
-
|
14
|
-
Compared to traditional rendering:
|
15
|
-
|
16
|
-
- Faster perceived speed: Stream rendering can gradually display content during the rendering process to display the business home page at the fastest speed.
|
17
|
-
- Better user experience: Through stream rendering, users can see the content on the page faster, instead of waiting for the entire page to be rendered before they can interact.
|
18
|
-
- Better performance control: Stream rendering allows developers to better control the loading priority and order of pages, thereby optimizing performance and user experience.
|
19
|
-
- Better adaptability: Stream rendering can better adapt to different network speeds and device performances, allowing the page to perform better in various environments.
|
20
|
-
|
21
|
-
## Usage
|
22
|
-
|
23
|
-
Modern.js supports streaming rendering in React 18 which can be enabled through the following configuration:
|
24
|
-
|
25
|
-
```ts title="modern.config.ts"
|
26
|
-
import { defineConfig } from '@modern-js/app-tools';
|
27
|
-
|
28
|
-
export default defineConfig({
|
29
|
-
server: {
|
30
|
-
ssr: {
|
31
|
-
mode: 'stream',
|
32
|
-
},
|
33
|
-
},
|
34
|
-
});
|
35
|
-
```
|
36
|
-
|
37
|
-
The streaming SSR of Modern.js is implemented based on React Router, and the main APIs involved are:
|
38
|
-
|
39
|
-
- [`defer`](https://reactrouter.com/en/main/utils/defer): This utility allows you to defer values returned from loaders by passing promises instead of resolved values.
|
40
|
-
- [`Await`](https://reactrouter.com/en/main/components/await): Used to render deferred values with automatic error handling.
|
41
|
-
- [`useAsyncValue`](https://reactrouter.com/en/main/hooks/use-async-value): Returns the resolved data from the nearest `<Await>` ancestor component.
|
42
|
-
|
43
|
-
### Return async data
|
44
|
-
|
45
|
-
```ts title="page.data.ts"
|
46
|
-
import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
|
47
|
-
|
48
|
-
interface User {
|
49
|
-
name: string;
|
50
|
-
age: number;
|
51
|
-
}
|
52
|
-
|
53
|
-
export interface Data {
|
54
|
-
data: User;
|
55
|
-
}
|
56
|
-
|
57
|
-
export const loader = ({ params }: LoaderFunctionArgs) => {
|
58
|
-
const userId = params.id;
|
59
|
-
|
60
|
-
const user = new Promise<User>(resolve => {
|
61
|
-
setTimeout(() => {
|
62
|
-
resolve({
|
63
|
-
name: `user-${userId}`,
|
64
|
-
age: 18,
|
65
|
-
});
|
66
|
-
}, 200);
|
67
|
-
});
|
68
|
-
|
69
|
-
return defer({ data: user });
|
70
|
-
};
|
71
|
-
```
|
72
|
-
|
73
|
-
`user` is a `Promise` object that represents the data that needs to be obtained asynchronously. Use `defer` to handle the asynchronous retrieval of user. Note that `defer` must receive an object type parameter, so the parameter passed to `defer` is: `{ data: user }`.
|
74
|
-
|
75
|
-
`defer` can receive both asynchronous and synchronous data at the same time. For example:
|
76
|
-
|
77
|
-
```ts title="page.data.ts"
|
78
|
-
// skip some codes
|
79
|
-
|
80
|
-
export default ({ params }: LoaderFunctionArgs) => {
|
81
|
-
const userId = params.id;
|
82
|
-
|
83
|
-
const user = new Promise<User>(resolve => {
|
84
|
-
setTimeout(() => {
|
85
|
-
resolve({
|
86
|
-
name: `user-${userId}`,
|
87
|
-
age: 18,
|
88
|
-
});
|
89
|
-
}, 200);
|
90
|
-
});
|
91
|
-
|
92
|
-
const otherData = new Promise<string>(resolve => {
|
93
|
-
setTimeout(() => {
|
94
|
-
resolve('some sync data');
|
95
|
-
}, 200);
|
96
|
-
});
|
97
|
-
|
98
|
-
return defer({
|
99
|
-
data: user,
|
100
|
-
other: await otherData,
|
101
|
-
});
|
102
|
-
};
|
103
|
-
```
|
104
|
-
|
105
|
-
The data obtained from otherData is synchronous because it has an `await` keyword in front of it. It can be passed into `defer` together with the asynchronous data obtained from `user`.
|
106
|
-
|
107
|
-
### Render asynchronous data.
|
108
|
-
|
109
|
-
With the `<Await>` component, you can retrieve the data asynchronously returned by the Data Loader and then render it. For example:
|
110
|
-
|
111
|
-
```tsx title="page.tsx"
|
112
|
-
import { Await, useLoaderData } from '@modern-js/runtime/router';
|
113
|
-
import { Suspense } from 'react';
|
114
|
-
import type { Data } from './page.data';
|
115
|
-
|
116
|
-
const Page = () => {
|
117
|
-
const data = useLoaderData() as Data;
|
118
|
-
|
119
|
-
return (
|
120
|
-
<div>
|
121
|
-
User info:
|
122
|
-
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
123
|
-
<Await resolve={data.data}>
|
124
|
-
{user => {
|
125
|
-
return (
|
126
|
-
<div id="data">
|
127
|
-
name: {user.name}, age: {user.age}
|
128
|
-
</div>
|
129
|
-
);
|
130
|
-
}}
|
131
|
-
</Await>
|
132
|
-
</Suspense>
|
133
|
-
</div>
|
134
|
-
);
|
135
|
-
};
|
136
|
-
|
137
|
-
export default Page;
|
138
|
-
```
|
139
|
-
|
140
|
-
`<Await>` needs to be wrapped inside the `<Suspense>` component. The `resolve` function passed into `<Await>` is used to asynchronously retrieve data from a Data Loader. Once the data has been retrieved, it is rendered using [Render Props](https://reactjs.org/docs/render-props.html) mode. During the data retrieval phase, the content specified in the `fallback` property of `<Suspense>` will be displayed.
|
141
|
-
|
142
|
-
:::warning Warning
|
143
|
-
When importing types from a Data Loader file, it is necessary to use the `import type` syntax to ensure that only type information is imported. This can avoid packaging Data Loader code into the bundle file of the front-end product.
|
144
|
-
|
145
|
-
Therefore, the import method here is: `import type { Data } from './page.data'`;
|
146
|
-
:::
|
147
|
-
|
148
|
-
You can also retrieve asynchronous data returned by the Data Loader using `useAsyncValue`. For example:
|
149
|
-
|
150
|
-
```tsx title="page.tsx"
|
151
|
-
import { useAsyncValue } from '@modern-js/runtime/router';
|
152
|
-
|
153
|
-
// skip some codes
|
154
|
-
|
155
|
-
const UserInfo = () => {
|
156
|
-
const user = useAsyncValue();
|
157
|
-
|
158
|
-
return (
|
159
|
-
<div>
|
160
|
-
name: {user.name}, age: {user.age}
|
161
|
-
</div>
|
162
|
-
);
|
163
|
-
};
|
164
|
-
|
165
|
-
const Page = () => {
|
166
|
-
const data = useLoaderData() as Data;
|
167
|
-
|
168
|
-
return (
|
169
|
-
<div>
|
170
|
-
User info:
|
171
|
-
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
172
|
-
<Await resolve={data.data}>
|
173
|
-
<UserInfo />
|
174
|
-
</Await>
|
175
|
-
</Suspense>
|
176
|
-
</div>
|
177
|
-
);
|
178
|
-
};
|
179
|
-
|
180
|
-
export default Page;
|
181
|
-
```
|
182
|
-
|
183
|
-
### Error handling
|
184
|
-
|
185
|
-
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.
|
186
|
-
For example, we intentionally throw an error in the Data Loader function:
|
187
|
-
|
188
|
-
```ts title="page.data.ts"
|
189
|
-
import { defer } from '@modern-js/runtime/router';
|
190
|
-
|
191
|
-
export const loader = () => {
|
192
|
-
const data = new Promise((resolve, reject) => {
|
193
|
-
setTimeout(() => {
|
194
|
-
reject(new Error('error occurs'));
|
195
|
-
}, 200);
|
196
|
-
});
|
197
|
-
|
198
|
-
return defer({ data });
|
199
|
-
};
|
200
|
-
```
|
201
|
-
|
202
|
-
Then use `useAsyncError` to get the error, and assign the component used to render the error to the `errorElement` property of the `<Await>` component:
|
203
|
-
|
204
|
-
```tsx title="page.ts"
|
205
|
-
import { Await, useAsyncError, useLoaderData } from '@modern-js/runtime/router';
|
206
|
-
import { Suspense } from 'react';
|
207
|
-
|
208
|
-
export default function Page() {
|
209
|
-
const data = useLoaderData();
|
210
|
-
|
211
|
-
return (
|
212
|
-
<div>
|
213
|
-
Error page
|
214
|
-
<Suspense fallback={<div>loading ...</div>}>
|
215
|
-
<Await resolve={data.data} errorElement={<ErrorElement />}>
|
216
|
-
{(data: any) => {
|
217
|
-
return <div>never displayed</div>;
|
218
|
-
}}
|
219
|
-
</Await>
|
220
|
-
</Suspense>
|
221
|
-
</div>
|
222
|
-
);
|
223
|
-
}
|
224
|
-
|
225
|
-
function ErrorElement() {
|
226
|
-
const error = useAsyncError() as Error;
|
227
|
-
return <p>Something went wrong! {error.message}</p>;
|
228
|
-
}
|
229
|
-
```
|
230
|
-
|
231
|
-
## Waiting for all content to load for spiders
|
232
|
-
|
233
|
-
Streaming offers a better user experience because the user can see the content as it becomes available.
|
234
|
-
|
235
|
-
However, when a spider visits your page, you might want to let all of the content load first and then produce the final HTML output instead of revealing it progressively.
|
236
|
-
|
237
|
-
Modern.js uses [isbot](https://www.npmjs.com/package/isbot) to examine the user-agent of requests, determining whether they come from a crawler.
|
238
|
-
|
239
|
-
:::info More
|
240
|
-
|
241
|
-
1. [Deferred Data](https://reactrouter.com/en/main/guides/deferred)
|
242
|
-
2. [New Suspense SSR Architecture in React 18](https://github.com/reactwg/react-18/discussions/37)
|
243
|
-
|
244
|
-
:::
|
245
|
-
|
246
|
-
import StreamSSRPerformance from '@site-docs/components/stream-ssr-performance';
|
247
|
-
|
248
|
-
<StreamSSRPerformance />
|