@modern-js/main-doc 2.58.3 → 2.60.0
Sign up to get free protection for your applications and to get access to all the features.
- package/docs/en/_meta.json +10 -5
- package/docs/en/apis/app/hooks/api/lambda.mdx +4 -48
- package/docs/en/apis/app/hooks/api/middleware.mdx +11 -0
- package/docs/en/apis/app/runtime/core/use-loader.mdx +1 -1
- package/docs/en/community/blog/v2-release-note.mdx +1 -1
- package/docs/en/components/enable-bff.mdx +19 -2
- package/docs/en/components/extend-bff-function.mdx +5 -0
- package/docs/en/components/init-app.mdx +0 -1
- package/docs/en/components/init-rspack-app.mdx +0 -1
- package/docs/en/components/other-plugins.mdx +0 -0
- package/docs/en/components/ssr-monitor.mdx +3 -0
- package/docs/en/configure/app/auto-load-plugin.mdx +4 -0
- package/docs/en/configure/app/output/ssg.mdx +52 -141
- package/docs/en/configure/app/plugins.mdx +2 -2
- package/docs/en/configure/app/tools/esbuild.mdx +1 -1
- package/docs/en/configure/app/tools/swc.mdx +1 -1
- package/docs/en/configure/app/tools/tailwindcss.mdx +1 -1
- package/docs/en/guides/_meta.json +0 -5
- package/docs/en/guides/advanced-features/_meta.json +3 -8
- package/docs/en/guides/advanced-features/bff/_meta.json +1 -1
- package/docs/en/guides/advanced-features/bff/extend-server.mdx +154 -0
- package/docs/en/guides/advanced-features/bff/frameworks.mdx +52 -123
- package/docs/en/guides/advanced-features/bff/function.mdx +108 -80
- package/docs/en/guides/advanced-features/bff/sdk.mdx +40 -51
- package/docs/en/guides/advanced-features/build-performance.mdx +6 -21
- package/docs/en/guides/advanced-features/page-performance/_meta.json +1 -0
- 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 +274 -263
- package/docs/en/guides/basic-features/static-assets/_meta.json +1 -0
- package/docs/en/guides/basic-features/static-assets.mdx +2 -2
- 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/deprecated.md +2 -0
- 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/_meta.json +0 -6
- 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/plugin/_meta.json +19 -0
- package/docs/en/plugin/cli-plugins/_meta.json +1 -0
- package/docs/en/plugin/cli-plugins/plugin-bff.mdx +5 -0
- package/docs/en/plugin/cli-plugins/plugin-ssg.mdx +5 -0
- package/docs/en/{guides/rsbuild-plugins → plugin/cli-plugins}/plugin-swc.mdx +7 -0
- package/docs/en/plugin/cli-plugins/plugin-tailwind.mdx +5 -0
- package/docs/en/plugin/cli-plugins.mdx +6 -0
- package/docs/en/{guides/advanced-features/rsbuild-plugin.mdx → plugin/introduction.mdx} +36 -11
- package/docs/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/extend.mdx +1 -1
- package/docs/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/implement.mdx +3 -3
- package/docs/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/plugin-api.mdx +2 -2
- package/docs/en/plugin/rsbuild-plugins/_meta.json +1 -0
- package/docs/en/plugin/rsbuild-plugins.mdx +3 -0
- package/docs/en/tutorials/first-app/c03-css.mdx +1 -1
- package/docs/zh/_meta.json +10 -5
- package/docs/zh/apis/app/hooks/api/lambda.mdx +5 -48
- package/docs/zh/apis/app/hooks/api/middleware.mdx +11 -0
- package/docs/zh/apis/app/runtime/core/use-loader.mdx +1 -1
- package/docs/zh/community/blog/v2-release-note.mdx +1 -1
- package/docs/zh/components/enable-bff.mdx +19 -2
- package/docs/zh/components/extend-bff-function.mdx +5 -0
- package/docs/zh/components/init-app.mdx +0 -1
- package/docs/zh/components/init-rspack-app.mdx +0 -1
- package/docs/zh/components/other-plugins.mdx +0 -0
- package/docs/zh/components/ssr-monitor.mdx +3 -0
- package/docs/zh/configure/app/auto-load-plugin.mdx +4 -0
- package/docs/zh/configure/app/output/ssg.mdx +49 -139
- package/docs/zh/configure/app/plugins.mdx +2 -2
- package/docs/zh/configure/app/tools/esbuild.mdx +1 -1
- package/docs/zh/configure/app/tools/swc.mdx +1 -1
- package/docs/zh/configure/app/tools/tailwindcss.mdx +1 -1
- package/docs/zh/guides/_meta.json +0 -5
- package/docs/zh/guides/advanced-features/_meta.json +3 -8
- package/docs/zh/guides/advanced-features/bff/_meta.json +1 -1
- package/docs/zh/guides/advanced-features/bff/extend-server.mdx +156 -0
- package/docs/zh/guides/advanced-features/bff/frameworks.mdx +51 -117
- package/docs/zh/guides/advanced-features/bff/function.mdx +69 -59
- package/docs/zh/guides/advanced-features/bff/sdk.mdx +27 -36
- package/docs/zh/guides/advanced-features/build-performance.mdx +6 -21
- package/docs/zh/guides/advanced-features/page-performance/_meta.json +1 -0
- package/docs/zh/guides/advanced-features/rspack-start.mdx +8 -17
- package/docs/zh/guides/basic-features/_meta.json +31 -9
- package/docs/zh/guides/basic-features/alias.mdx +5 -11
- 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/env-vars.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 +251 -237
- package/docs/zh/guides/basic-features/static-assets/_meta.json +1 -0
- package/docs/zh/guides/basic-features/static-assets.mdx +3 -7
- 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/deprecated.md +4 -0
- 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/_meta.json +0 -6
- 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/plugin/_meta.json +19 -0
- package/docs/zh/plugin/cli-plugins/_meta.json +1 -0
- package/docs/zh/plugin/cli-plugins/plugin-bff.mdx +5 -0
- package/docs/zh/plugin/cli-plugins/plugin-ssg.mdx +5 -0
- package/docs/zh/{guides/rsbuild-plugins → plugin/cli-plugins}/plugin-swc.mdx +7 -0
- package/docs/zh/plugin/cli-plugins/plugin-tailwind.mdx +5 -0
- package/docs/zh/plugin/cli-plugins.mdx +6 -0
- package/docs/zh/{guides/advanced-features/rsbuild-plugin.mdx → plugin/introduction.mdx} +38 -13
- package/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/extend.mdx +1 -1
- package/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/implement.mdx +3 -3
- package/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/plugin-api.mdx +2 -2
- package/docs/zh/plugin/rsbuild-plugins/_meta.json +1 -0
- package/docs/zh/plugin/rsbuild-plugins.mdx +4 -0
- package/docs/zh/tutorials/first-app/c03-css.mdx +1 -1
- package/i18n.json +42 -6
- package/package.json +7 -6
- package/rspress.config.ts +1 -58
- package/src/components/Footer/index.tsx +1 -1
- package/src/pages/index.tsx +0 -1
- package/docs/en/apis/app/hooks/api/api.mdx +0 -80
- package/docs/en/apis/app/hooks/api/app.mdx +0 -12
- package/docs/en/apis/app/hooks/config/storybook.mdx +0 -37
- package/docs/en/guides/advanced-features/bff/type.mdx +0 -46
- package/docs/en/guides/advanced-features/eslint.mdx +0 -148
- 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/api/api.mdx +0 -81
- package/docs/zh/apis/app/hooks/api/app.mdx +0 -12
- package/docs/zh/apis/app/hooks/config/storybook.mdx +0 -38
- package/docs/zh/guides/advanced-features/bff/type.mdx +0 -46
- package/docs/zh/guides/advanced-features/eslint.mdx +0 -152
- 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/advanced-features/{bff/index.mdx → bff.mdx} +0 -0
- /package/docs/en/guides/advanced-features/{code-split.mdx → page-performance/code-split.mdx} +0 -0
- /package/docs/en/guides/advanced-features/{inline-assets.mdx → page-performance/inline-assets.mdx} +0 -0
- /package/docs/en/guides/advanced-features/{optimize-bundle.mdx → page-performance/optimize-bundle.mdx} +0 -0
- /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/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/_meta.json +0 -0
- /package/docs/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/hook-list.mdx +0 -0
- /package/docs/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/hook.mdx +0 -0
- /package/docs/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/introduction.mdx +0 -0
- /package/docs/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/lifecycle.mdx +0 -0
- /package/docs/en/{guides/topic-detail/framework-plugin → plugin/plugin-system}/relationship.mdx +0 -0
- /package/docs/en/{guides → plugin}/rsbuild-plugins/plugin-esbuild.mdx +0 -0
- /package/docs/zh/guides/advanced-features/{bff/index.mdx → bff.mdx} +0 -0
- /package/docs/zh/guides/advanced-features/{code-split.mdx → page-performance/code-split.mdx} +0 -0
- /package/docs/zh/guides/advanced-features/{inline-assets.mdx → page-performance/inline-assets.mdx} +0 -0
- /package/docs/zh/guides/advanced-features/{optimize-bundle.mdx → page-performance/optimize-bundle.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/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/_meta.json +0 -0
- /package/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/hook-list.mdx +0 -0
- /package/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/hook.mdx +0 -0
- /package/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/introduction.mdx +0 -0
- /package/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/lifecycle.mdx +0 -0
- /package/docs/zh/{guides/topic-detail/framework-plugin → plugin/plugin-system}/relationship.mdx +0 -0
- /package/docs/zh/{guides → plugin}/rsbuild-plugins/plugin-esbuild.mdx +0 -0
@@ -1,555 +0,0 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 4
|
3
|
-
---
|
4
|
-
|
5
|
-
# Server-Side Rendering
|
6
|
-
|
7
|
-
In Modern.js, SSR is readily available without the need for developers to write intricate server-level logic or worry about the operation and maintenance of SSR services. Additionally, Modern.js includes a comprehensive degradation strategy for SSR to ensure safe page execution.
|
8
|
-
|
9
|
-
Enabling SSR is simple. Just set the value of [`server.ssr`](/configure/app/server/ssr) to `true`.
|
10
|
-
|
11
|
-
```ts title="modern.config.ts"
|
12
|
-
import { defineConfig } from '@modern-js/app-tools';
|
13
|
-
|
14
|
-
export default defineConfig({
|
15
|
-
server: {
|
16
|
-
ssr: true,
|
17
|
-
},
|
18
|
-
});
|
19
|
-
```
|
20
|
-
|
21
|
-
## SSR Data Fetch
|
22
|
-
|
23
|
-
Modern.js provides a Data Loader that simplifies data fetching for developers working with SSR and CSR. Each routing module, such as `layout.tsx` and `page.tsx`, can define its own Data Loader:
|
24
|
-
|
25
|
-
```ts title="src/routes/page.data.ts"
|
26
|
-
export const loader = () => {
|
27
|
-
return {
|
28
|
-
message: 'Hello World',
|
29
|
-
};
|
30
|
-
};
|
31
|
-
```
|
32
|
-
|
33
|
-
Within the component, data returned by the `loader` function can be accessed through the Hooks API:
|
34
|
-
|
35
|
-
```tsx
|
36
|
-
export default () => {
|
37
|
-
const data = useLoaderData();
|
38
|
-
return <div>{data.message}</div>;
|
39
|
-
};
|
40
|
-
```
|
41
|
-
|
42
|
-
Modern.js breaks the traditional model of server-side rendering (SSR) development and offers users a more user-friendly SSR development experience.
|
43
|
-
|
44
|
-
This feature offers elegant degradation processing. If the SSR request fails, it will automatically downgrade and restart the request on the browser side.
|
45
|
-
|
46
|
-
Developers should still be mindful of data fallback, including `null` values or unexpected data returns. This will help prevent React rendering errors and messy results during SSR.
|
47
|
-
|
48
|
-
:::info
|
49
|
-
|
50
|
-
1. When requesting a page through client-side routing, Modern.js sends an HTTP request. The server receives the request and executes the corresponding Data Loader function for the page, then returns the execution result as a response to the browser.
|
51
|
-
|
52
|
-
2. When using Data Loader, data is fetched before rendering. Modern.js also supports obtaining data during component rendering. For more related content, please refer to [Data Fetch](/guides/basic-features/data/data-fetch).
|
53
|
-
|
54
|
-
:::
|
55
|
-
|
56
|
-
## Keep Rendering Consistent
|
57
|
-
|
58
|
-
In some businesses, it is usually necessary to make different UI displays based on the current operating container environment characteristics, such as [UA](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) information.
|
59
|
-
|
60
|
-
If not handled carefully, unexpected rendering results are likely to occur.
|
61
|
-
|
62
|
-
Here is an example to show the problem when SSR and CSR rendering are inconsistent, add the following code to the component:
|
63
|
-
|
64
|
-
```tsx
|
65
|
-
{
|
66
|
-
typeof window !== 'undefined' ? <div>browser content</div> : null;
|
67
|
-
}
|
68
|
-
```
|
69
|
-
|
70
|
-
After launching the application and accessing the page, you will find that the browser console throws a warning message.
|
71
|
-
|
72
|
-
```sh
|
73
|
-
Warning: Expected server HTML to contain a matching <div> in <div>.
|
74
|
-
```
|
75
|
-
|
76
|
-
This is caused by React's hydration logic on the client side detecting inconsistencies between the rendered result and the SSR rendering result. Although the page appears normal, in complex applications, it is likely to cause problems such as DOM hierarchy confusion and style disorder.
|
77
|
-
|
78
|
-
:::info
|
79
|
-
More information about [`React hydrate`](https://reactjs.org/docs/react-dom.html#hydrate).
|
80
|
-
|
81
|
-
:::
|
82
|
-
|
83
|
-
The application needs to maintain consistency between SSR and CSR rendering results. If there is inconsistency, it means that this part of the content does not need to be rendered in SSR.
|
84
|
-
|
85
|
-
Modern.js provides a [`<NoSSR>`](/apis/app/runtime/core/use-runtime-context) component for such content that does not need to be rendered in SSR.
|
86
|
-
|
87
|
-
```ts
|
88
|
-
import { NoSSR } from '@modern-js/runtime/ssr';
|
89
|
-
```
|
90
|
-
|
91
|
-
Wrap the element that does not require SSR with the `NoSSR` component:
|
92
|
-
|
93
|
-
```tsx
|
94
|
-
<NoSSR>
|
95
|
-
<div>client content</div>
|
96
|
-
</NoSSR>
|
97
|
-
```
|
98
|
-
|
99
|
-
After modifying the code, refreshing the page shows that the previous warning has disappeared. Opening the Network tab of the browser devtools and checking the returned HTML document does not contain content wrapped by `NoSSR` components.
|
100
|
-
|
101
|
-
:::info
|
102
|
-
['useRuntimeContext'](/apis/app/runtime/core/use-runtime-context) can get complete request information, which can be used to ensure consistent rendering results between SSR and CSR.
|
103
|
-
|
104
|
-
:::
|
105
|
-
|
106
|
-
## Pay Attention to Memory Leaks
|
107
|
-
|
108
|
-
:::warning
|
109
|
-
Developers need to pay special attention to memory leaks in the SSR mode. Even tiny memory leaks can have an impact on the service after a large number of accesses.
|
110
|
-
|
111
|
-
:::
|
112
|
-
|
113
|
-
When using SSR, each request from the browser will trigger the server to re-execute the component rendering logic. Therefore, it is necessary to avoid defining any data structures that may continue to grow globally, subscribing to events globally, or creating streams that will not be destroyed globally.
|
114
|
-
|
115
|
-
For example, when using [redux-observable](https://redux-observable.js.org/), developers who are used to CSR development usually code in the component like this:
|
116
|
-
|
117
|
-
```tsx
|
118
|
-
/* This code is for demonstration purposes only */
|
119
|
-
import { createEpicMiddleware, combineEpics } from 'redux-observable';
|
120
|
-
|
121
|
-
const epicMiddleware = createEpicMiddleware();
|
122
|
-
const rootEpic = combineEpics();
|
123
|
-
|
124
|
-
export default function Test() {
|
125
|
-
epicMiddleware.run(rootEpic);
|
126
|
-
return <div>Hello Modern.js</div>;
|
127
|
-
}
|
128
|
-
```
|
129
|
-
|
130
|
-
Create a Middleware instance `epicMiddleware` outside the component and call epicMiddleware.run inside the component.
|
131
|
-
|
132
|
-
When running on the client-side, this code will not cause any issues. However, during SSR, the Middleware instance cannot be destroyed.
|
133
|
-
|
134
|
-
Every time a component is rendered and `epicMiddleware.run(rootEpic)` is called, new event bindings are added internally which causes the entire object to grow continuously and ultimately affects application performance.
|
135
|
-
|
136
|
-
CSR issues are not easy to detect, so when switching from CSR to SSR, if you are unsure whether the application has such problems, you can perform stress testing on applications.
|
137
|
-
|
138
|
-
## Converging Server Data.
|
139
|
-
|
140
|
-
In order to maintain the data requested during the SSR phase, it can be directly used on the browser side. Modern.js will inject the data and state collected during rendering into HTML.
|
141
|
-
|
142
|
-
However, in CSR applications, there are often situations where interface data is large and component states are not converged. If SSR is used directly in this case, the rendered HTML may have a problem of being too large.
|
143
|
-
|
144
|
-
At this time, SSR may not only fail to improve user experience for applications but may also have an opposite effect.
|
145
|
-
|
146
|
-
Therefore, when using SSR, **developers need to properly slim down the application**.
|
147
|
-
|
148
|
-
1. Focus on the first screen, SSR can request only the data needed for the first screen and render the remaining parts on the browser side.
|
149
|
-
2. Remove data unrelated to rendering from the returned data of the interface.
|
150
|
-
|
151
|
-
## Serverless Pre-render
|
152
|
-
|
153
|
-
Modern.js provides the Serverless Pre-rendering (SPR) feature to improve SSR performance.
|
154
|
-
|
155
|
-
SPR uses pre-rendering and caching technology to provide responsive performance for SSR pages. It enables SSR applications to have the response speed and stability of static web pages while also maintaining dynamic data updates.
|
156
|
-
|
157
|
-
Using SPR in Modern.js is very simple. Just add the PreRender component to your component, and the page where it is located will automatically enable SPR.
|
158
|
-
|
159
|
-
Here is a simulated component that uses the useLoaderData API. The request in the Data Loader takes 2 seconds to consume.
|
160
|
-
|
161
|
-
```tsx title="page.data.ts"
|
162
|
-
import { useLoaderData } from '@modern-js/runtime/router';
|
163
|
-
|
164
|
-
export const loader = async () => {
|
165
|
-
await new Promise((resolve, reject) => {
|
166
|
-
setTimeout(() => {
|
167
|
-
resolve(null);
|
168
|
-
}, 2000);
|
169
|
-
});
|
170
|
-
|
171
|
-
return {
|
172
|
-
message: 'Hello Modern.js',
|
173
|
-
};
|
174
|
-
};
|
175
|
-
```
|
176
|
-
|
177
|
-
```tsx title="page.tsx"
|
178
|
-
import { useLoaderData } from '@modern-js/runtime/router';
|
179
|
-
|
180
|
-
export default () => {
|
181
|
-
const data = useLoaderData();
|
182
|
-
return <div>{data?.message}</div>;
|
183
|
-
};
|
184
|
-
```
|
185
|
-
|
186
|
-
After executing the `dev` command and opening the page, it is obvious that the page needs to wait 2s before returning.
|
187
|
-
|
188
|
-
Use the `<PreRender>` component for optimization next, which can be directly exported from `@modern-js/runtime/ssr`.
|
189
|
-
|
190
|
-
```ts
|
191
|
-
import { PreRender } from '@modern-js/runtime/ssr';
|
192
|
-
```
|
193
|
-
|
194
|
-
Use the `<PreRender>` component within the routing component and set the parameter `interval` to indicate that the expiration time of this rendering result is 5 seconds:
|
195
|
-
|
196
|
-
```tsx
|
197
|
-
<PreRender interval={5} />
|
198
|
-
```
|
199
|
-
|
200
|
-
After modification, execute `pnpm run build && pnpm run serve` to start the application and open the page.
|
201
|
-
|
202
|
-
The first time it is opened, there is no difference in rendering compared to before, and there is still a 2-second delay.
|
203
|
-
|
204
|
-
Clicking refresh opens the page instantly, but at this point, the page data has not changed due to the refresh because the cache has not yet expired.
|
205
|
-
|
206
|
-
After waiting for 5 seconds and refreshing the page, the data on the page remained unchanged. After refreshing again, the data changed, but the response was nearly immediate.
|
207
|
-
|
208
|
-
This is because during the previous request, SPR had already asynchronously obtained a new rendering result in the background, and this time's requested page is a version that has been cached on the server.
|
209
|
-
|
210
|
-
One can imagine that when the `interval` is set to 1, users can have a responsive experience of static pages while perceiving real-time data.
|
211
|
-
|
212
|
-
:::info
|
213
|
-
For more detail, see [`<PreRender>`](/apis/app/runtime/ssr/pre-render).
|
214
|
-
|
215
|
-
:::
|
216
|
-
|
217
|
-
## Treeshaking
|
218
|
-
|
219
|
-
When SSR is enabled, Modern.js uses the same entry point to build both SSR Bundle and CSR Bundle. Therefore, errors may occur if there are Web APIs in the SSR Bundle or Node APIs in the CSR Bundle.
|
220
|
-
|
221
|
-
Introducing Web APIs in components usually do some global listening or obtaining browser-related data, such as:
|
222
|
-
|
223
|
-
```tsx
|
224
|
-
document.addEventListener('load', () => {
|
225
|
-
console.log('document load');
|
226
|
-
});
|
227
|
-
const App = () => {
|
228
|
-
return <div>Hello World</div>;
|
229
|
-
};
|
230
|
-
export default App;
|
231
|
-
```
|
232
|
-
|
233
|
-
Importing Node API in component files is usually done when using `useLoader`, for example:
|
234
|
-
|
235
|
-
```ts
|
236
|
-
import fse from 'fs-extra';
|
237
|
-
export default () => {
|
238
|
-
const file = fse.readFileSync('./myfile');
|
239
|
-
return {
|
240
|
-
...
|
241
|
-
};
|
242
|
-
};
|
243
|
-
```
|
244
|
-
|
245
|
-
### Use Environment Variables
|
246
|
-
|
247
|
-
For the first case, we can directly use the built-in environment variable `MODERN_TARGET` in Modern.js to determine and remove unused code during build time:
|
248
|
-
|
249
|
-
```ts
|
250
|
-
if (process.env.MODERN_TARGET === 'browser') {
|
251
|
-
document.addEventListener('load', () => {
|
252
|
-
console.log('document load');
|
253
|
-
});
|
254
|
-
}
|
255
|
-
```
|
256
|
-
|
257
|
-
After the development environment is bundled, the SSR and CSR artifacts will be compiled into the following content. Therefore, there will be no more Web API errors in the SSR environment.
|
258
|
-
|
259
|
-
```ts
|
260
|
-
// SSR production
|
261
|
-
if (false) {
|
262
|
-
}
|
263
|
-
|
264
|
-
// CSR production
|
265
|
-
if (true) {
|
266
|
-
document.addEventListener('load', () => {
|
267
|
-
console.log('document load');
|
268
|
-
});
|
269
|
-
}
|
270
|
-
```
|
271
|
-
|
272
|
-
:::note
|
273
|
-
For more information, see [environment variables](/guides/basic-features/env-vars).
|
274
|
-
|
275
|
-
:::
|
276
|
-
|
277
|
-
### Use File Suffix
|
278
|
-
|
279
|
-
But for example, in the second case, `fs-extra` is imported in the code, which has side effects using Node API internally. If it is directly referenced in the component, it will cause an error when CSR loading.
|
280
|
-
|
281
|
-
Environment variables does not work in this case. Modern.js also supports using files with the `.node.` suffix to distinguish between the packaging files of SSR Bundle and CSR Bundle products.
|
282
|
-
|
283
|
-
You can create a proxy layer by creating files with the same name but different extensions, such as `.ts` and `.node.ts`:
|
284
|
-
|
285
|
-
```ts title="compat.ts"
|
286
|
-
export const readFileSync: any = () => {};
|
287
|
-
```
|
288
|
-
|
289
|
-
```ts title="compat.node.ts"
|
290
|
-
export { readFileSync } from 'fs-extra';
|
291
|
-
```
|
292
|
-
|
293
|
-
Import `./compat` directly in the file. In SSR environment, files with `.node.ts` suffix will be used first, while in CSR environment, files with `.ts` suffix will be used.
|
294
|
-
|
295
|
-
```ts title="App.tsx"
|
296
|
-
import { readFileSync } from './compat'
|
297
|
-
|
298
|
-
export const loader = () => {
|
299
|
-
const file = readFileSync('./myfile');
|
300
|
-
return {
|
301
|
-
...
|
302
|
-
};
|
303
|
-
};
|
304
|
-
```
|
305
|
-
|
306
|
-
### Independent File
|
307
|
-
|
308
|
-
The two methods mentioned above will both bring some mental burden to developers. In real business scenarios, we found that most of the mixed Node/Web code appears in data requests.
|
309
|
-
|
310
|
-
Therefore, Modern.js has designed a [Data Fetch](/guides/basic-features/data/data-fetch) to separate CSR and SSR code based on [Nested Routing](/guides/basic-features/routes).
|
311
|
-
|
312
|
-
We can separate **data requests from component code** by using independent files. Write the component logic in `routes/page.tsx` and write the data request logic in `routes/page.data.ts`.
|
313
|
-
|
314
|
-
```ts title="routes/page.tsx"
|
315
|
-
export default Page = () => {
|
316
|
-
return <div>Hello World<div>
|
317
|
-
}
|
318
|
-
```
|
319
|
-
|
320
|
-
```ts title="routes/page.data.tsx"
|
321
|
-
import fse from 'fs-extra';
|
322
|
-
export const loader = () => {
|
323
|
-
const file = fse.readFileSync('./myfile');
|
324
|
-
return {
|
325
|
-
...
|
326
|
-
};
|
327
|
-
}
|
328
|
-
```
|
329
|
-
|
330
|
-
## Remote Request
|
331
|
-
|
332
|
-
When initiating interface requests in SSR, developers sometimes encapsulate isomorphic request tools themselves. For some interfaces that require passing user cookies, developers can use the ['useRuntimeContext'](/guides/basic-features/data/data-fetch#route-loader) API to get the request header for implementation.
|
333
|
-
|
334
|
-
It should be noted that the obtained request header is for HTML requests, which may not be suitable for API requests. Therefore, ** don't passed through all request headers **.
|
335
|
-
|
336
|
-
In addition, some backend interfaces or common gateways will verify based on the information in the request header. Full pass-through can easily lead to various difficult-to-troubleshoot issues, so it is recommended to **pass through as needed**.
|
337
|
-
|
338
|
-
If it is really necessary to pass through all request headers, please be sure to filter the `host` field.
|
339
|
-
|
340
|
-
## Streaming SSR
|
341
|
-
|
342
|
-
Modern.js supports streaming rendering in React 18 which can be enabled through the following configuration:
|
343
|
-
|
344
|
-
```ts title="modern.config.ts"
|
345
|
-
import { defineConfig } from '@modern-js/app-tools';
|
346
|
-
|
347
|
-
export default defineConfig({
|
348
|
-
server: {
|
349
|
-
ssr: {
|
350
|
-
mode: 'stream',
|
351
|
-
},
|
352
|
-
},
|
353
|
-
});
|
354
|
-
```
|
355
|
-
|
356
|
-
The streaming SSR of Modern.js is implemented based on React Router, and the main APIs involved are:
|
357
|
-
|
358
|
-
- [`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.
|
359
|
-
- [`Await`](https://reactrouter.com/en/main/components/await): Used to render deferred values with automatic error handling.
|
360
|
-
- [`useAsyncValue`](https://reactrouter.com/en/main/hooks/use-async-value): Returns the resolved data from the nearest `<Await>` ancestor component.
|
361
|
-
|
362
|
-
### Return async data
|
363
|
-
|
364
|
-
```ts title="page.data.ts"
|
365
|
-
import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
|
366
|
-
|
367
|
-
interface User {
|
368
|
-
name: string;
|
369
|
-
age: number;
|
370
|
-
}
|
371
|
-
|
372
|
-
export interface Data {
|
373
|
-
data: User;
|
374
|
-
}
|
375
|
-
|
376
|
-
export const loader = ({ params }: LoaderFunctionArgs) => {
|
377
|
-
const userId = params.id;
|
378
|
-
|
379
|
-
const user = new Promise<User>(resolve => {
|
380
|
-
setTimeout(() => {
|
381
|
-
resolve({
|
382
|
-
name: `user-${userId}`,
|
383
|
-
age: 18,
|
384
|
-
});
|
385
|
-
}, 200);
|
386
|
-
});
|
387
|
-
|
388
|
-
return defer({ data: user });
|
389
|
-
};
|
390
|
-
```
|
391
|
-
|
392
|
-
`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 }`.
|
393
|
-
|
394
|
-
`defer` can receive both asynchronous and synchronous data at the same time. For example:
|
395
|
-
|
396
|
-
```ts title="page.data.ts"
|
397
|
-
// skip some codes
|
398
|
-
|
399
|
-
export default ({ params }: LoaderFunctionArgs) => {
|
400
|
-
const userId = params.id;
|
401
|
-
|
402
|
-
const user = new Promise<User>(resolve => {
|
403
|
-
setTimeout(() => {
|
404
|
-
resolve({
|
405
|
-
name: `user-${userId}`,
|
406
|
-
age: 18,
|
407
|
-
});
|
408
|
-
}, 200);
|
409
|
-
});
|
410
|
-
|
411
|
-
const otherData = new Promise<string>(resolve => {
|
412
|
-
setTimeout(() => {
|
413
|
-
resolve('some sync data');
|
414
|
-
}, 200);
|
415
|
-
});
|
416
|
-
|
417
|
-
return defer({
|
418
|
-
data: user,
|
419
|
-
other: await otherData,
|
420
|
-
});
|
421
|
-
};
|
422
|
-
```
|
423
|
-
|
424
|
-
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`.
|
425
|
-
|
426
|
-
### Render asynchronous data.
|
427
|
-
|
428
|
-
With the `<Await>` component, you can retrieve the data asynchronously returned by the Data Loader and then render it. For example:
|
429
|
-
|
430
|
-
```tsx title="page.tsx"
|
431
|
-
import { Await, useLoaderData } from '@modern-js/runtime/router';
|
432
|
-
import { Suspense } from 'react';
|
433
|
-
import type { Data } from './page.data';
|
434
|
-
|
435
|
-
const Page = () => {
|
436
|
-
const data = useLoaderData() as Data;
|
437
|
-
|
438
|
-
return (
|
439
|
-
<div>
|
440
|
-
User info:
|
441
|
-
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
442
|
-
<Await resolve={data.data}>
|
443
|
-
{user => {
|
444
|
-
return (
|
445
|
-
<div id="data">
|
446
|
-
name: {user.name}, age: {user.age}
|
447
|
-
</div>
|
448
|
-
);
|
449
|
-
}}
|
450
|
-
</Await>
|
451
|
-
</Suspense>
|
452
|
-
</div>
|
453
|
-
);
|
454
|
-
};
|
455
|
-
|
456
|
-
export default Page;
|
457
|
-
```
|
458
|
-
|
459
|
-
`<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.
|
460
|
-
|
461
|
-
:::warning Warning
|
462
|
-
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.
|
463
|
-
|
464
|
-
Therefore, the import method here is: `import type { Data } from './page.data'`;
|
465
|
-
:::
|
466
|
-
|
467
|
-
You can also retrieve asynchronous data returned by the Data Loader using `useAsyncValue`. For example:
|
468
|
-
|
469
|
-
```tsx title="page.tsx"
|
470
|
-
import { useAsyncValue } from '@modern-js/runtime/router';
|
471
|
-
|
472
|
-
// skip some codes
|
473
|
-
|
474
|
-
const UserInfo = () => {
|
475
|
-
const user = useAsyncValue();
|
476
|
-
|
477
|
-
return (
|
478
|
-
<div>
|
479
|
-
name: {user.name}, age: {user.age}
|
480
|
-
</div>
|
481
|
-
);
|
482
|
-
};
|
483
|
-
|
484
|
-
const Page = () => {
|
485
|
-
const data = useLoaderData() as Data;
|
486
|
-
|
487
|
-
return (
|
488
|
-
<div>
|
489
|
-
User info:
|
490
|
-
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
491
|
-
<Await resolve={data.data}>
|
492
|
-
<UserInfo />
|
493
|
-
</Await>
|
494
|
-
</Suspense>
|
495
|
-
</div>
|
496
|
-
);
|
497
|
-
};
|
498
|
-
|
499
|
-
export default Page;
|
500
|
-
```
|
501
|
-
|
502
|
-
### Error handling
|
503
|
-
|
504
|
-
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.
|
505
|
-
For example, we intentionally throw an error in the Data Loader function:
|
506
|
-
|
507
|
-
```ts title="page.data.ts"
|
508
|
-
import { defer } from '@modern-js/runtime/router';
|
509
|
-
|
510
|
-
export const loader = () => {
|
511
|
-
const data = new Promise((resolve, reject) => {
|
512
|
-
setTimeout(() => {
|
513
|
-
reject(new Error('error occurs'));
|
514
|
-
}, 200);
|
515
|
-
});
|
516
|
-
|
517
|
-
return defer({ data });
|
518
|
-
};
|
519
|
-
```
|
520
|
-
|
521
|
-
Then use `useAsyncError` to get the error, and assign the component used to render the error to the `errorElement` property of the `<Await>` component:
|
522
|
-
|
523
|
-
```tsx title="page.ts"
|
524
|
-
import { Await, useAsyncError, useLoaderData } from '@modern-js/runtime/router';
|
525
|
-
import { Suspense } from 'react';
|
526
|
-
|
527
|
-
export default function Page() {
|
528
|
-
const data = useLoaderData();
|
529
|
-
|
530
|
-
return (
|
531
|
-
<div>
|
532
|
-
Error page
|
533
|
-
<Suspense fallback={<div>loading ...</div>}>
|
534
|
-
<Await resolve={data.data} errorElement={<ErrorElement />}>
|
535
|
-
{(data: any) => {
|
536
|
-
return <div>never displayed</div>;
|
537
|
-
}}
|
538
|
-
</Await>
|
539
|
-
</Suspense>
|
540
|
-
</div>
|
541
|
-
);
|
542
|
-
}
|
543
|
-
|
544
|
-
function ErrorElement() {
|
545
|
-
const error = useAsyncError() as Error;
|
546
|
-
return <p>Something went wrong! {error.message}</p>;
|
547
|
-
}
|
548
|
-
```
|
549
|
-
|
550
|
-
:::info More
|
551
|
-
|
552
|
-
1. [Deferred Data](https://reactrouter.com/en/main/guides/deferred)
|
553
|
-
2. [New Suspense SSR Architecture in React 18](https://github.com/reactwg/react-18/discussions/37)
|
554
|
-
|
555
|
-
:::
|
@@ -1,81 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: '**/*.[tj]s'
|
3
|
-
sidebar_position: 1
|
4
|
-
---
|
5
|
-
|
6
|
-
# **/*.[tj]s
|
7
|
-
|
8
|
-
在 [BFF 函数写法](/guides/advanced-features/bff/type.html#函数写法)下,声明 API 路由的文件。除了[某些约定文件](/apis/app/hooks/api/api#白名单)外,`api` 目录下的文件会被注册为接口的路由。
|
9
|
-
|
10
|
-
:::info
|
11
|
-
使用 `api` 目录需要开启 BFF 功能,需要在项目下执行 new 命令启用「BFF」功能。
|
12
|
-
|
13
|
-
该文件支持使用 `js` 或 `ts` 语言,但必须使用 `esm` 语法导出函数。
|
14
|
-
|
15
|
-
:::
|
16
|
-
|
17
|
-
## 该文件约定路由如下:
|
18
|
-
|
19
|
-
### 默认路由
|
20
|
-
|
21
|
-
路由系统会将以 `index` 命名的文件会被映射到上一层目录。
|
22
|
-
|
23
|
-
- `api/index.ts` -> `$BASENAME/`
|
24
|
-
- `api/user/index.ts` -> `$BASENAME/user`
|
25
|
-
|
26
|
-
### 嵌套路由
|
27
|
-
|
28
|
-
路由系统也支持解析嵌套的文件,如果创建嵌套文件夹结构,文件仍会以相同方式自动解析路由。
|
29
|
-
|
30
|
-
- `api/hello.ts` -> `$BASENAME/hello`
|
31
|
-
- `api/user/list.ts` -> `$BASENAME/user/list`
|
32
|
-
|
33
|
-
### 动态路由
|
34
|
-
|
35
|
-
路由系统支持通过 `[]` 命名的文件目录生成动态路由。
|
36
|
-
|
37
|
-
- `api/user/[username]/info.ts` -> `$BASENAME/user/:username/info`
|
38
|
-
- `api/user/[username]/delete.ts` -> `$BASENAME/user/:username/delete`
|
39
|
-
- `api/article/[id]/info.ts` -> `$BASENAME/article/:id/info`
|
40
|
-
|
41
|
-
其中的 `$BASENAME` 可以在 `modern.config.js` 中进行配置,默认值为 `/api`。
|
42
|
-
|
43
|
-
### 白名单
|
44
|
-
|
45
|
-
默认 `api` 目录下所有文件都会当作 BFF 函数文件去解析,但同样我们也设置了白名单,这些文件不被被解析:
|
46
|
-
|
47
|
-
- 命名以 `_` 开头的文件。例如:`_utils.ts`。
|
48
|
-
- 命名以 `_` 开头的文件夹下所有文件。例如:`_utils/index.ts`、`_utils/cp.ts`。
|
49
|
-
- 测试文件。例如:`foo.test.ts`。
|
50
|
-
- TypeScript 类型文件。例如:`hello.d.ts`。
|
51
|
-
- `node_module` 下的文件。
|
52
|
-
|
53
|
-
## 函数定义
|
54
|
-
|
55
|
-
除了上面的路由规则之外,代码中函数定义与导出也有相应的约定。
|
56
|
-
|
57
|
-
函数通过具名导出,导出函数的名字为对应接口接受的 HTTP Method,即:
|
58
|
-
|
59
|
-
```ts
|
60
|
-
export const get = async () => {
|
61
|
-
return {
|
62
|
-
name: 'Modern.js',
|
63
|
-
desc: '现代 web 工程方案',
|
64
|
-
};
|
65
|
-
};
|
66
|
-
```
|
67
|
-
|
68
|
-
这样导出函数,则会得到一个 `GET` 接口。
|
69
|
-
|
70
|
-
Modern.js 工程中支持了 9 个 Method 定义,即:`GET`、`POST`、`PUT`、`DELETE`、`CONNECT`、`TRACE`、`PATCH`、`OPTION`、`HEAD`,即可以用这些 Method 作为函数导出的名字。
|
71
|
-
|
72
|
-
名字是大小不敏感的,就是说,如果是 `GET`,写成 `get`、`Get`、`GEt`、`GET`,都可以准确识别。而默认导出,即 `export default xxx` 则会被映射为 `Get`。
|
73
|
-
|
74
|
-
因为 `delete` 是 JavaScript 中的关键字,可以使用 `del` 或者 `DELETE` 代替。
|
75
|
-
|
76
|
-
可以在一个文件中定义多个不同 Method 的函数,但如果定义多个相同 Method 的函数,则只有第一个会生效。
|
77
|
-
|
78
|
-
:::info
|
79
|
-
需要注意的是,定义的函数都应该是异步的,这个与函数调用时类型有关。
|
80
|
-
|
81
|
-
:::
|
@@ -1,38 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: storybook/
|
3
|
-
sidebar_position: 7
|
4
|
-
---
|
5
|
-
|
6
|
-
# storybook/
|
7
|
-
|
8
|
-
Modern.js 支持使用 Storybook 进行调试,当需要对 Storybook 进行配置时,需要在项目 config/storybook 目录进行配置。
|
9
|
-
|
10
|
-
Storybook 配置请查看:[Storybook 配置](https://storybook.js.org/docs/react/configure/overview)
|
11
|
-
|
12
|
-
:::info
|
13
|
-
使用 Storybook 进行调试需要提前在项目下执行 new 命令启用「Storybook」模式功能。
|
14
|
-
|
15
|
-
:::
|
16
|
-
|
17
|
-
### 示例
|
18
|
-
|
19
|
-
对于 Storybook Manager app 部分的 webpack 配置,可以通过增加 `./config/storybook/main.js` 文件进行配置。
|
20
|
-
|
21
|
-
```js
|
22
|
-
// ./config/storybook/main.js
|
23
|
-
|
24
|
-
module.exports = {
|
25
|
-
// it controls the Storybook manager app
|
26
|
-
managerWebpack: async (config, options) => {
|
27
|
-
// update config here
|
28
|
-
return config;
|
29
|
-
},
|
30
|
-
};
|
31
|
-
```
|
32
|
-
|
33
|
-
### 限制
|
34
|
-
|
35
|
-
在使用 config/storybook 目录进行配置时,存在以下限制:
|
36
|
-
|
37
|
-
- 不能修改 Story 文件存放的位置,即无法在 `main.js` 文件里修改 `stories` 配置。
|
38
|
-
- 不支持在 `main.js` 中修改 Webpack 和 Babel 相关配置,相关需求可通过 [`tools.webpack`](/configure/app/tools/webpack.html) / [`tools.babel`](/configure/app/tools/babel.html) 修改。
|