@modern-js/main-doc 2.0.0-beta.6 → 2.0.0-canary.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.turbo/turbo-build.log +1 -1
- package/en/docusaurus-plugin-content-docs/current/apis/app/hooks/src/index_.md +1 -1
- package/en/docusaurus-plugin-content-docs/current/apis/app/hooks/src/pages.md +1 -1
- package/en/docusaurus-plugin-content-docs/current/apis/app/hooks/src/routes.md +86 -0
- package/en/docusaurus-plugin-content-docs/current/components/global-proxy-config.md +74 -0
- package/en/docusaurus-plugin-content-docs/current/components/global-proxy.md +28 -0
- package/en/docusaurus-plugin-content-docs/current/configure/app/dev/proxy.md +2 -72
- package/en/docusaurus-plugin-content-docs/current/configure/app/source/entries.md +0 -2
- package/en/docusaurus-plugin-content-docs/current/configure/app/tools/tailwindcss.md +16 -22
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/_category_.json +8 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/bff-proxy.md +27 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/frameworks.md +148 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/function.md +218 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/index.md +20 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/type.md +43 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/code-split.md +77 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/compatibility.md +76 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/eslint.md +145 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/index.md +12 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/low-level.md +46 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/ssg.md +128 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/ssr.md +306 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/testing.md +46 -0
- package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/web-server.md +57 -0
- package/en/docusaurus-plugin-content-docs/current/guides/basic-features/alias.md +67 -0
- package/en/docusaurus-plugin-content-docs/current/guides/basic-features/css/tailwindcss.md +30 -35
- package/en/docusaurus-plugin-content-docs/current/guides/basic-features/data-fetch.md +400 -0
- package/en/docusaurus-plugin-content-docs/current/guides/basic-features/env-vars.md +166 -0
- package/en/docusaurus-plugin-content-docs/current/guides/basic-features/html.md +235 -0
- package/en/docusaurus-plugin-content-docs/current/guides/basic-features/mock.md +78 -0
- package/en/docusaurus-plugin-content-docs/current/guides/basic-features/proxy.md +60 -0
- package/en/docusaurus-plugin-content-docs/current/guides/get-started/quick-start.md +2 -4
- package/package.json +3 -3
- package/zh/apis/app/hooks/src/index_.md +1 -1
- package/zh/apis/app/hooks/src/pages.md +1 -1
- package/zh/apis/app/hooks/src/routes.md +89 -0
- package/zh/components/debug-app.md +1 -2
- package/zh/components/global-proxy-config.md +70 -0
- package/zh/configure/app/dev/proxy.md +2 -70
- package/zh/configure/app/source/entries.md +1 -3
- package/zh/configure/app/tools/tailwindcss.md +16 -23
- package/zh/guides/advanced-features/bff/function.md +37 -19
- package/zh/guides/advanced-features/code-split.md +28 -20
- package/zh/guides/advanced-features/compatibility.md +24 -14
- package/zh/guides/advanced-features/ssg.md +1 -47
- package/zh/guides/advanced-features/ssr.md +1 -1
- package/zh/guides/advanced-features/testing.md +2 -2
- package/zh/guides/basic-features/alias.md +5 -5
- package/zh/guides/basic-features/css/tailwindcss.md +31 -35
- package/zh/guides/basic-features/data-fetch.md +7 -6
- package/zh/guides/basic-features/env-vars.md +2 -2
- package/zh/guides/basic-features/html.md +62 -137
- package/zh/guides/basic-features/mock.md +8 -9
- package/zh/guides/basic-features/proxy.md +2 -2
- package/zh/guides/basic-features/routes.md +37 -3
- package/zh/guides/get-started/quick-start.md +1 -2
- package/zh/guides/topic-detail/framework-plugin/implement.md +54 -6
- package/zh/guides/topic-detail/micro-frontend/c02-development.md +1 -1
@@ -4,8 +4,6 @@ sidebar_position: 2
|
|
4
4
|
|
5
5
|
# Tailwind CSS
|
6
6
|
|
7
|
-
|
8
|
-
|
9
7
|
[Tailwind CSS](https://tailwindcss.com/) is a CSS framework and design system based on Utility Class, which can quickly add common styles to components, and support flexible extension of theme styles. To use [Tailwind CSS](https://tailwindcss.com/) in the Modern.js, just execute `pnpm run new` in the project root directory and turn it on.
|
10
8
|
|
11
9
|
Choose as follows:
|
@@ -23,12 +21,40 @@ import 'tailwindcss/components.css';
|
|
23
21
|
import 'tailwindcss/utilities.css';
|
24
22
|
```
|
25
23
|
|
26
|
-
You can then use the Utility Class provided by Tailwind CSS in each component
|
24
|
+
You can then use the Utility Class provided by Tailwind CSS in each component:
|
25
|
+
|
26
|
+
```tsx
|
27
|
+
const App = () => (
|
28
|
+
<div className="h-12 w-48">
|
29
|
+
<p className="text-xl font-medium text-black">hello world</p>
|
30
|
+
</div>
|
31
|
+
);
|
32
|
+
```
|
27
33
|
|
28
34
|
::: info Additional
|
29
35
|
According to different needs, you can optionally import the CSS files provided by Tailwind CSS. Since the use of `@taiwind` is equivalent to directly importing CSS files, you can refer to the content in the annotate in the [`@tailwind` usage](https://tailwindcss.com/docs/functions-and-directives#tailwind) document for the purpose of the CSS files provided by Tailwind CSS.
|
30
36
|
:::
|
31
37
|
|
38
|
+
## Tailwind CSS version
|
39
|
+
|
40
|
+
Modern.js supports both Tailwind CSS v2 and v3. The framework will recognize the version of `tailwindcss` in the project `package.json` and apply the corresponding configuration. By default, we install Tailwind CSS v3 for you.
|
41
|
+
|
42
|
+
If your project is still using Tailwind CSS v2, we recommend that you upgrade to v3 to support JIT and other capabilities. For the differences between Tailwind CSS v2 and v3 versions, please refer to the following articles:
|
43
|
+
|
44
|
+
- [Tailwind CSS v3.0](https://tailwindcss.com/blog/tailwindcss-v3)
|
45
|
+
- [Upgrade Guide](https://tailwindcss.com/docs/upgrade-guide)
|
46
|
+
|
47
|
+
### Browser Compatibility
|
48
|
+
|
49
|
+
Both Tailwind CSS v2 and v3 do not support IE 11 browsers. For background, please refer to:
|
50
|
+
|
51
|
+
- [Tailwind CSS v3 - Browser Support](https://tailwindcss.com/docs/browser-support).
|
52
|
+
- [Tailwind CSS v2 - Browser Support](https://v2.tailwindcss.com/docs/browser-support)
|
53
|
+
|
54
|
+
If you use Tailwind CSS on IE 11 browser, some styles may not be available, please pay attention.
|
55
|
+
|
56
|
+
## Theme config
|
57
|
+
|
32
58
|
When you need to customize the [theme](https://tailwindcss.com/docs/theme) configuration of Tailwind CSS, you can modify it in the configuration [`source.designSystem`](/docs/configure/app/source/design-system), for example, add a color theme `primary`:
|
33
59
|
|
34
60
|
```typescript title="modern.config.ts"
|
@@ -61,35 +87,4 @@ export default defineConfig({
|
|
61
87
|
});
|
62
88
|
```
|
63
89
|
|
64
|
-
|
65
|
-
|
66
|
-
In the previous chapter, we introduced what CSS-in-JS is and the CSS-in-JS library [styled-components](https://styled-components.com/) commonly used by the community. This section will explain how to use [Tailwind CSS](https://tailwindcss.com/) in CSS with [`Twin`](https://github.com/ben-rogerson/twin.macro). Using [`Twin`](https://github.com/ben-rogerson/twin.macro) makes it easier to use Tailwind CSS in CSS-in-JS code. [`Twin`](https://github.com/ben-rogerson/twin.macro) for its own description is:
|
67
|
-
|
68
|
-
> *Twin blends the magic of Tailwind with the flexibility of css-in-js*
|
69
|
-
|
70
|
-
After enabling the "Tailwind CSS" function, you first need to install the ['Twin'](https://github.com/ben-rogerson/twin.macro) dependency:
|
71
|
-
|
72
|
-
``` bash
|
73
|
-
pnpm add twin.macro -D
|
74
|
-
```
|
75
|
-
|
76
|
-
When the project installs the `twin.macro` dependency, Modern.js detects the dependency and adds twin.macro related configuration to the `baby-plugin-macro` of build-in. So after installing the dependency, there is no need for manual configuration. Here is an example of a simple use of twin.macro:
|
77
|
-
|
78
|
-
``` js
|
79
|
-
import tw from 'twin.macro'
|
80
|
-
|
81
|
-
const Input = tw.input`border hover:border-black`
|
82
|
-
```
|
83
|
-
|
84
|
-
:::tip
|
85
|
-
If a `MacroError: /project/App.tsx` error occurs during runtime, it may be caused by a lack of `twin.macro` dependency.
|
86
|
-
:::
|
87
|
-
|
88
|
-
For more usage, please refer to the [twin.macro](https://github.com/ben-rogerson/twin.macro/blob/master/docs/index.md).
|
89
|
-
|
90
|
-
`twin.macro` reads the `tailwindcss.config.js` files in the project directory by default, or reads the Tailwind CSS configuration via the file path specified by [twin.config](https://github.com/ben-rogerson/twin.macro/blob/master/docs/options.md#options) on the `babel-plugin-macro`. However, these additional configurations are not required in the Modern.js.
|
91
|
-
|
92
|
-
When the Tailwind CSS is configured in the `modern.config.ts` file by [`source.designSystem`](/docs/configure/app/source/design-system) and [`tools.tailwindcss`](/docs/configure/app/tools/tailwindcss), these configurations will also take effect on the `twin.macro`.
|
93
|
-
|
94
|
-
> When configuring Tailwind CSS for a project, the combination of the two configurations [`source.designSystem`](/docs/configure/app/source/design-system) and [`tools.tailwindcss`](/docs/configure/app/tools/tailwindcss) is equivalent to a separate configuration `tailwindcss.config.js` file.
|
95
|
-
> Where [`source.designSystem`](/docs/configure/app/source/design-system) is equivalent to the [`theme`](https://v2.tailwindcss.com/docs/configuration#theme) configuration of Tailwind CSS.
|
90
|
+
> When configuring Tailwind CSS for a project, the combination of the two configurations [source.designSystem](/docs/configure/app/source/design-system) and [tools.tailwindcss](/docs/configure/app/tools/tailwindcss) is equivalent to a separate configuration `tailwindcss.config.js` file. Where [source.designSystem](/docs/configure/app/source/design-system) is equivalent to the [theme](https://v2.tailwindcss.com/docs/configuration#theme) configuration of Tailwind CSS.
|
@@ -7,6 +7,406 @@ Modern.js provides out of the box fetching data capabilities, developers can use
|
|
7
7
|
|
8
8
|
It should be noted that these APIs do not help applications to initiate requests, but help developers better manage the relationship between data and routing.
|
9
9
|
|
10
|
+
## Data loader(recommend)
|
11
|
+
|
12
|
+
Modern.js recommends the use of conventional routing for route management. With Modern.js' [conventional (nested) routing](/docs/guides/basic-features/routes#conventional-routing), each routing component (`layout.ts` or `page.ts`) can export a function `loader` that can be executed before the component renders, providing data to the routing component.
|
13
|
+
|
14
|
+
:::info
|
15
|
+
Modern.js v1 supports getting data by [useLoader](#useloaderold), which is no longer the recommended usage and it is not recommended to mix both except for migration process.
|
16
|
+
:::
|
17
|
+
|
18
|
+
### Basic example
|
19
|
+
|
20
|
+
Each routing component can export a `loader` function and get the data via the `useLoaderData` function:
|
21
|
+
|
22
|
+
```ts
|
23
|
+
// routes/user/page.tsx
|
24
|
+
import { useLoaderData } from '@modern-js/runtime/router';
|
25
|
+
|
26
|
+
export const loader = async(): ProfileData => {
|
27
|
+
const res = await fetch('https://api/user/profile');
|
28
|
+
return await res.json();
|
29
|
+
}
|
30
|
+
|
31
|
+
export default function UserPage() {
|
32
|
+
const profileData = useLoaderData() as ProfileData;
|
33
|
+
return <div>{profileData}</div>;
|
34
|
+
}
|
35
|
+
```
|
36
|
+
|
37
|
+
In a CSR environment, the `loader` function is executed on the client side, and the browser API can be used within the `loader` function (but it is usually not needed and not recommended).
|
38
|
+
|
39
|
+
In an SSR environment, the `loader` function will only be executed on the server side, regardless of the first screen or the navigation on the client side, where any Node.js API can be called, and any dependencies and code used here will not be included in the client bundle.
|
40
|
+
|
41
|
+
:::info
|
42
|
+
In later versions, Modern.js may support `loader` functions running on the server side as well in CSR environments to improve performance and security, so here it is recommended to keep the loader as pure as possible and only do data fetching scenarios.
|
43
|
+
:::
|
44
|
+
|
45
|
+
When navigating on the client side, all loader functions under `/user` and `/user/profile` are executed (requested) in parallel based on Modern.js's [conventional routing](/docs/guides/basic-features/routes), i.e. when accessing `/user/profile`, the loader functions under `/user` and `/user/profile` are executed (requested) in parallel to improve client-side performance.
|
46
|
+
|
47
|
+
### Loader is defined in a separate file
|
48
|
+
|
49
|
+
In addition to supporting the export of `loader` functions from front-end components, Modern.js also supports placing `loader` functions in a separate file, by defining the `loader` function in a separate file, there is no need to pay attention to [side effect](#side-effects) related considerations, but it is important to note that routing components such as `layout.ts` and the loader file `layout.loader.ts` cannot introduce each other's code, if you need the related types you can use `import type`, you can see the following example:
|
50
|
+
|
51
|
+
```
|
52
|
+
.
|
53
|
+
└── routes
|
54
|
+
├── layout.tsx
|
55
|
+
└── user
|
56
|
+
├── layout.tsx
|
57
|
+
├── layout.loader.ts
|
58
|
+
├── page.tsx
|
59
|
+
└── page.loader.ts
|
60
|
+
```
|
61
|
+
|
62
|
+
For example, `loader` in [basic example](#basic-example) can be replaced with the following code:
|
63
|
+
|
64
|
+
```tsx
|
65
|
+
// routes/user/page.loader.tsx
|
66
|
+
type ProfileData = { /* some type declarations */ }
|
67
|
+
|
68
|
+
export const loader = async(): ProfileData => {
|
69
|
+
const res = await fetch('https://api/user/profile');
|
70
|
+
return await res.json();
|
71
|
+
}
|
72
|
+
|
73
|
+
// routes/user/page.tsx
|
74
|
+
import { useLoaderData } from '@modern-js/runtime/router';
|
75
|
+
// here you can only use import type
|
76
|
+
import type { ProfileData } from './page.loader';
|
77
|
+
|
78
|
+
export default function UserPage() {
|
79
|
+
const profileData = useLoaderData() as ProfileData;
|
80
|
+
return <div>{profileData}</div>;
|
81
|
+
}
|
82
|
+
```
|
83
|
+
|
84
|
+
### `loader` function
|
85
|
+
|
86
|
+
The `loader` function has two input parameters:
|
87
|
+
|
88
|
+
##### `Params`
|
89
|
+
|
90
|
+
When a routing file is passed through `[]`, it is passed as a [dynamic route](/docs/guides/basic-features/routes#dynamic-route) and the dynamic route fragment is passed as an argument to the loader function:
|
91
|
+
|
92
|
+
```tsx
|
93
|
+
// routes/user/[id]/page.tsx
|
94
|
+
import { LoaderArgs } from '@modern-js/runtime/router';
|
95
|
+
|
96
|
+
export const loader = async({ params }: LoaderArgs) => {
|
97
|
+
const { id } = params;
|
98
|
+
const res = await fetch(`https://api/user/${id}`);
|
99
|
+
return res.json();
|
100
|
+
}
|
101
|
+
```
|
102
|
+
|
103
|
+
当访问 `/user/123` 时,`loader` 函数的参数为 `{ params: { id: '123' } }`。
|
104
|
+
|
105
|
+
#### `request`
|
106
|
+
|
107
|
+
`request` is a [Fetch Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) instance.
|
108
|
+
|
109
|
+
A common usage scenario is to obtain query parameters via `request`:
|
110
|
+
```tsx
|
111
|
+
// routes/user/[id]/page.tsx
|
112
|
+
import { LoaderArgs } from '@modern-js/runtime/router';
|
113
|
+
|
114
|
+
export const loader = async({ request }: LoaderArgs) => {
|
115
|
+
const url = new URL(request.url);
|
116
|
+
const userId = url.searchParams.get("id");
|
117
|
+
return queryUser(userId);
|
118
|
+
}
|
119
|
+
```
|
120
|
+
|
121
|
+
#### Return value
|
122
|
+
|
123
|
+
`loader` 函数的返回值可以是任何可序列化的内容,也可以是一个 [Fetch Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) 实例:
|
124
|
+
|
125
|
+
```tsx
|
126
|
+
export const loader = async(): ProfileData => {
|
127
|
+
return {
|
128
|
+
message: 'hello world',
|
129
|
+
}
|
130
|
+
}
|
131
|
+
```
|
132
|
+
|
133
|
+
By default, the response `Content-type` returned by `loader` is `application/json` and `status` is 200, which you can set by customizing `Response`:
|
134
|
+
|
135
|
+
```tsx
|
136
|
+
export const loader = async(): ProfileData => {
|
137
|
+
const data = {message: 'hello world'};
|
138
|
+
return new Response(JSON.stringify(data), {
|
139
|
+
status: 200,
|
140
|
+
headers: {
|
141
|
+
"Content-Type": "application/json; utf-8",
|
142
|
+
},
|
143
|
+
});
|
144
|
+
}
|
145
|
+
```
|
146
|
+
|
147
|
+
### Request API
|
148
|
+
|
149
|
+
Modern.js does a polyfill of the `fetch` API to initiate requests, which is consistent with the browser's `fetch` API, but can also be used on the server side to initiate requests, meaning that both CSRs and SSRs can use the unified `fetch` API for data fetching:
|
150
|
+
|
151
|
+
```tsx
|
152
|
+
export async function loader(){
|
153
|
+
const res = await fetch('https://api/user/profile');
|
154
|
+
}
|
155
|
+
```
|
156
|
+
|
157
|
+
### Error handling
|
158
|
+
|
159
|
+
In the `loader` function, errors can be handled by `throw error` or `throw response`. When an error is thrown in the `loader` function, Modern.js will stop executing the code in the current loader and switch the front-end UI to the defined [`ErrorBoundary`](/docs/guides/basic-features/routes#errorboundary) component.
|
160
|
+
|
161
|
+
```tsx
|
162
|
+
// routes/user/profile/page.tsx
|
163
|
+
export async function loader(){
|
164
|
+
const res = await fetch('https://api/user/profile');
|
165
|
+
if(!res.ok){
|
166
|
+
throw res;
|
167
|
+
}
|
168
|
+
return res.json();
|
169
|
+
}
|
170
|
+
|
171
|
+
// routes/user/profile/error.tsx
|
172
|
+
import { useRouteError } from '@modern-js/runtime/router';
|
173
|
+
const ErrorBoundary = () => {
|
174
|
+
const error = useRouteError() as Response;
|
175
|
+
return (
|
176
|
+
<div>
|
177
|
+
<h1>{error.status}</h1>
|
178
|
+
<h2>{error.statusText}</h2>
|
179
|
+
</div>
|
180
|
+
);
|
181
|
+
};
|
182
|
+
|
183
|
+
export default ErrorBoundary;
|
184
|
+
```
|
185
|
+
|
186
|
+
### Get data from upper level components
|
187
|
+
|
188
|
+
In many cases, the child component needs to access the data in the ancestor's loader, and you can easily access the ancestor's data with `useRouteLoaderData`: `useRouteLoaderData`:
|
189
|
+
|
190
|
+
```tsx
|
191
|
+
// routes/user/profile/page.tsx
|
192
|
+
import { useRouteLoaderData } from '@modern-js/runtime/router';
|
193
|
+
|
194
|
+
export default function UserLayout() {
|
195
|
+
// Get the data returned by the loader in routes/user/layout.tsx
|
196
|
+
const data = useRouteLoaderData('user/layout');
|
197
|
+
return (
|
198
|
+
<div>
|
199
|
+
<h1>{data.name}</h1>
|
200
|
+
<h2>{data.age}</h2>
|
201
|
+
</div>
|
202
|
+
);
|
203
|
+
}
|
204
|
+
```
|
205
|
+
|
206
|
+
`userRouteLoaderData` takes one parameter `routeId`,When using conventional routing, Modern.js will automatically generate `routeId` for you. The value of `routeId` is the path of the corresponding component relative to `src/routes`, as in the example above, the child component wants to get the data returned by the loader in `routes/user/layout.tsx`, the value of `routeId` is `user/layout`.
|
207
|
+
|
208
|
+
In a multi-entry (MPA) scenario, the value of `routeId` needs to be added to the name of the corresponding entry, and the entry name is usually the entry directory name if not specified, such as the following directory structure:
|
209
|
+
```bash
|
210
|
+
.
|
211
|
+
└── src
|
212
|
+
├── entry1
|
213
|
+
│ ├── layout.tsx
|
214
|
+
└── entry2
|
215
|
+
└── layout.tsx
|
216
|
+
```
|
217
|
+
|
218
|
+
If you want to get the data returned by the loader in `entry1/layout.tsx`, the value of `routeId` is `entry1_layout`.
|
219
|
+
|
220
|
+
### (WIP)Loading UI
|
221
|
+
|
222
|
+
:::info
|
223
|
+
This feature is currently experimental and the API may be adjusted in the future.
|
224
|
+
Currently, only CSR is supported, so stay tuned for Streaming SSR.
|
225
|
+
:::
|
226
|
+
|
227
|
+
Because fetching data is asynchronous, it is often necessary to display a loading UI before the data fetching is complete, and the following is a basic example, assuming the following directory structure:
|
228
|
+
|
229
|
+
```bash
|
230
|
+
.
|
231
|
+
└── routes
|
232
|
+
├── layout.tsx
|
233
|
+
└── user
|
234
|
+
├── layout.tsx
|
235
|
+
└── page.ts
|
236
|
+
```
|
237
|
+
|
238
|
+
We get the user's detailed data in `user/layout.tsx` and want to show a loading UI before getting the data.
|
239
|
+
|
240
|
+
First, add a `loading.tsx` component to the `routes` directory in your project, which will take effect for all routes in the subdirectory (e.g. user):
|
241
|
+
```bash
|
242
|
+
.
|
243
|
+
└── routes
|
244
|
+
├── layout.tsx
|
245
|
+
├── loading.tsx
|
246
|
+
└── user
|
247
|
+
├── layout.tsx
|
248
|
+
└── page.ts
|
249
|
+
```
|
250
|
+
|
251
|
+
:::info
|
252
|
+
For specific usage of the loading component, see [loading](/docs/guides/basic-features/routes#loading)
|
253
|
+
:::
|
254
|
+
|
255
|
+
Then, add the following code to `user/layout.tsx`:
|
256
|
+
|
257
|
+
```tsx title="routes/user/layout.tsx"
|
258
|
+
import {
|
259
|
+
Await,
|
260
|
+
defer,
|
261
|
+
useLoaderData,
|
262
|
+
Outlet
|
263
|
+
} from '@modern-js/runtime/router';
|
264
|
+
|
265
|
+
export const loader = () => {
|
266
|
+
return defer({
|
267
|
+
// fetchUserInfo 是一个异步函数,返回用户信息
|
268
|
+
userInfo: fetchUserInfo(),
|
269
|
+
})
|
270
|
+
}
|
271
|
+
|
272
|
+
export default function UserLayout() {
|
273
|
+
const { userInfo } = useLoaderData() as {userInfo: Promise<UserInfo>};
|
274
|
+
return (
|
275
|
+
<div>
|
276
|
+
<Await resolve={userInfo} children={userInfo => (
|
277
|
+
<div>
|
278
|
+
<span>{userInfo.name}</span>
|
279
|
+
<span>{userInfo.age}</span>
|
280
|
+
<Outlet>
|
281
|
+
</div>
|
282
|
+
)}>
|
283
|
+
</Await>
|
284
|
+
</div>
|
285
|
+
);
|
286
|
+
}
|
287
|
+
```
|
288
|
+
|
289
|
+
:::info
|
290
|
+
For specific usage of the Await component, see [Await](/docs/guides/basic-features/routes#await)
|
291
|
+
|
292
|
+
For specific usage of the defer function, see[defer](https://reactrouter.com/en/main/guides/deferred)
|
293
|
+
:::
|
294
|
+
|
295
|
+
### Side Effects
|
296
|
+
|
297
|
+
As mentioned above, Modern.js removes the server-side code `loader` from the client bundle, there are some things you need to be aware of, if you don't want to suffer from side effects, you can define `loader` in a [separate file](#loader-is-defined-in-a-separate-file).
|
298
|
+
|
299
|
+
:::info
|
300
|
+
In CSR scenarios, the side effects usually have less impact on the project, but you are still expected to follow the following conventions.
|
301
|
+
:::
|
302
|
+
|
303
|
+
Side effects of a module can be thought of as code that is executed when the module is loaded, such as:
|
304
|
+
|
305
|
+
```tsx
|
306
|
+
// routes/user/page.tsx
|
307
|
+
import { useLoaderData } from '@modern-js/runtime/router';
|
308
|
+
// highlight-next-line
|
309
|
+
import { transformProfile } from "./utils";
|
310
|
+
// highlight-next-line
|
311
|
+
console.log(transformProfile);
|
312
|
+
|
313
|
+
export const loader = async(): ProfileData => {
|
314
|
+
const res = await fetch('https://api/user/profile');
|
315
|
+
const data = await res.json();
|
316
|
+
return transformProfile(data);
|
317
|
+
}
|
318
|
+
|
319
|
+
export default function UserPage() {
|
320
|
+
const profileData = useLoaderData() as ProfileData;
|
321
|
+
return <div>{profileData}</div>;
|
322
|
+
}
|
323
|
+
```
|
324
|
+
|
325
|
+
Since `console.log` is executed when `routes/user/page.tsx` is loaded, the compiler does not remove it and `transformProfile` is bundled into the client bundle.
|
326
|
+
|
327
|
+
So we do not recommend having any code executed when the module is loaded, including project code and third-party modules used, and the following are some of the ways we do not recommend writing.
|
328
|
+
|
329
|
+
```tsx
|
330
|
+
// routes/user/page.tsx
|
331
|
+
export default function UserPage() {
|
332
|
+
return <div>profile</div>;
|
333
|
+
}
|
334
|
+
|
335
|
+
UserPage.config = {}
|
336
|
+
```
|
337
|
+
|
338
|
+
```tsx
|
339
|
+
// routes/init.ts
|
340
|
+
document.title = 'Modern.js';
|
341
|
+
|
342
|
+
// routes/layout.tsx
|
343
|
+
import "./init.ts";
|
344
|
+
|
345
|
+
export default function Layout() {
|
346
|
+
return <></>
|
347
|
+
}
|
348
|
+
```
|
349
|
+
|
350
|
+
In SSR scenarios, you usually need to use Server Side packages in the `loader` function, and for non-Node.js core packages, you need to do a re-export using a specially agreed file, such as using `fs-extra`:
|
351
|
+
|
352
|
+
```tsx
|
353
|
+
// routes/user/avatar.tsx
|
354
|
+
import { useLoaderData } from '@modern-js/runtime/router';
|
355
|
+
import { readFile } from './utils.server';
|
356
|
+
|
357
|
+
type ProfileData = { /* some type declarations */ }
|
358
|
+
|
359
|
+
export const loader = async(): ProfileData => {
|
360
|
+
const profile = await readFile('profile.json');
|
361
|
+
return profile;
|
362
|
+
}
|
363
|
+
|
364
|
+
export default function UserPage() {
|
365
|
+
const profileData = useLoaderData() as ProfileData;
|
366
|
+
return <div>{profileData}</div>;
|
367
|
+
}
|
368
|
+
|
369
|
+
// routes/user/utils.server.ts
|
370
|
+
export * from 'fs-extra';
|
371
|
+
```
|
372
|
+
|
373
|
+
### Wrong usage
|
374
|
+
|
375
|
+
1. Only serializable data can be returned in `loader`. In SSR environments, the return value of the `loader` function is serialized to a JSON string, which is then deserialized to an object on the client side. Therefore, no non-serializable data (such as functions) can be returned in the `loader` function.
|
376
|
+
|
377
|
+
:::warning
|
378
|
+
This restriction is not currently in place under CSR, but we strongly recommend that you follow it, and we may add it under CSR in the future.
|
379
|
+
:::
|
380
|
+
|
381
|
+
```ts
|
382
|
+
// This won't work!
|
383
|
+
export const loader = () => {
|
384
|
+
return {
|
385
|
+
user: {},
|
386
|
+
method: () => {
|
387
|
+
|
388
|
+
}
|
389
|
+
}
|
390
|
+
}
|
391
|
+
```
|
392
|
+
|
393
|
+
2. Modern.js will call the `loader` function for you, you shouldn't call it yourself in the component.
|
394
|
+
|
395
|
+
```tsx
|
396
|
+
// This won't work!
|
397
|
+
export const loader = async () => {
|
398
|
+
const res = fetch('https://api/user/profile');
|
399
|
+
return res.json();
|
400
|
+
};
|
401
|
+
|
402
|
+
export default function RouteComp() {
|
403
|
+
const data = loader();
|
404
|
+
}
|
405
|
+
```
|
406
|
+
|
407
|
+
3. When run on the server side, the `loader` functions are packaged into a single bundle, so we do not recommend using `__filename` and `__dirname` for server-side code.
|
408
|
+
|
409
|
+
|
10
410
|
## useLoader(Old)
|
11
411
|
|
12
412
|
**`useLoader`** is an API in Modern.js old version. The API is a React Hook specially provided for SSR applications, allowing developers to fetch data in components.
|
@@ -0,0 +1,166 @@
|
|
1
|
+
---
|
2
|
+
title: Environment Variable
|
3
|
+
sidebar_position: 7
|
4
|
+
---
|
5
|
+
|
6
|
+
Modern.js provides support for environment variables, including built-in environment variables and custom environment variables.
|
7
|
+
|
8
|
+
## Built-in Environment
|
9
|
+
|
10
|
+
### NODE_ENV
|
11
|
+
|
12
|
+
The current execution environment and is a **read-only** environment variable whose have different values under different execution commands:
|
13
|
+
|
14
|
+
- `production`:the default value when exec `modern build` or `modern serve`.
|
15
|
+
- `test`:the default value when exec `modern test`.
|
16
|
+
- `development`:the default value when exec `modern dev`, alse the default value of other case.
|
17
|
+
|
18
|
+
### MODERN_ENV
|
19
|
+
|
20
|
+
Set the current execution environment manually. In addition to the values in the NODE_ENV, custom environment names are supported here, such as `staging`, `boe`, etc.
|
21
|
+
|
22
|
+
:::tip
|
23
|
+
MODERN_ENV priority is higher than NODE_ENV.
|
24
|
+
:::
|
25
|
+
|
26
|
+
### MODERN_TARGET
|
27
|
+
|
28
|
+
Auto inject when use `@modern-js/runtime`,Used to distinguish between SSR and CSR environments. Developers can judge by themselves in the code, and dead code will be removed by default when building.
|
29
|
+
|
30
|
+
```ts title="App.tsx"
|
31
|
+
function App() {
|
32
|
+
if (process.env.MODERN_TARGET === 'browser') {
|
33
|
+
console.log(window.innerHeight);
|
34
|
+
};
|
35
|
+
};
|
36
|
+
```
|
37
|
+
|
38
|
+
In the development environment, you can see that the SSR and CSR bundles as follows:
|
39
|
+
|
40
|
+
```js title="dist/bundles/main.js"
|
41
|
+
function App() {
|
42
|
+
if (false) {}
|
43
|
+
}
|
44
|
+
```
|
45
|
+
|
46
|
+
```js title="dist/static/main.js"
|
47
|
+
function App() {
|
48
|
+
if (true) {
|
49
|
+
console.log(window.innerHeight);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
```
|
53
|
+
|
54
|
+
:::note
|
55
|
+
In a production environment, dead code is removed, such as the `if` statement above.
|
56
|
+
:::
|
57
|
+
|
58
|
+
This can provide different products for different client sides to ensure that the bundle size is minimized. It can also be convenient to deal with some side effects in the code in different environments.
|
59
|
+
|
60
|
+
## Custom Environment Variables
|
61
|
+
|
62
|
+
Custom environment variables can be specified in both `shell` and `.env` files.
|
63
|
+
|
64
|
+
### Specify via `shell`
|
65
|
+
|
66
|
+
Add custom environment variables before the command:
|
67
|
+
|
68
|
+
```shell
|
69
|
+
REACT_APP_FOO=123 BAR=456 pnpm run dev
|
70
|
+
```
|
71
|
+
|
72
|
+
### Specify via `.env` file
|
73
|
+
|
74
|
+
Create a `.env` file in the project root and add custom environment variables, which are added to the Node.js process by default, for example:
|
75
|
+
|
76
|
+
```env
|
77
|
+
REACT_APP_FOO=123
|
78
|
+
BAR=456
|
79
|
+
```
|
80
|
+
|
81
|
+
The `.env` file follows the following loading rules:
|
82
|
+
|
83
|
+
* `.env`:default.
|
84
|
+
* `.env.{ MODERN_ENV | NODE_ENV }`:Setting environment variables for a specific environment overrides the same in `.env`.
|
85
|
+
|
86
|
+
When you need to use different config according to the environment, you can define environment variables in the `.env` file corresponding to the environment name, and manually set the execution environment when starting the project.
|
87
|
+
|
88
|
+
For example, when starting a project with the following command,the `.env` and `.env.staging` will load:
|
89
|
+
|
90
|
+
```shell
|
91
|
+
MODERN_ENV=staging pnpm run dev
|
92
|
+
```
|
93
|
+
|
94
|
+
## Using Environment Variables
|
95
|
+
|
96
|
+
### Convention Names
|
97
|
+
|
98
|
+
`NODE_ENV` can be used directly in front-end code. In addition, custom environment variables starting with `MODERN_` can also be used directly in code.
|
99
|
+
|
100
|
+
For Example:
|
101
|
+
|
102
|
+
```js
|
103
|
+
if (process.env.NODE_ENV === 'development') {
|
104
|
+
// do something
|
105
|
+
}
|
106
|
+
```
|
107
|
+
|
108
|
+
After executing the `pnpm run dev`, you can see the following bundle:
|
109
|
+
|
110
|
+
```js
|
111
|
+
if (true) {
|
112
|
+
// do something
|
113
|
+
}
|
114
|
+
```
|
115
|
+
|
116
|
+
In custom HTML templates, you can also use such environment variables directly. For example, in `config/html/head.html`:
|
117
|
+
|
118
|
+
```html
|
119
|
+
<meta name="test" content="<process.env.NODE_ENV>">
|
120
|
+
```
|
121
|
+
|
122
|
+
### Any Other Names
|
123
|
+
|
124
|
+
If you need to use environment variables with any other names in your code,you can config [`source.globalVars`](/docs/configure/app/source/global-vars), for example:
|
125
|
+
|
126
|
+
```typescript title="modern.config.ts"
|
127
|
+
export default defineConfig({
|
128
|
+
source: {
|
129
|
+
globalVars: {
|
130
|
+
'process.env.VERSION': process.env.VERSION,
|
131
|
+
}.
|
132
|
+
},
|
133
|
+
});
|
134
|
+
```
|
135
|
+
|
136
|
+
At this point, the `process.env.VERSION` in the code will be replaced with the value of `VERSION` in the environment variables.
|
137
|
+
|
138
|
+
:::note
|
139
|
+
`source.globalVars` also supports replacing other expressions or strings with specified values, not limited to environment variables.
|
140
|
+
:::
|
141
|
+
|
142
|
+
## Use Global Replacement
|
143
|
+
|
144
|
+
In addition to environment variables, Modern.js also supports replacing variables in code with other values or expressions, which can be used like distinguish development environment and production environment in code.
|
145
|
+
|
146
|
+
|
147
|
+
For example, converts the expression `TWO` to `1 + 1`:
|
148
|
+
|
149
|
+
```ts
|
150
|
+
export default {
|
151
|
+
source: {
|
152
|
+
define: {
|
153
|
+
TWO: '1 + 1',
|
154
|
+
},
|
155
|
+
},
|
156
|
+
};
|
157
|
+
```
|
158
|
+
|
159
|
+
```ts
|
160
|
+
const foo = TWO;
|
161
|
+
|
162
|
+
// ⬇️ Turn into being...
|
163
|
+
const foo = 1 + 1;
|
164
|
+
```
|
165
|
+
|
166
|
+
In most cases, `source.globalVars` is already sufficient to replace variables. But the values passed in by `source.globalVars` will be serialized by JSON by default. So it cannot be replaced like `1 + 1` in the example above,at this time, we need use [`source.define`](/docs/configure/app/source/define)。
|