@modern-js/main-doc 0.0.0-nightly-20241106064312 → 0.0.0-nightly-20241107170702
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/docs/en/apis/app/runtime/core/use-runtime-context.mdx +86 -33
- package/docs/en/guides/concept/entries.mdx +58 -5
- package/docs/en/guides/get-started/tech-stack.mdx +1 -1
- package/docs/zh/apis/app/runtime/core/use-runtime-context.mdx +86 -33
- package/docs/zh/guides/concept/entries.mdx +52 -4
- package/docs/zh/guides/get-started/tech-stack.mdx +1 -1
- package/package.json +2 -2
- package/src/i18n/enUS.ts +0 -7
- package/src/i18n/zhCN.ts +0 -7
- package/src/pages/index.tsx +3 -32
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: useRuntimeContext
|
|
3
|
-
---
|
|
4
1
|
# useRuntimeContext
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
This function is primarily used to obtain the runtime context and can only be used in the function component.
|
|
7
4
|
|
|
8
5
|
## Usage
|
|
9
6
|
|
|
@@ -12,7 +9,7 @@ import { useRuntimeContext } from '@modern-js/runtime';
|
|
|
12
9
|
|
|
13
10
|
export function App() {
|
|
14
11
|
const runtimeContext = useRuntimeContext();
|
|
15
|
-
return <div>Hello World</div
|
|
12
|
+
return <div>Hello World</div>
|
|
16
13
|
}
|
|
17
14
|
```
|
|
18
15
|
|
|
@@ -20,44 +17,100 @@ export function App() {
|
|
|
20
17
|
|
|
21
18
|
```ts
|
|
22
19
|
type RuntimeContext = {
|
|
23
|
-
|
|
24
|
-
params: Record<string, string>;
|
|
25
|
-
pathname: string;
|
|
26
|
-
query: Record<string, string>;
|
|
27
|
-
headers: IncomingHttpHeaders;
|
|
28
|
-
cookie: string;
|
|
29
|
-
};
|
|
30
|
-
store: ReduckStore;
|
|
31
|
-
router: RemixRouter;
|
|
20
|
+
context: RequestContext;
|
|
32
21
|
};
|
|
33
|
-
|
|
34
|
-
function useRuntimeContext(): RuntimeContext;
|
|
35
22
|
```
|
|
36
23
|
|
|
37
|
-
###
|
|
24
|
+
### context
|
|
38
25
|
|
|
39
|
-
|
|
40
|
-
- `params`: dynamic parameters in the request path.
|
|
41
|
-
- `pathname`: the pathname of the request.
|
|
42
|
-
- `query`: the query of the request.
|
|
43
|
-
- `headers`: the header info of the request.
|
|
44
|
-
- `cookie`: the cookie of the request.
|
|
45
|
-
- `store`: when the `runtime.state` is enabled, this value is the Reduck global `store`.
|
|
46
|
-
- `router`: When the `runtime.router` is enabled, this value exists.
|
|
47
|
-
- `location`: The current location reflected by the router. The same as [`useLocation`] the return value of (/apis/app/runtime/router/router.html#uselocation).
|
|
48
|
-
- `navigate`: Navigate to the given path. The same as the return value of [`useNavigate`](/apis/app/runtime/router/router.html#usenavigate).
|
|
26
|
+
Used to get [Request Context](#request-context).
|
|
49
27
|
|
|
50
28
|
## Example
|
|
51
29
|
|
|
52
|
-
|
|
53
|
-
import { useRuntimeContext } from '@modern-js/runtime';
|
|
54
|
-
import { fooModel } from '@/common/models';
|
|
30
|
+
### Distinguish the runtime environment
|
|
55
31
|
|
|
32
|
+
```ts
|
|
56
33
|
function App() {
|
|
57
|
-
const {
|
|
34
|
+
const { context } = useRuntimeContext();
|
|
58
35
|
|
|
59
|
-
|
|
36
|
+
if (context.isBrowser === true) {
|
|
37
|
+
// browser-side execution logic
|
|
38
|
+
console.log('browser render')
|
|
39
|
+
} else {
|
|
40
|
+
// The server-side executes logic, which can access the unique 'logger' attribute
|
|
41
|
+
context.logger.info('server render')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
60
45
|
|
|
61
|
-
|
|
46
|
+
### Request context
|
|
47
|
+
|
|
48
|
+
When SSR is enabled, uniform request contexts can be obtained in both the node environment and the browser-side environment.
|
|
49
|
+
|
|
50
|
+
The slightly different is that the node environment also supports setting response headers, response codes, and provides Logger logs and Metrics management.
|
|
51
|
+
|
|
52
|
+
:::tip
|
|
53
|
+
when ssr is disabled, only the part of information that can be obtained in the browser environment is included.
|
|
54
|
+
|
|
55
|
+
:::
|
|
56
|
+
|
|
57
|
+
import { Tabs, Tab as TabItem } from "@theme";
|
|
58
|
+
|
|
59
|
+
<Tabs
|
|
60
|
+
defaultValue="RequestContext"
|
|
61
|
+
values={[
|
|
62
|
+
{ label: 'RequestContext', value: 'RequestContext', },
|
|
63
|
+
{ label: 'ServerContext', value: 'ServerContext', },
|
|
64
|
+
{ label: 'ClientContext', value: 'ClientContext', },
|
|
65
|
+
]
|
|
66
|
+
}>
|
|
67
|
+
<TabItem value="RequestContext">
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
type RequestContext = ServerContext | ClientContext;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
</TabItem>
|
|
74
|
+
<TabItem value="ServerContext">
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
interface ServerContext {
|
|
78
|
+
isBrowser: false;
|
|
79
|
+
request: {
|
|
80
|
+
userAgent: string;
|
|
81
|
+
cookie: string;
|
|
82
|
+
cookieMap: Record<string, any>;
|
|
83
|
+
query: Record<string, any>;
|
|
84
|
+
url: string;
|
|
85
|
+
host: string;
|
|
86
|
+
headers?: IncomingHttpHeaders;
|
|
87
|
+
};
|
|
88
|
+
response: {
|
|
89
|
+
setHeader: (key: string, value: string) => void;
|
|
90
|
+
status: (code: number) => void;
|
|
91
|
+
}
|
|
92
|
+
logger: Logger;
|
|
93
|
+
metrics: Metrics;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
</TabItem>
|
|
98
|
+
<TabItem value="ClientContext">
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
interface ClientContext {
|
|
102
|
+
isBrowser: true;
|
|
103
|
+
request: {
|
|
104
|
+
userAgent: string;
|
|
105
|
+
cookie: string;
|
|
106
|
+
cookieMap: Record<string, any>;
|
|
107
|
+
query: Record<string, any>;
|
|
108
|
+
url: string;
|
|
109
|
+
host: string;
|
|
110
|
+
headers?: IncomingHttpHeaders;
|
|
111
|
+
};
|
|
62
112
|
}
|
|
63
113
|
```
|
|
114
|
+
|
|
115
|
+
</TabItem>
|
|
116
|
+
</Tabs>
|
|
@@ -12,7 +12,7 @@ In this chapter, you will learn about the entry convention in Modern.js and how
|
|
|
12
12
|
|
|
13
13
|
In a Modern.js project, each entry corresponds to an independent page and a server-side route. By default, Modern.js automatically determines the entry of a page based on directory conventions, but also supports customizing the entry through configuration options.
|
|
14
14
|
|
|
15
|
-
Many configuration options provided by Modern.js are divided by entry, such as page title, HTML template, page meta information, whether to enable SSR/SSG, server-side routing rules, etc.
|
|
15
|
+
Many configuration options provided by Modern.js are divided by entry, such as page title, HTML template, page meta information, whether to enable SSR/SSG, server-side routing rules, etc. If you want to learn more about the technical details of entries, please refer to the [In-Depth](#in-depth) section.
|
|
16
16
|
|
|
17
17
|
## Single Entry and Multiple Entries
|
|
18
18
|
|
|
@@ -169,7 +169,7 @@ const ModernRoot = createRoot();
|
|
|
169
169
|
render(<ModernRoot />);
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
In the code above, the component returned by the `createRoot` function is either the component generated from the `routes/` directory or the component exported by `App.tsx`.
|
|
172
|
+
In the code above, the component returned by the `createRoot` function is either the component generated from the `routes/` directory or the component exported by `App.tsx`.
|
|
173
173
|
|
|
174
174
|
The `render` function is used to handle rendering and mounting of the component. For example, if you want to execute some asynchronous tasks before rendering, you can achieve it like this:
|
|
175
175
|
|
|
@@ -245,10 +245,63 @@ export default defineConfig({
|
|
|
245
245
|
});
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
It is worth noting that, by default, Modern.js considers entries specified through the configuration as **framework mode entries** and will automatically generate the actual compilation entry.
|
|
248
|
+
It is worth noting that, by default, Modern.js considers entries specified through the configuration as **framework mode entries** and will automatically generate the actual compilation entry.
|
|
249
249
|
|
|
250
250
|
If your application is migrating from build tools like Webpack or Vite to the Modern.js framework, you typically need to enable the `disableMount` option in the entry configuration. In this case, Modern.js will treat the entry as a **build mode entry**.
|
|
251
251
|
|
|
252
|
+
## In-Depth
|
|
253
|
+
|
|
254
|
+
The concept of page entry is derived from the concept of [Entrypoint](https://webpack.js.org/concepts/entry-points/) in webpack. It is mainly used to configure the JavaScript or any other modules to be executed during the application startup. Webpack usually corresponds each entry to an HTML file in the output. The modules imported by the entry will be bundled and split into multiple chunks in the output. For a JavaScript module, it might be compiled into several chunks like `dist/static/js/index.ea39u8.js`.
|
|
255
|
+
|
|
256
|
+
Here's a summary of the differences between the concepts of entry and route:
|
|
257
|
+
|
|
258
|
+
- **Entry**:Contains multiple modules to be executed during application startup.
|
|
259
|
+
- **Client Router**:In Modern.js, it is usually implemented by `react-router`, determining which React component to load and render based on the browser's current URL using the History API.
|
|
260
|
+
- **Server Router**:The server can mimic the behavior of [devServer](https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback), replacing all 404 responses with the index.html page to implement client-side routing, or implement any routing logic as needed.
|
|
261
|
+
|
|
262
|
+
Their relationships are as follows:
|
|
263
|
+
|
|
264
|
+
- Each webpack website project can contain multiple entries.
|
|
265
|
+
- Each entry contains several modules (source files).
|
|
266
|
+
- Each entry usually corresponds to an HTML file in the output.
|
|
267
|
+
- Each HTML file can contain multiple client-side routing solutions (for example, using `react-router` and `@tanstack/react-router` in the same page).
|
|
268
|
+
- Each HTML file can be mapped to multiple server-side routes.
|
|
269
|
+
- Each HTML file can contain multiple client-side routing solutions, and when accessing different routes of a single-entry application, the same HTML file is actually used.
|
|
270
|
+
|
|
271
|
+
## Troubleshooting
|
|
272
|
+
|
|
273
|
+
1. **Does each `react-router` defined client route generate a separate HTML file?**
|
|
274
|
+
|
|
275
|
+
No. Each entry usually only generates one HTML file, and if a single entry contains multiple client routing systems, it will share the same HTML file.
|
|
276
|
+
|
|
277
|
+
2. **Does the convention routing project in the `routes/` directory generate multiple HTML files?**
|
|
278
|
+
|
|
279
|
+
No. Modern.js will scan the `routes/` directory during startup and automatically generate client-side routes based on the file conventions. The HTML file generated corresponds to the `routes/` directory.
|
|
280
|
+
|
|
281
|
+
3. **Does the Server Side Rendering (SSR) project generate multiple HTML files in the output?**
|
|
282
|
+
|
|
283
|
+
A Server Side Rendering (SSR) project does not necessarily need to generate an HTML file in the output. It can only include the server-side JavaScript output. At this point, the `react-router` routing will be executed on the server side, and the HTML content will be rendered and responded while a request is triggered.
|
|
284
|
+
|
|
285
|
+
At the same time, Modern.js will also generate a complete client-side HTML file for each entry in the output, which can be used to fallback to client-side rendering when the SSR fails.
|
|
286
|
+
|
|
287
|
+
Another special case is a Single Entry Static Site Generation (SSG) project, even if it is built with a convention routing, Modern.js will also generate an HTML file for each `page.tsx` file.
|
|
288
|
+
|
|
289
|
+
Note that even if SSR is used, React still needs to go through the hydration phase, so the routing defined by `react-router` will still be executed on the client side.
|
|
290
|
+
|
|
291
|
+
4. **What are the exceptions to generating multiple HTML files?**
|
|
292
|
+
|
|
293
|
+
You can configure [html-rspack-plugin](https://rspack.dev/plugins/rspack/html-rspack-plugin#generate-multiple-html-files) to generate multiple HTML files for each entry, or let multiple entries share the same HTML file.
|
|
294
|
+
|
|
295
|
+
5. **What is a Multi-Page Application (MPA)?**
|
|
296
|
+
|
|
297
|
+
The "page" in a Multi-Page Application (MPA) refers to a static HTML file.
|
|
298
|
+
|
|
299
|
+
Generally, any application that outputs multiple entries and multiple HTML files can be called a Multi-Page Application.
|
|
300
|
+
|
|
301
|
+
Narrowly speaking, a Multi-Page Application does not contain client-side routing, and navigation between pages is usually achieved through elements like `<a>` tags. But in practice, Multi-Page Applications also often need to configure client-side routing to meet different needs.
|
|
302
|
+
|
|
303
|
+
Conversely, a application with `react-router` defined routes that generates only one HTML file is called a Single-Page Application (SPA).
|
|
304
|
+
|
|
252
305
|
## Deprecated
|
|
253
306
|
|
|
254
307
|
Currently, if the entry directory meets the following conditions, it will also be considered an application entry:
|
|
@@ -262,7 +315,7 @@ The `index.[jt]sx?` file supported **Custom Bootstrap** and **Build Mode Entry**
|
|
|
262
315
|
|
|
263
316
|
### Custom Bootstrap
|
|
264
317
|
|
|
265
|
-
When there is an `index.[jt]sx` file in the entry, and the file's default export is a function, Modern.js will pass the default `bootstrap` function as an argument and use the exported function to replace the default `bootstrap`.
|
|
318
|
+
When there is an `index.[jt]sx` file in the entry, and the file's default export is a function, Modern.js will pass the default `bootstrap` function as an argument and use the exported function to replace the default `bootstrap`.
|
|
266
319
|
|
|
267
320
|
This allows developers to customize mounting components to DOM or add custom behaviors before mounting. For example:
|
|
268
321
|
|
|
@@ -277,4 +330,4 @@ export default (App: React.ComponentType, bootstrap: () => void) => {
|
|
|
277
330
|
|
|
278
331
|
### Build Mode Entry
|
|
279
332
|
|
|
280
|
-
When an `index.[jt]sx` file exists in the entry directory and does not export a function via export default, this entry will also be considered a build mode entry.
|
|
333
|
+
When an `index.[jt]sx` file exists in the entry directory and does not export a function via export default, this entry will also be considered a build mode entry.
|
|
@@ -12,7 +12,7 @@ In this document, you can learn about the main technology stack involved in the
|
|
|
12
12
|
|
|
13
13
|
Modern.js uses [React 18](https://react.dev/) to build user interfaces and is also compatible with React 17.
|
|
14
14
|
|
|
15
|
-
Rsbuild supports building Vue applications. If you need to use Vue, you can refer to ["Rsbuild - Vue"](https://rsbuild.dev/guide/framework/
|
|
15
|
+
Rsbuild supports building Vue applications. If you need to use Vue, you can refer to ["Rsbuild - Vue"](https://rsbuild.dev/guide/framework/vue).
|
|
16
16
|
|
|
17
17
|
## Routing
|
|
18
18
|
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: useRuntimeContext
|
|
3
|
-
---
|
|
4
1
|
# useRuntimeContext
|
|
5
2
|
|
|
6
3
|
该函数主要用于获取 Runtime 上下文,只能在函数组件中使用。
|
|
@@ -12,7 +9,7 @@ import { useRuntimeContext } from '@modern-js/runtime';
|
|
|
12
9
|
|
|
13
10
|
export function App() {
|
|
14
11
|
const runtimeContext = useRuntimeContext();
|
|
15
|
-
return <div>Hello World</div
|
|
12
|
+
return <div>Hello World</div>
|
|
16
13
|
}
|
|
17
14
|
```
|
|
18
15
|
|
|
@@ -20,44 +17,100 @@ export function App() {
|
|
|
20
17
|
|
|
21
18
|
```ts
|
|
22
19
|
type RuntimeContext = {
|
|
23
|
-
|
|
24
|
-
params: Record<string, string>;
|
|
25
|
-
pathname: string;
|
|
26
|
-
query: Record<string, string>;
|
|
27
|
-
headers: IncomingHttpHeaders;
|
|
28
|
-
cookie: string;
|
|
29
|
-
};
|
|
30
|
-
store: ReduckStore;
|
|
31
|
-
router: RemixRouter;
|
|
20
|
+
context: RequestContext;
|
|
32
21
|
};
|
|
33
|
-
|
|
34
|
-
function useRuntimeContext(): RuntimeContext;
|
|
35
22
|
```
|
|
36
23
|
|
|
37
|
-
###
|
|
24
|
+
### context
|
|
38
25
|
|
|
39
|
-
|
|
40
|
-
- `params`:请求路径中的动态参数。
|
|
41
|
-
- `pathname`:请求的 pathname。
|
|
42
|
-
- `query`:请求的查询字符串对象。
|
|
43
|
-
- `headers`:请求头信息。
|
|
44
|
-
- `cookie`:请求的 cookie 信息。
|
|
45
|
-
- `store`:在开启了 state 插件的时候,该值为 Reduck 全局 `store`。
|
|
46
|
-
- `router`:在开启 router 插件的时候存在。
|
|
47
|
-
- `location`:当前路由对应的位置信息。同 [`useLocation`](/apis/app/runtime/router/router.html#uselocation) 返回值。
|
|
48
|
-
- `navigate`:导航到给定路径。同 [`useNavigate`](/apis/app/runtime/router/router.html#usenavigate) 返回值。
|
|
26
|
+
用于获取[请求上下文](#请求上下文)。
|
|
49
27
|
|
|
50
|
-
##
|
|
28
|
+
## 使用示例
|
|
51
29
|
|
|
52
|
-
|
|
53
|
-
import { useRuntimeContext } from '@modern-js/runtime';
|
|
54
|
-
import { fooModel } from '@/common/models';
|
|
30
|
+
### 区分运行环境
|
|
55
31
|
|
|
32
|
+
```ts
|
|
56
33
|
function App() {
|
|
57
|
-
const {
|
|
34
|
+
const { context } = useRuntimeContext();
|
|
35
|
+
|
|
36
|
+
if (context.isBrowser === true) {
|
|
37
|
+
// 浏览器端执行逻辑
|
|
38
|
+
console.log('browser render')
|
|
39
|
+
} else {
|
|
40
|
+
// 服务器端执行逻辑 logger 功能需开启
|
|
41
|
+
context.logger.info('server render')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 请求上下文
|
|
47
|
+
|
|
48
|
+
开启 SSR 时,在 Node 环境和浏览器端环境可以获取到同构的请求上下文。
|
|
49
|
+
|
|
50
|
+
稍有不同的是 Node 环境还支持设置响应头、响应码,并提供了 Logger 日志与 Metrics 打点。
|
|
51
|
+
|
|
52
|
+
:::tip
|
|
53
|
+
当 SSR 未开启时,仅包含可在浏览器端获取的部分信息。
|
|
54
|
+
|
|
55
|
+
:::
|
|
56
|
+
|
|
57
|
+
import { Tabs, Tab as TabItem } from "@theme";
|
|
58
|
+
|
|
59
|
+
<Tabs
|
|
60
|
+
defaultValue="RequestContext"
|
|
61
|
+
values={[
|
|
62
|
+
{ label: 'RequestContext', value: 'RequestContext', },
|
|
63
|
+
{ label: 'ServerContext', value: 'ServerContext', },
|
|
64
|
+
{ label: 'ClientContext', value: 'ClientContext', },
|
|
65
|
+
]
|
|
66
|
+
}>
|
|
67
|
+
<TabItem value="RequestContext">
|
|
58
68
|
|
|
59
|
-
|
|
69
|
+
```ts
|
|
70
|
+
type RequestContext = ServerContext | ClientContext;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
</TabItem>
|
|
74
|
+
<TabItem value="ServerContext">
|
|
60
75
|
|
|
61
|
-
|
|
76
|
+
```ts
|
|
77
|
+
interface ServerContext {
|
|
78
|
+
isBrowser: false;
|
|
79
|
+
request: {
|
|
80
|
+
userAgent: string;
|
|
81
|
+
cookie: string;
|
|
82
|
+
cookieMap: Record<string, any>;
|
|
83
|
+
query: Record<string, any>;
|
|
84
|
+
url: string;
|
|
85
|
+
host: string;
|
|
86
|
+
headers?: IncomingHttpHeaders;
|
|
87
|
+
};
|
|
88
|
+
response: {
|
|
89
|
+
setHeader: (key: string, value: string) => void;
|
|
90
|
+
status: (code: number) => void;
|
|
91
|
+
};
|
|
92
|
+
logger: Logger;
|
|
93
|
+
metrics: Metrics;
|
|
62
94
|
}
|
|
63
95
|
```
|
|
96
|
+
|
|
97
|
+
</TabItem>
|
|
98
|
+
<TabItem value="ClientContext">
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
interface ClientContext {
|
|
102
|
+
isBrowser: true;
|
|
103
|
+
request: {
|
|
104
|
+
userAgent: string;
|
|
105
|
+
cookie: string;
|
|
106
|
+
cookieMap: Record<string, any>;
|
|
107
|
+
query: Record<string, any>;
|
|
108
|
+
url: string;
|
|
109
|
+
host: string;
|
|
110
|
+
headers?: IncomingHttpHeaders;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
</TabItem>
|
|
116
|
+
</Tabs>
|
|
@@ -12,7 +12,7 @@ sidebar_position: 1
|
|
|
12
12
|
|
|
13
13
|
在 Modern.js 应用中,每一个入口对应一个独立的页面,也对应一条服务端路由。默认情况下,Modern.js 会基于目录约定来自动确定页面的入口,同时也支持通过配置项来自定义入口。
|
|
14
14
|
|
|
15
|
-
Modern.js 提供的很多配置项都是以入口为维度进行划分的,比如页面标题、HTML 模板、页面 Meta 信息、是否开启 SSR/SSG
|
|
15
|
+
Modern.js 提供的很多配置项都是以入口为维度进行划分的,比如页面标题、HTML 模板、页面 Meta 信息、是否开启 SSR/SSG、服务端路由规则等。如果你希望了解更多关于入口的技术细节,请参考[深入了解](#深入了解)章节的内容。
|
|
16
16
|
|
|
17
17
|
## 单入口与多入口
|
|
18
18
|
|
|
@@ -239,6 +239,57 @@ export default defineConfig({
|
|
|
239
239
|
值得注意的是,默认情况下,Modern.js 认为通过配置指定的入口是**框架模式入口**,将自动生成真正的编译入口。如果你的应用是从 Webpack 或 Vite 等构建工具迁移到 Modern.js 框架时,你通常需要在入口配置中开启 `disableMount` 选项,此时 Modern.js 认为该入口是**构建模式入口**。
|
|
240
240
|
|
|
241
241
|
|
|
242
|
+
## 深入了解
|
|
243
|
+
|
|
244
|
+
页面入口的概念衍生自 webpack 的入口(Entrypoint)概念,其主要用于配置 JavaScript 或其他模块在应用启动时加载和执行。webpack 对于网页应用的 [最佳实践](https://webpack.docschina.org/concepts/entry-points/#multi-page-application) 通常将入口与 HTML 产物对应,即每增加一个入口最终就会在产物中生成一份对应的 HTML 文件。入口引入的模块会在编译打包后生成多个 Chunk 产物,例如对于 JavaScript 模块最终可能会生成数个类似 `dist/static/js/index.ea39u8.js` 的文件产物。
|
|
245
|
+
|
|
246
|
+
需要注意区分入口、路由等概念之间的关系:
|
|
247
|
+
|
|
248
|
+
- **入口**:包含多个用于启动时执行的模块。
|
|
249
|
+
- **客户端路由**:在 Modern.js 中通常由 `react-router` 实现,通过 History API 判断浏览器当前 URL 决定加载和显示哪个 React 组件。
|
|
250
|
+
- **服务端路由**:服务端可以模仿 [devServer 的行为](https://webpack.docschina.org/configuration/dev-server/#devserverhistoryapifallback),将 index.html 页面代替所有 404 响应被返回以实现客户端路由,也可以自行实现任何路由逻辑。
|
|
251
|
+
|
|
252
|
+
它们的对应关系如下:
|
|
253
|
+
|
|
254
|
+
- 每个 webpack 网站项目可以包含多个入口
|
|
255
|
+
- 每个入口包含若干个模块(源码文件)
|
|
256
|
+
- 每个入口通常对应一个 HTML 文件产物和若干其它产物。
|
|
257
|
+
- 每个 HTML 文件可以包含多个客户端路由方案(比如在页面中同时使用 `react-router` 和 `@tanstack/react-router`)。
|
|
258
|
+
- 每个 HTML 文件可以被多个服务端路由对应。
|
|
259
|
+
- 每个 HTML 文件可以包含多个客户端路由,当访问单入口应用的不同路由时实际使用的是同一个 HTML 文件。
|
|
260
|
+
|
|
261
|
+
## 常见问题
|
|
262
|
+
|
|
263
|
+
1. **`react-router` 定义的每个客户端路由会分别生成一个 HTML 文件吗?**
|
|
264
|
+
|
|
265
|
+
不会。每个入口通常只会生成一个 HTML 文件,单个入口中如果定义多个客户端路由系统会共用这一个 HTML 文件。
|
|
266
|
+
|
|
267
|
+
2. **约定式路由的 `routes/` 目录下每个 `page.tsx` 文件都会生成一个 HTML 文件吗?**
|
|
268
|
+
|
|
269
|
+
不是。约定式路由是基于 `react-router` 实现的客户端路由方案,其约定 `routes/` 目录下每个 `page.tsx` 文件都会对应生成一个 `react-router` 的客户端路由。`routes/` 本身作为一个页面入口,对应最终产物中的一个 HTML 文件。
|
|
270
|
+
|
|
271
|
+
3. **服务端渲染(SSR)的项目是否会构建多份 HTML 产物?**
|
|
272
|
+
|
|
273
|
+
在使用服务端渲染应用时并不必须在编译时生成一份 HTML 产物,它可以只包含用于渲染的服务端 JavaScript 产物。此时 `react-router` 将在服务端运行和调度路由,并在每次请求时渲染并响应 HTML 内容。
|
|
274
|
+
|
|
275
|
+
而 Modern.js 在编译时仍会为每个入口生成包含 HTML 文件的完整的客户端产物,用于在服务端渲染失败时降级为客户端渲染使用。
|
|
276
|
+
|
|
277
|
+
另一个特殊情况是使用静态站点生成(SSG)的项目,即使是使用约定式路由搭建的单入口 SSG 应用,Modern.js 也会在 webpack 的流程外为每个 `page.tsx` 文件生成一份单独的 HTML 文件。
|
|
278
|
+
|
|
279
|
+
需要注意的是即使开启服务端渲染,React 通常仍需要执行水合阶段并在前端执行 `react-router` 的路由。
|
|
280
|
+
|
|
281
|
+
4. **单入口应用是否存在输出多个 HTML 文件的例外情况?**
|
|
282
|
+
|
|
283
|
+
你可以自行配置 [html-rspack-plugin](https://rspack.dev/zh/plugins/rspack/html-rspack-plugin#%E7%94%9F%E6%88%90%E5%A4%9A%E4%B8%AA-html-%E6%96%87%E4%BB%B6) 为每个入口生成多个 HTML 产物,或使多个入口共用一个 HTML 产物。
|
|
284
|
+
|
|
285
|
+
5. **什么叫多页应用(Multi-Page Application)?**
|
|
286
|
+
|
|
287
|
+
多页应用的 “页面” 指的是静态的 HTML 文件。
|
|
288
|
+
一般可以将任何包含多个入口、多个 HTML 文件产物的网页应用称为多页应用。
|
|
289
|
+
狭义的多页应用可能不包含客户端路由、仅通过 `<a>` 之类的标签元素进行 HTML 静态页面之间的跳转,但实践中上多页应用也经常需要为其入口配置客户端路由以满足不同需求。
|
|
290
|
+
|
|
291
|
+
相反地,通过 `react-router` 定义多个路由的单入口应用因为只生成一个 HTML 文件产物,所以被称为单页应用(Single Page Application)。
|
|
292
|
+
|
|
242
293
|
## 弃用功能
|
|
243
294
|
|
|
244
295
|
目前,如果入口所在的目录满足以下条件,也会成为应用入口。
|
|
@@ -267,6 +318,3 @@ export default (App: React.ComponentType, bootstrap: () => void) => {
|
|
|
267
318
|
### 构建模式入口
|
|
268
319
|
|
|
269
320
|
当入口目录中存在 `index.[jt]sx`(即将废弃) 并且没有通过 `export default` 导出函数时,该入口也将被认为是构建模式入口。
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
@@ -12,7 +12,7 @@ Modern.js 框架默认集成了一些社区中流行的库和开发工具。
|
|
|
12
12
|
|
|
13
13
|
Modern.js 使用 [React 18](https://react.dev/) 来构建用户界面,同时也兼容 React 17。
|
|
14
14
|
|
|
15
|
-
Modern.js 底层的 Rsbuild 支持构建 Vue 应用,如果你需要使用 Vue,可以参考 [Rsbuild - Vue](https://rsbuild.dev/zh/guide/framework/
|
|
15
|
+
Modern.js 底层的 Rsbuild 支持构建 Vue 应用,如果你需要使用 Vue,可以参考 [Rsbuild - Vue](https://rsbuild.dev/zh/guide/framework/vue)。
|
|
16
16
|
|
|
17
17
|
## 路由
|
|
18
18
|
|
package/package.json
CHANGED
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
"modern",
|
|
16
16
|
"modern.js"
|
|
17
17
|
],
|
|
18
|
-
"version": "0.0.0-nightly-
|
|
18
|
+
"version": "0.0.0-nightly-20241107170702",
|
|
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": "0.0.0-nightly-
|
|
25
|
+
"@modern-js/sandpack-react": "0.0.0-nightly-20241107170702"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@rspress/shared": "1.35.3",
|
package/src/i18n/enUS.ts
CHANGED
|
@@ -23,13 +23,6 @@ export const EN_US = {
|
|
|
23
23
|
featureDesc6:
|
|
24
24
|
'Launch with zero configuration, then everything is configurable.',
|
|
25
25
|
|
|
26
|
-
// Solutions
|
|
27
|
-
solutions: 'Solutions',
|
|
28
|
-
solutionsDesc1: 'A progressive React framework for web development.',
|
|
29
|
-
solutionsDesc2: 'A powerful solution for npm package development.',
|
|
30
|
-
solutionsDesc3: 'An Rspack-based build tool for web development.',
|
|
31
|
-
solutionsDesc4: 'A fast Rspack-based static site generator',
|
|
32
|
-
|
|
33
26
|
// Footer
|
|
34
27
|
guide: 'Guide',
|
|
35
28
|
topic: 'Topic',
|
package/src/i18n/zhCN.ts
CHANGED
|
@@ -23,13 +23,6 @@ export const ZH_CN: Record<keyof typeof EN_US, string> = {
|
|
|
23
23
|
feature6: '易于配置',
|
|
24
24
|
featureDesc6: '以零配置启动,然后一切皆可配置。',
|
|
25
25
|
|
|
26
|
-
// Solutions
|
|
27
|
-
solutions: '解决方案',
|
|
28
|
-
solutionsDesc1: '基于 React 的渐进式 Web 开发框架。',
|
|
29
|
-
solutionsDesc2: '简单、高性能的 npm 包开发方案。',
|
|
30
|
-
solutionsDesc3: '基于 Rspack 的 Web 构建工具。',
|
|
31
|
-
solutionsDesc4: '基于 Rspack 的静态站点生成器。',
|
|
32
|
-
|
|
33
26
|
// Footer
|
|
34
27
|
guide: '指南',
|
|
35
28
|
topic: '专题',
|
package/src/pages/index.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
|
-
import { Helmet,
|
|
3
|
+
import { Helmet, useLocation } from 'rspress/runtime';
|
|
4
4
|
import ContentCard from '../components/ContentCard';
|
|
5
5
|
import { FeatureLayout } from '../components/FeatureLayout';
|
|
6
6
|
import Footer from '../components/Footer';
|
|
@@ -92,22 +92,6 @@ export default function Home() {
|
|
|
92
92
|
},
|
|
93
93
|
];
|
|
94
94
|
|
|
95
|
-
const lang = useLang();
|
|
96
|
-
const solutions = [
|
|
97
|
-
{
|
|
98
|
-
id: 'framework',
|
|
99
|
-
title: 'Modern.js Framework',
|
|
100
|
-
href: useUrl('/guides/get-started/introduction'),
|
|
101
|
-
desc: t('solutionsDesc1'),
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
id: 'module',
|
|
105
|
-
title: 'Modern.js Module',
|
|
106
|
-
href: `https://modernjs.dev/module-tools${lang === 'en' ? '/en' : ''}`,
|
|
107
|
-
desc: t('solutionsDesc2'),
|
|
108
|
-
},
|
|
109
|
-
];
|
|
110
|
-
|
|
111
95
|
return (
|
|
112
96
|
<div>
|
|
113
97
|
<Helmet>
|
|
@@ -122,9 +106,9 @@ export default function Home() {
|
|
|
122
106
|
<HomepageHeader />
|
|
123
107
|
<main className={styles['homepage-main']}>
|
|
124
108
|
<FeatureLayout>
|
|
125
|
-
<SecondaryTitle>
|
|
109
|
+
<SecondaryTitle>Features</SecondaryTitle>
|
|
126
110
|
<div className={styles.cardContainer}>
|
|
127
|
-
{
|
|
111
|
+
{features.map(card => (
|
|
128
112
|
<ContentCard
|
|
129
113
|
key={card.id}
|
|
130
114
|
title={card.title}
|
|
@@ -145,19 +129,6 @@ export default function Home() {
|
|
|
145
129
|
</h1>
|
|
146
130
|
</FeatureLayout>
|
|
147
131
|
|
|
148
|
-
<FeatureLayout>
|
|
149
|
-
<SecondaryTitle>Modern.js Framework</SecondaryTitle>
|
|
150
|
-
<div className={styles.cardContainer}>
|
|
151
|
-
{features.map(card => (
|
|
152
|
-
<ContentCard
|
|
153
|
-
key={card.id}
|
|
154
|
-
title={card.title}
|
|
155
|
-
desc={card.desc}
|
|
156
|
-
href={card.href}
|
|
157
|
-
/>
|
|
158
|
-
))}
|
|
159
|
-
</div>
|
|
160
|
-
</FeatureLayout>
|
|
161
132
|
<Footer />
|
|
162
133
|
</main>
|
|
163
134
|
</div>
|