@modern-js/main-doc 0.0.0-nightly-20240827170702 → 0.0.0-nightly-20240829170706

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.
Files changed (39) hide show
  1. package/docs/en/apis/app/runtime/core/use-loader.mdx +1 -1
  2. package/docs/en/components/ssr-monitor.mdx +3 -0
  3. package/docs/en/configure/app/output/ssg.mdx +52 -141
  4. package/docs/en/guides/advanced-features/_meta.json +0 -7
  5. package/docs/en/guides/basic-features/_meta.json +7 -1
  6. package/docs/en/guides/basic-features/data/data-fetch.mdx +134 -235
  7. package/docs/en/guides/basic-features/data/data-write.mdx +66 -77
  8. package/docs/en/guides/basic-features/render/_meta.json +1 -0
  9. package/docs/en/guides/basic-features/render/ssg.mdx +208 -0
  10. package/docs/en/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +38 -50
  11. package/docs/en/guides/basic-features/render/ssr.mdx +301 -0
  12. package/docs/en/guides/basic-features/render/streaming-ssr.mdx +230 -0
  13. package/docs/en/guides/basic-features/routes.mdx +275 -263
  14. package/docs/en/guides/concept/entries.mdx +9 -2
  15. package/docs/zh/apis/app/runtime/core/use-loader.mdx +1 -1
  16. package/docs/zh/components/ssr-monitor.mdx +3 -0
  17. package/docs/zh/configure/app/output/ssg.mdx +49 -139
  18. package/docs/zh/guides/advanced-features/_meta.json +0 -7
  19. package/docs/zh/guides/basic-features/_meta.json +7 -1
  20. package/docs/zh/guides/basic-features/data/data-fetch.mdx +98 -213
  21. package/docs/zh/guides/basic-features/data/data-write.mdx +54 -55
  22. package/docs/zh/guides/basic-features/render/_meta.json +1 -0
  23. package/docs/zh/guides/basic-features/render/ssg.mdx +210 -0
  24. package/docs/zh/guides/{advanced-features/ssr/cache.mdx → basic-features/render/ssr-cache.mdx} +16 -26
  25. package/docs/zh/guides/basic-features/render/ssr.mdx +309 -0
  26. package/docs/zh/guides/{advanced-features/ssr/stream.mdx → basic-features/render/streaming-ssr.mdx} +22 -37
  27. package/docs/zh/guides/basic-features/routes.mdx +252 -237
  28. package/docs/zh/guides/concept/entries.mdx +6 -3
  29. package/package.json +6 -6
  30. package/docs/en/guides/advanced-features/ssg.mdx +0 -116
  31. package/docs/en/guides/advanced-features/ssr/_meta.json +0 -1
  32. package/docs/en/guides/advanced-features/ssr/index.mdx +0 -23
  33. package/docs/en/guides/advanced-features/ssr/stream.mdx +0 -248
  34. package/docs/en/guides/advanced-features/ssr/usage.mdx +0 -341
  35. package/docs/en/guides/advanced-features/ssr.mdx +0 -555
  36. package/docs/zh/guides/advanced-features/ssg.mdx +0 -116
  37. package/docs/zh/guides/advanced-features/ssr/_meta.json +0 -1
  38. package/docs/zh/guides/advanced-features/ssr/index.mdx +0 -23
  39. package/docs/zh/guides/advanced-features/ssr/usage.mdx +0 -329
@@ -0,0 +1,230 @@
1
+ # Streaming SSR
2
+
3
+ Streaming SSR is a new rendering method that updates the page content in real-time as users interact with it, enhancing user experience.
4
+
5
+ In conventional rendering, the page is rendered all at once. In streaming rendering, the page is rendered progressively, loading data step-by-step as users interact with the page instead of loading all data at once.
6
+
7
+ Compared to traditional rendering:
8
+
9
+ - **Faster Perceived Speed**: Streaming rendering can progressively display content, quickly rendering the home page.
10
+ - **Enhanced User Experience**: Users can see page content faster and interact without waiting for the entire page to render.
11
+ - **Better Performance Control**: Developers can better control the loading priority and order, optimizing performance and user experience.
12
+ - **Better Adaptability**: Streaming rendering adapts better to various network speeds and device performance, ensuring good performance across different environments.
13
+
14
+ ## Enabling Streaming Rendering
15
+
16
+ Modern.js supports React 18's streaming rendering, which can be enabled as follows:
17
+
18
+ ```ts title="modern.config.ts"
19
+ import { defineConfig } from '@modern-js/app-tools';
20
+
21
+ export default defineConfig({
22
+ server: {
23
+ ssr: {
24
+ mode: 'stream',
25
+ },
26
+ },
27
+ });
28
+ ```
29
+
30
+ Modern.js streaming rendering is based on React Router and involves several key APIs:
31
+
32
+ - [`defer`](https://reactrouter.com/en/main/utils/defer): Used in Data Loader to support asynchronous data fetching.
33
+ - [`Await`](https://reactrouter.com/en/main/components/await): Used to render the asynchronous data returned by the Data Loader.
34
+ - [`useAsyncValue`](https://reactrouter.com/en/main/hooks/use-async-value): Used to fetch data from the nearest parent `Await` component.
35
+
36
+ ## Fetching Data
37
+
38
+ ```ts title="user/[id]/page.data.ts"
39
+ import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
40
+
41
+ interface User {
42
+ name: string;
43
+ age: number;
44
+ }
45
+
46
+ export interface Data {
47
+ data: User;
48
+ }
49
+
50
+ export const loader = ({ params }: LoaderFunctionArgs) => {
51
+ const userId = params.id;
52
+
53
+ const user = new Promise<User>(resolve => {
54
+ setTimeout(() => {
55
+ resolve({
56
+ name: `user-${userId}`,
57
+ age: 18,
58
+ });
59
+ }, 200);
60
+ });
61
+
62
+ return defer({ data: user });
63
+ };
64
+ ```
65
+
66
+ Here, `user` is a Promise object representing asynchronously fetched data, processed using `defer`. Notice that `defer` must receive an object parameter; a direct Promise cannot be passed.
67
+
68
+ Additionally, `defer` can receive both asynchronous and synchronous data. In the example below, short-duration requests are returned using object data, while longer-duration requests are returned using a Promise:
69
+
70
+ ```ts title="user/[id]/page.data.ts"
71
+ export const loader = ({ params }: LoaderFunctionArgs) => {
72
+ const userId = params.id;
73
+
74
+ const user = new Promise<User>(resolve => {
75
+ setTimeout(() => {
76
+ resolve({
77
+ name: `user-${userId}`,
78
+ age: 18,
79
+ });
80
+ }, 2000);
81
+ });
82
+
83
+ const otherData = new Promise<string>(resolve => {
84
+ setTimeout(() => {
85
+ resolve('some sync data');
86
+ }, 200);
87
+ });
88
+
89
+ return defer({
90
+ data: user,
91
+ other: await otherData,
92
+ });
93
+ };
94
+ ```
95
+
96
+ This way, the application can prioritize displaying partially available content without waiting for the most time-consuming data requests.
97
+
98
+ ## Rendering Data
99
+
100
+ To render the asynchronous data returned by the Data Loader, use the `Await` component. For example:
101
+
102
+ ```tsx title="user/[id]/page.tsx"
103
+ import { Await, useLoaderData } from '@modern-js/runtime/router';
104
+ import { Suspense } from 'react';
105
+ import type { Data } from './page.data';
106
+
107
+ const Page = () => {
108
+ const data = useLoaderData() as Data;
109
+
110
+ return (
111
+ <div>
112
+ User info:
113
+ <Suspense fallback={<div id="loading">loading user data ...</div>}>
114
+ <Await resolve={data.data}>
115
+ {user => {
116
+ return (
117
+ <div id="data">
118
+ name: {user.name}, age: {user.age}
119
+ </div>
120
+ );
121
+ }}
122
+ </Await>
123
+ </Suspense>
124
+ </div>
125
+ );
126
+ };
127
+
128
+ export default Page;
129
+ ```
130
+
131
+ The `Await` component needs to be wrapped inside a `Suspense` component. The `resolve` prop of `Await` should be the asynchronously fetched data from the Data Loader. When the data is fetched, it will be rendered using the [Render Props](https://zh-hans.react.dev/reference/react/cloneElement#passing-data-with-a-render-prop) pattern. During data fetching, the content set by the `fallback` prop of `Suspense` is displayed.
132
+
133
+ :::warning Warning
134
+ When importing types from the `page.data.ts` file, use `import type` to ensure only type information is imported, preventing Data Loader code from being bundled into the frontend.
135
+ :::
136
+
137
+ In the component, you can also fetch asynchronous data returned by the Data Loader using `useAsyncValue`. For example:
138
+
139
+ ```tsx title='page.tsx'
140
+ import { useAsyncValue } from '@modern-js/runtime/router';
141
+
142
+ const UserInfo = () => {
143
+ const user = useAsyncValue();
144
+ return (
145
+ <div>
146
+ name: {user.name}, age: {user.age}
147
+ </div>
148
+ );
149
+ };
150
+
151
+ const Page = () => {
152
+ const data = useLoaderData() as Data;
153
+ return (
154
+ <div>
155
+ User info:
156
+ <Suspense fallback={<div id="loading">loading user data ...</div>}>
157
+ <Await resolve={data.data}>
158
+ <UserInfo />
159
+ </Await>
160
+ </Suspense>
161
+ </div>
162
+ );
163
+ };
164
+
165
+ export default Page;
166
+ ```
167
+
168
+ ## Error Handling
169
+
170
+ The `errorElement` prop of the `Await` component handles errors in Data Loader or sub-component rendering. For example, intentionally throwing an error in the Data Loader function:
171
+
172
+ ```ts title="page.loader.ts"
173
+ import { defer } from '@modern-js/runtime/router';
174
+
175
+ export default () => {
176
+ const data = new Promise((resolve, reject) => {
177
+ setTimeout(() => {
178
+ reject(new Error('error occurs'));
179
+ }, 200);
180
+ });
181
+
182
+ return defer({ data });
183
+ };
184
+ ```
185
+
186
+ Then, fetch the error using `useAsyncError` and set a component to render the error message for the `errorElement` prop of the `Await` component:
187
+
188
+ ```tsx title="page.ts"
189
+ import { Await, useAsyncError, useLoaderData } from '@modern-js/runtime/router';
190
+ import { Suspense } from 'react';
191
+
192
+ export default function Page() {
193
+ const data = useLoaderData();
194
+
195
+ return (
196
+ <div>
197
+ Error page
198
+ <Suspense fallback={<div>loading ...</div>}>
199
+ <Await resolve={data.data} errorElement={<ErrorElement />}>
200
+ {(data: any) => {
201
+ return <div>never displayed</div>;
202
+ }}
203
+ </Await>
204
+ </Suspense>
205
+ </div>
206
+ );
207
+ }
208
+
209
+ function ErrorElement() {
210
+ const error = useAsyncError() as Error;
211
+ return <p>Something went wrong! {error.message}</p>;
212
+ }
213
+ ```
214
+
215
+ ## Waiting for All Content to Load for Crawlers
216
+
217
+ Streaming can enhance user experience by allowing users to perceive content as it becomes available.
218
+
219
+ However, when a crawler visits the page, it might need to load all content and output the entire HTML at once, rather than progressively loading it.
220
+
221
+ Modern.js uses [isbot](https://www.npmjs.com/package/isbot) to determine if a request is from a crawler based on the `user-agent` header.
222
+
223
+ import StreamSSRPerformance from '@site-docs/components/stream-ssr-performance';
224
+
225
+ <StreamSSRPerformance />
226
+
227
+ ## Related Documentation
228
+
229
+ 1. [Deferred Data](https://reactrouter.com/en/main/guides/deferred)
230
+ 2. [New Suspense SSR Architecture in React 18](https://github.com/reactwg/react-18/discussions/37)