@modern-js/main-doc 2.0.0-beta.6 → 2.0.0-canary.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/en/docusaurus-plugin-content-docs/current/apis/app/hooks/src/index_.md +1 -1
  3. package/en/docusaurus-plugin-content-docs/current/apis/app/hooks/src/pages.md +1 -1
  4. package/en/docusaurus-plugin-content-docs/current/apis/app/hooks/src/routes.md +86 -0
  5. package/en/docusaurus-plugin-content-docs/current/components/enable-bff.md +36 -0
  6. package/en/docusaurus-plugin-content-docs/current/components/global-proxy-config.md +74 -0
  7. package/en/docusaurus-plugin-content-docs/current/components/global-proxy.md +28 -0
  8. package/en/docusaurus-plugin-content-docs/current/configure/app/dev/proxy.md +2 -72
  9. package/en/docusaurus-plugin-content-docs/current/configure/app/source/entries.md +0 -2
  10. package/en/docusaurus-plugin-content-docs/current/configure/app/tools/tailwindcss.md +16 -22
  11. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/_category_.json +8 -0
  12. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/bff-proxy.md +27 -0
  13. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/frameworks.md +150 -0
  14. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/function.md +222 -0
  15. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/index.md +20 -0
  16. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/type.md +43 -0
  17. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/code-split.md +77 -0
  18. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/compatibility.md +76 -0
  19. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/eslint.md +145 -0
  20. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/index.md +12 -0
  21. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/low-level.md +46 -0
  22. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/ssg.md +128 -0
  23. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/ssr.md +306 -0
  24. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/testing.md +46 -0
  25. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/web-server.md +57 -0
  26. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/alias.md +67 -0
  27. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/css/tailwindcss.md +30 -35
  28. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/data-fetch.md +400 -0
  29. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/env-vars.md +166 -0
  30. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/html.md +235 -0
  31. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/mock.md +78 -0
  32. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/proxy.md +60 -0
  33. package/en/docusaurus-plugin-content-docs/current/guides/get-started/quick-start.md +2 -4
  34. package/package.json +3 -3
  35. package/zh/apis/app/hooks/src/index_.md +1 -1
  36. package/zh/apis/app/hooks/src/pages.md +1 -1
  37. package/zh/apis/app/hooks/src/routes.md +89 -0
  38. package/zh/components/debug-app.md +1 -2
  39. package/zh/components/enable-bff.md +36 -0
  40. package/zh/components/global-proxy-config.md +70 -0
  41. package/zh/configure/app/dev/proxy.md +2 -70
  42. package/zh/configure/app/source/entries.md +1 -3
  43. package/zh/configure/app/tools/tailwindcss.md +16 -23
  44. package/zh/guides/advanced-features/bff/frameworks.md +2 -0
  45. package/zh/guides/advanced-features/bff/function.md +44 -24
  46. package/zh/guides/advanced-features/code-split.md +28 -20
  47. package/zh/guides/advanced-features/compatibility.md +24 -14
  48. package/zh/guides/advanced-features/ssg.md +1 -47
  49. package/zh/guides/advanced-features/ssr.md +1 -1
  50. package/zh/guides/advanced-features/testing.md +2 -2
  51. package/zh/guides/basic-features/alias.md +5 -5
  52. package/zh/guides/basic-features/css/tailwindcss.md +31 -35
  53. package/zh/guides/basic-features/data-fetch.md +7 -6
  54. package/zh/guides/basic-features/env-vars.md +2 -2
  55. package/zh/guides/basic-features/html.md +62 -137
  56. package/zh/guides/basic-features/mock.md +8 -9
  57. package/zh/guides/basic-features/proxy.md +2 -2
  58. package/zh/guides/basic-features/routes.md +37 -3
  59. package/zh/guides/get-started/quick-start.md +1 -2
  60. package/zh/guides/topic-detail/framework-plugin/implement.md +54 -6
  61. 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
- ## Use the [`Twin`](https://github.com/ben-rogerson/twin.macro)
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)。