@modern-js/main-doc 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/en/apis/app/commands.mdx +297 -0
- package/en/apis/app/hooks/_category_.json +1 -1
- package/en/apis/app/hooks/config/upload.mdx +10 -0
- package/en/apis/app/hooks/src/routes.mdx +2 -2
- package/en/components/init-app.mdx +5 -5
- package/en/configure/app/bff/prefix.mdx +2 -3
- package/en/configure/app/bff/proxy.mdx +1 -1
- package/en/configure/app/dev/before-start-url.mdx +13 -0
- package/en/configure/app/dev/host.mdx +13 -0
- package/en/configure/app/output/ssg.mdx +3 -3
- package/en/configure/app/runtime/master-app.mdx +1 -1
- package/en/configure/app/server/ssr.mdx +18 -0
- package/en/configure/app/source/entries-dir.mdx +1 -1
- package/en/configure/app/testing/transformer.mdx +1 -1
- package/en/configure/app/tools/jest.mdx +1 -1
- package/en/guides/advanced-features/rspack-start.mdx +69 -0
- package/en/guides/advanced-features/ssg.mdx +8 -8
- package/en/guides/advanced-features/ssr.mdx +210 -5
- package/en/guides/basic-features/alias.mdx +4 -4
- package/en/guides/{css/tailwindcss.mdx → basic-features/css.mdx} +60 -3
- package/en/guides/basic-features/data-fetch.mdx +4 -4
- package/en/guides/basic-features/env-vars.mdx +4 -0
- package/en/guides/basic-features/routes.mdx +23 -7
- package/en/guides/concept/builder.mdx +1 -1
- package/en/guides/get-started/quick-start.mdx +2 -2
- package/en/guides/topic-detail/micro-frontend/c03-main-app.mdx +1 -1
- package/en/tutorials/first-app/c03-css.mdx +1 -1
- package/en/tutorials/first-app/c07-container.mdx +17 -17
- package/en/tutorials/first-app/c08-entries.mdx +23 -23
- package/package.json +3 -3
- package/scripts/summary.en.json +1 -1
- package/scripts/summary.zh.json +1 -1
- package/zh/apis/app/commands.mdx +299 -0
- package/zh/apis/app/hooks/_category_.json +1 -1
- package/zh/apis/app/hooks/config/upload.mdx +12 -2
- package/zh/apis/app/hooks/src/routes.mdx +2 -2
- package/zh/components/init-app.mdx +5 -5
- package/zh/configure/app/bff/prefix.mdx +1 -1
- package/zh/configure/app/bff/proxy.mdx +1 -1
- package/zh/configure/app/dev/before-start-url.mdx +13 -0
- package/zh/configure/app/dev/host.mdx +13 -0
- package/zh/configure/app/output/ssg.mdx +3 -3
- package/zh/configure/app/server/ssr.mdx +19 -1
- package/zh/configure/app/source/entries-dir.mdx +1 -1
- package/zh/guides/advanced-features/rspack-start.mdx +69 -0
- package/zh/guides/advanced-features/ssg.mdx +8 -8
- package/zh/guides/advanced-features/ssr.mdx +213 -6
- package/zh/guides/basic-features/alias.mdx +4 -4
- package/zh/guides/{css/tailwindcss.mdx → basic-features/css.mdx} +60 -3
- package/zh/guides/basic-features/data-fetch.mdx +4 -4
- package/zh/guides/basic-features/env-vars.mdx +4 -0
- package/zh/guides/basic-features/routes.mdx +23 -7
- package/zh/guides/concept/builder.mdx +2 -2
- package/zh/guides/get-started/quick-start.mdx +2 -2
- package/zh/guides/topic-detail/micro-frontend/c03-main-app.mdx +1 -1
- package/zh/tutorials/first-app/c03-css.mdx +1 -1
- package/zh/tutorials/first-app/c07-container.mdx +17 -17
- package/zh/tutorials/first-app/c08-entries.mdx +23 -23
- package/en/apis/app/commands/_category_.json +0 -5
- package/en/apis/app/commands/build.mdx +0 -39
- package/en/apis/app/commands/dev.mdx +0 -61
- package/en/apis/app/commands/inspect.mdx +0 -61
- package/en/apis/app/commands/lint.mdx +0 -19
- package/en/apis/app/commands/new.mdx +0 -55
- package/en/apis/app/commands/serve.mdx +0 -27
- package/en/apis/app/commands/test.mdx +0 -35
- package/en/apis/app/commands/upgrade.mdx +0 -18
- package/en/guides/css/_category_.json +0 -5
- package/en/guides/css/css-in-js.mdx +0 -40
- package/en/guides/css/css-modules.mdx +0 -87
- package/en/guides/css/less-sass.mdx +0 -17
- package/en/guides/css/postcss.mdx +0 -79
- package/zh/apis/app/commands/_category_.json +0 -5
- package/zh/apis/app/commands/build.mdx +0 -39
- package/zh/apis/app/commands/dev.mdx +0 -61
- package/zh/apis/app/commands/inspect.mdx +0 -61
- package/zh/apis/app/commands/lint.mdx +0 -19
- package/zh/apis/app/commands/new.mdx +0 -54
- package/zh/apis/app/commands/serve.mdx +0 -27
- package/zh/apis/app/commands/test.mdx +0 -35
- package/zh/apis/app/commands/upgrade.mdx +0 -18
- package/zh/guides/css/_category_.json +0 -5
- package/zh/guides/css/css-in-js.mdx +0 -40
- package/zh/guides/css/css-modules.mdx +0 -87
- package/zh/guides/css/less-sass.mdx +0 -17
- package/zh/guides/css/postcss.mdx +0 -80
|
@@ -44,8 +44,9 @@ And it provides elegant degradation processing. Once the SSR request fails, it w
|
|
|
44
44
|
However, developers still need to pay attention to the fallback of data, such as `null` values or data returns that do not as expect. Avoid React rendering errors or messy rendering results when SSR.
|
|
45
45
|
|
|
46
46
|
:::info
|
|
47
|
-
When
|
|
47
|
+
1. When you request the page on client-side page transitions, Modern.js sends an API request to the server, which runs Data Loader function.
|
|
48
48
|
|
|
49
|
+
2. When using Data Loader, data fetching happens before rendering, Modern.js still supports fetching data when the component is rendered. See [Data Fetch](/guides/basic-features/data-fetch).
|
|
49
50
|
:::
|
|
50
51
|
|
|
51
52
|
## Keep Rendering Consistent
|
|
@@ -288,9 +289,9 @@ In addition, some backend interfaces, or general gateways, will verify according
|
|
|
288
289
|
|
|
289
290
|
Be sure to filter the `host` field if you really need to pass through all request headers.
|
|
290
291
|
|
|
291
|
-
##
|
|
292
|
+
## Streaming SSR
|
|
292
293
|
|
|
293
|
-
Modern.js supports streaming rendering in React 18
|
|
294
|
+
Modern.js supports streaming rendering in React 18. Opt in it with the following configuration:
|
|
294
295
|
|
|
295
296
|
```json
|
|
296
297
|
{
|
|
@@ -302,7 +303,211 @@ Modern.js supports streaming rendering in React 18, the default rendering mode c
|
|
|
302
303
|
}
|
|
303
304
|
```
|
|
304
305
|
|
|
305
|
-
|
|
306
|
-
|
|
306
|
+
The streaming SSR of Modern.js is implemented based on React Router, and the main APIs involved are:
|
|
307
|
+
|
|
308
|
+
- [`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.
|
|
309
|
+
- [`Await`](https://reactrouter.com/en/main/components/await): Used to render deferred values with automatic error handling.
|
|
310
|
+
- [`useAsyncValue`](https://reactrouter.com/en/main/hooks/use-async-value):Returns the resolved data from the nearest `<Await>` ancestor component.
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
### Return async data
|
|
314
|
+
|
|
315
|
+
```ts title='page.loader.ts'
|
|
316
|
+
import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
|
|
317
|
+
|
|
318
|
+
interface User {
|
|
319
|
+
name: string;
|
|
320
|
+
age: number;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export interface Data {
|
|
324
|
+
data: User;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export default ({ params }: LoaderFunctionArgs) => {
|
|
328
|
+
const userId = params.id;
|
|
329
|
+
|
|
330
|
+
const user = new Promise<User>(resolve => {
|
|
331
|
+
setTimeout(() => {
|
|
332
|
+
resolve({
|
|
333
|
+
name: `user-${userId}`,
|
|
334
|
+
age: 18,
|
|
335
|
+
});
|
|
336
|
+
}, 200);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
return defer({ data: user });
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
`user` is of `Promise` type, which means the data will be obtained asynchronously. Note that `defer` must accept an object type parameter,
|
|
345
|
+
therefore, the parameter passed to `defer` is `{data: user}`.
|
|
346
|
+
|
|
347
|
+
`defer` can also receive asynchronous data and synchronous data at the same time. For example:
|
|
348
|
+
|
|
349
|
+
```ts title='page.loader.ts'
|
|
350
|
+
|
|
351
|
+
// skip some codes
|
|
352
|
+
|
|
353
|
+
export default ({ params }: LoaderFunctionArgs) => {
|
|
354
|
+
const userId = params.id;
|
|
355
|
+
|
|
356
|
+
const user = new Promise<User>(resolve => {
|
|
357
|
+
setTimeout(() => {
|
|
358
|
+
resolve({
|
|
359
|
+
name: `user-${userId}`,
|
|
360
|
+
age: 18,
|
|
361
|
+
});
|
|
362
|
+
}, 200);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const otherData = new Promise<string>(resolve => {
|
|
366
|
+
setTimeout(() => {
|
|
367
|
+
resolve('some sync data');
|
|
368
|
+
}, 200);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
return defer({
|
|
372
|
+
data: user,
|
|
373
|
+
other: await otherData
|
|
374
|
+
});
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
`await` is added before `otherData`, so the data is obtained synchronously. It can be passed to `defer` with the data `user` at the same time.
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
### Render async data
|
|
383
|
+
|
|
384
|
+
Use the `Await` component to render the data returned asynchronously from the Data Loader. For example:
|
|
385
|
+
|
|
386
|
+
```ts title='page.tsx'
|
|
387
|
+
import { Await, useLoaderData } from '@modern-js/runtime/router';
|
|
388
|
+
import { Suspense } from 'react';
|
|
389
|
+
import type { Data } from './page.loader';
|
|
390
|
+
|
|
391
|
+
const Page = () => {
|
|
392
|
+
const data = useLoaderData() as Data;
|
|
393
|
+
|
|
394
|
+
return (
|
|
395
|
+
<div>
|
|
396
|
+
User info:
|
|
397
|
+
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
|
398
|
+
<Await resolve={data.data}>
|
|
399
|
+
{(user) => {
|
|
400
|
+
return (
|
|
401
|
+
<div id="data">
|
|
402
|
+
name: {user.name}, age: {user.age}
|
|
403
|
+
</div>
|
|
404
|
+
);
|
|
405
|
+
}}
|
|
406
|
+
</Await>
|
|
407
|
+
</Suspense>
|
|
408
|
+
</div>
|
|
409
|
+
);
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
export default Page;
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
`Await` needs to be wrapped inside the `Suspense` component. The `resolve` of `Await` passes in the data acquired asynchronously by the Data Loader. When the data acquisition is completed,
|
|
416
|
+
the obtained data is rendered through the [Render Props](https://reactjs.org/docs/render-props.html) mode. When the data acquisition is in pending status, the
|
|
417
|
+
content set by the `fallback` property of the `Suspense` component will display.
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
:::warning Warning
|
|
421
|
+
When importing a type from a Data Loader file, you need to use the `import type` syntax to ensure that only type information is imported, which can prevent the Data Loader code from being packaged into the client bundle.
|
|
422
|
+
|
|
423
|
+
So, here we import like this: `import type { Data } from './page.loader'`;
|
|
424
|
+
:::
|
|
425
|
+
|
|
426
|
+
You can also get the asynchronous data returned by Data Loader through `useAsyncValue`. For example:
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
```ts title='page.tsx'
|
|
430
|
+
import { useAsyncValue } from '@modern-js/runtime/router';
|
|
431
|
+
|
|
432
|
+
// skip some codes
|
|
433
|
+
|
|
434
|
+
const UserInfo = () => {
|
|
435
|
+
const user = useAsyncValue();
|
|
436
|
+
|
|
437
|
+
return (
|
|
438
|
+
<div>
|
|
439
|
+
name: {user.name}, age: {user.age}
|
|
440
|
+
</div>
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const Page = () => {
|
|
445
|
+
const data = useLoaderData() as Data;
|
|
446
|
+
|
|
447
|
+
return (
|
|
448
|
+
<div>
|
|
449
|
+
User info:
|
|
450
|
+
<Suspense fallback={<div id="loading">loading user data ...</div>}>
|
|
451
|
+
<Await resolve={data.data}>
|
|
452
|
+
<UserInfo />
|
|
453
|
+
</Await>
|
|
454
|
+
</Suspense>
|
|
455
|
+
</div>
|
|
456
|
+
);
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
export default Page;
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Error handling
|
|
463
|
+
|
|
464
|
+
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.
|
|
465
|
+
For example, we intentionally throw an error in the Data Loader function:
|
|
466
|
+
|
|
467
|
+
```ts title='page.loader.ts'
|
|
468
|
+
import { defer } from '@modern-js/runtime/router';
|
|
469
|
+
|
|
470
|
+
export default () => {
|
|
471
|
+
const data = new Promise((resolve, reject) => {
|
|
472
|
+
setTimeout(() => {
|
|
473
|
+
reject(new Error('error occurs'));
|
|
474
|
+
}, 200);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
return defer({ data });
|
|
478
|
+
};
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Then use `useAsyncError` to get the error, and assign the component used to render the error to the `errorElement` property of the `Await` component:
|
|
482
|
+
|
|
483
|
+
```ts title='page.ts'
|
|
484
|
+
import { Await, useAsyncError, useLoaderData } from '@modern-js/runtime/router';
|
|
485
|
+
import { Suspense } from 'react';
|
|
486
|
+
|
|
487
|
+
export default function Page() {
|
|
488
|
+
const data = useLoaderData();
|
|
489
|
+
|
|
490
|
+
return (
|
|
491
|
+
<div>
|
|
492
|
+
Error page
|
|
493
|
+
<Suspense fallback={<div>loading ...</div>}>
|
|
494
|
+
<Await resolve={data.data} errorElement={<ErrorElement />}>
|
|
495
|
+
{(data: any) => {
|
|
496
|
+
return <div>never displayed</div>;
|
|
497
|
+
}}
|
|
498
|
+
</Await>
|
|
499
|
+
</Suspense>
|
|
500
|
+
</div>
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function ErrorElement() {
|
|
505
|
+
const error = useAsyncError() as Error;
|
|
506
|
+
return <p>Something went wrong! {error.message}</p>;
|
|
507
|
+
}
|
|
508
|
+
```
|
|
307
509
|
|
|
510
|
+
:::info More
|
|
511
|
+
1. [Deferred Data](https://reactrouter.com/en/main/guides/deferred)
|
|
512
|
+
2. [New Suspense SSR Architecture in React 18](https://github.com/reactwg/react-18/discussions/37)
|
|
308
513
|
:::
|
|
@@ -23,10 +23,10 @@ For example, import the modules from the `src/common/` directory in the `src/App
|
|
|
23
23
|
```bash
|
|
24
24
|
.
|
|
25
25
|
├── common
|
|
26
|
-
│
|
|
27
|
-
│
|
|
28
|
-
│
|
|
29
|
-
│
|
|
26
|
+
│ ├── styles
|
|
27
|
+
│ │ └── base.css
|
|
28
|
+
│ └── utils
|
|
29
|
+
│ └── index.ts
|
|
30
30
|
├── App.tsx
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -2,7 +2,64 @@
|
|
|
2
2
|
sidebar_position: 2
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# CSS Solutions
|
|
6
|
+
|
|
7
|
+
Modern.js has built-in multiple CSS solutions, including Less / Sass / Stylus preprocessor, PostCSS, CSS Modules, CSS-in-JS and Tailwind CSS.
|
|
8
|
+
|
|
9
|
+
## Using Less, Sass and Stylus
|
|
10
|
+
|
|
11
|
+
Modern.js has built-in community popular CSS preprocessors such as Less, Sass.
|
|
12
|
+
|
|
13
|
+
By default, you don't need to configure anything for Less and Sass. If you need to customize loader config, you can configure [tools.less](/configure/app/tools/less), [tools.sass](/configure/app/tools/sass) to set it up.
|
|
14
|
+
|
|
15
|
+
You can also use Stylus in Modern.js, just install the Stylus plugin provided by Modern.js Builder, please refer to [Stylus Plugin](https://modernjs.dev/builder/en/plugins/plugin-stylus.html) for usage.
|
|
16
|
+
|
|
17
|
+
## Using PostCSS
|
|
18
|
+
|
|
19
|
+
Modern.js has built-in [PostCSS](https://postcss.org/) to transform the CSS code.
|
|
20
|
+
|
|
21
|
+
Please refer to [Modern.js Builder - Using PostCSS](https://modernjs.dev/builder/en/guide/basic/css-usage.html#using-postcss) for detailed usage.
|
|
22
|
+
|
|
23
|
+
## Using CSS Modules
|
|
24
|
+
|
|
25
|
+
Please read the [Using CSS Modules](https://modernjs.dev/builder/en/guide/basic/css-modules.html) chapter for a complete usage of CSS Modules.
|
|
26
|
+
|
|
27
|
+
## Using CSS-in-JS
|
|
28
|
+
|
|
29
|
+
CSS-in-JS is a technology that can write CSS styles in JS files.
|
|
30
|
+
|
|
31
|
+
Modern.js integrates the CSS-in-JS library [styled-components](https://styled-components.com/) commonly used in the community, which uses the new feature of JavaScript [Tagged template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) to write CSS styles for components. You can use the [styled-components](https://styled-components.com/) API directly from `@modern-js/runtime/styled`.
|
|
32
|
+
|
|
33
|
+
When you need to write a `div` component with an internal font in red, you can do the following implementation:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
import styled from '@modern-js/runtime/styled';
|
|
37
|
+
|
|
38
|
+
const RedDiv = styled.div`
|
|
39
|
+
color: red;
|
|
40
|
+
`;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
When you need to dynamically set the component style according to the `props` of the component, for example, when the attribute `primary` of `props` is `true`, the color of the button is white, and otherwise it is red. The implementation code is as follows:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import styled from '@modern-js/runtime/styled';
|
|
47
|
+
|
|
48
|
+
const Button = styled.button`
|
|
49
|
+
color: ${props => (props.primary ? 'white' : 'red')};
|
|
50
|
+
font-size: 1em;
|
|
51
|
+
`;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For more usage of styled-components, please refer to [[styled-components official website](https://styled-components.com/) ].
|
|
55
|
+
|
|
56
|
+
Modern.js uses the Babel plugin [babel-plugin-styled-components](https://github.com/styled-components/babel-plugin-styled-components) internally, and the plugin can be configured through [tools.styledComponents](/configure/app/tools/styled-components).
|
|
57
|
+
|
|
58
|
+
:::tip
|
|
59
|
+
If you need to use [styled-jsx](https://www.npmjs.com/package/styled-jsx), [Emotion](https://emotion.sh/) and other CSS-in-JS libraries, you need to install the dependency of the corresponding library first. For specific usage, please refer to the official website of the corresponding library.
|
|
60
|
+
:::
|
|
61
|
+
|
|
62
|
+
## Using Tailwind CSS
|
|
6
63
|
|
|
7
64
|
[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.
|
|
8
65
|
|
|
@@ -36,7 +93,7 @@ According to different needs, you can optionally import the CSS files provided b
|
|
|
36
93
|
|
|
37
94
|
:::
|
|
38
95
|
|
|
39
|
-
|
|
96
|
+
### Tailwind CSS version
|
|
40
97
|
|
|
41
98
|
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.
|
|
42
99
|
|
|
@@ -54,7 +111,7 @@ Both Tailwind CSS v2 and v3 do not support IE 11 browsers. For background, pleas
|
|
|
54
111
|
|
|
55
112
|
If you use Tailwind CSS on IE 11 browser, some styles may not be available, please pay attention.
|
|
56
113
|
|
|
57
|
-
|
|
114
|
+
### Theme config
|
|
58
115
|
|
|
59
116
|
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`](/configure/app/source/design-system), for example, add a color theme `primary`:
|
|
60
117
|
|
|
@@ -81,9 +81,9 @@ When a routing file is passed through `[]`, it is passed as a [dynamic route](/g
|
|
|
81
81
|
|
|
82
82
|
```tsx
|
|
83
83
|
// routes/user/[id]/page.loader.tsx
|
|
84
|
-
import {
|
|
84
|
+
import { LoaderFunctionArgs } from '@modern-js/runtime/router';
|
|
85
85
|
|
|
86
|
-
export default async ({ params }:
|
|
86
|
+
export default async ({ params }: LoaderFunctionArgs) => {
|
|
87
87
|
const { id } = params;
|
|
88
88
|
const res = await fetch(`https://api/user/${id}`);
|
|
89
89
|
return res.json();
|
|
@@ -100,9 +100,9 @@ A common usage scenario is to obtain query parameters via `request`:
|
|
|
100
100
|
|
|
101
101
|
```tsx
|
|
102
102
|
// routes/user/[id]/page.loader.ts
|
|
103
|
-
import {
|
|
103
|
+
import { LoaderFunctionArgs } from '@modern-js/runtime/router';
|
|
104
104
|
|
|
105
|
-
export default async ({ request }:
|
|
105
|
+
export default async ({ request }: LoaderFunctionArgs) => {
|
|
106
106
|
const url = new URL(request.url);
|
|
107
107
|
const userId = url.searchParams.get('id');
|
|
108
108
|
return queryUser(userId);
|
|
@@ -8,6 +8,10 @@ Modern.js provides support for environment variables, including built-in environ
|
|
|
8
8
|
|
|
9
9
|
## Built-in Environment
|
|
10
10
|
|
|
11
|
+
### ASSET_PREFIX
|
|
12
|
+
|
|
13
|
+
The current path prefix of resource file, which is a **read-only** environment variable.
|
|
14
|
+
|
|
11
15
|
### NODE_ENV
|
|
12
16
|
|
|
13
17
|
The current execution environment and is a **read-only** environment variable whose have different values under different execution commands:
|
|
@@ -77,7 +77,7 @@ In order to facilitate the introduction of the relationship between `<Layout>` a
|
|
|
77
77
|
.
|
|
78
78
|
└── routes
|
|
79
79
|
├── blog
|
|
80
|
-
│
|
|
80
|
+
│ └── page.tsx
|
|
81
81
|
├── layout.tsx
|
|
82
82
|
├── page.tsx
|
|
83
83
|
└── user
|
|
@@ -124,9 +124,9 @@ With a file directory named `[]`, the generated route will be used as a dynamic
|
|
|
124
124
|
```
|
|
125
125
|
└── routes
|
|
126
126
|
├── [id]
|
|
127
|
-
│
|
|
127
|
+
│ └── page.tsx
|
|
128
128
|
├── blog
|
|
129
|
-
│
|
|
129
|
+
│ └── page.tsx
|
|
130
130
|
└── page.tsx
|
|
131
131
|
```
|
|
132
132
|
|
|
@@ -148,7 +148,7 @@ For example, the following directory structure:
|
|
|
148
148
|
└── routes
|
|
149
149
|
├── $.tsx
|
|
150
150
|
├── blog
|
|
151
|
-
│
|
|
151
|
+
│ └── page.tsx
|
|
152
152
|
└── page.tsx
|
|
153
153
|
```
|
|
154
154
|
|
|
@@ -215,9 +215,9 @@ When the component exists in the routing directory, all routing switches under t
|
|
|
215
215
|
.
|
|
216
216
|
└── routes
|
|
217
217
|
├── blog
|
|
218
|
-
│
|
|
219
|
-
│
|
|
220
|
-
│
|
|
218
|
+
│ ├── [id]
|
|
219
|
+
│ │ └── page.tsx
|
|
220
|
+
│ └── page.tsx
|
|
221
221
|
├── layout.tsx
|
|
222
222
|
├── loading.tsx
|
|
223
223
|
└── page.tsx
|
|
@@ -225,6 +225,22 @@ When the component exists in the routing directory, all routing switches under t
|
|
|
225
225
|
|
|
226
226
|
When a route jumps from `/` to `/blog`, if the JS Chunk of the `<Blog>` component is not loaded, the component UI exported in `loading.tsx` will be displayed first. But when jumping from `/blog` to `/blog/20220101`, it will not be displayed.
|
|
227
227
|
|
|
228
|
+
### Redirect
|
|
229
|
+
|
|
230
|
+
You can redirect routes by creating a [`data loader`](/guides/basic-features/data-fetch) file, Suppose you have the file `routes/user/page.tsx` and you want to redirect the route corresponding to this file, you can create the file `routes/user/page.loader.ts`:
|
|
231
|
+
|
|
232
|
+
```ts title="routes/user/page.loader.ts"
|
|
233
|
+
import { redirect } from "@edenx/runtime/router"
|
|
234
|
+
|
|
235
|
+
export default () => {
|
|
236
|
+
const user = await getUser();
|
|
237
|
+
if(!user){
|
|
238
|
+
return redirect('/login');
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
228
244
|
### ErrorBoundary
|
|
229
245
|
|
|
230
246
|
In each layer directory under `routes/`, the developer can also define a `error.tsx` file, and export a `<ErrorBoundary>` component by default.
|
|
@@ -14,7 +14,7 @@ From the perspective of building, Modern.js is divided into three layers, from t
|
|
|
14
14
|
|
|
15
15
|
- Upper-layer framework: Modern.js.
|
|
16
16
|
- Universal build engine: Modern.js Builder.
|
|
17
|
-
- Low-level bundlers: webpack and
|
|
17
|
+
- Low-level bundlers: webpack and rspack.
|
|
18
18
|
|
|
19
19
|
<img src="https://lf3-static.bytednsdoc.com/obj/eden-cn/zq-uylkvT/ljhwZthlaukjlkulzlp/builder-layers-1117.png" style={{ maxWidth: '540px' }} />
|
|
20
20
|
|
|
@@ -129,9 +129,9 @@ The bundle is generated to `dist/` by default, and the directory structure is as
|
|
|
129
129
|
.
|
|
130
130
|
├── asset-manifest.json
|
|
131
131
|
├── html
|
|
132
|
-
│
|
|
132
|
+
│ └── main
|
|
133
133
|
├── loader-routes
|
|
134
|
-
│
|
|
134
|
+
│ └── main
|
|
135
135
|
├── modern.config.json
|
|
136
136
|
├── route.json
|
|
137
137
|
└── static
|
|
@@ -149,7 +149,7 @@ export default () => {
|
|
|
149
149
|
然后在主应用中使用 `useModuleApp` 方法获取 `MApp` 组件, 并在主应用渲染 `MApp`。
|
|
150
150
|
|
|
151
151
|
```tsx title=主应用:App.tsx
|
|
152
|
-
import { useModuleApp } from '@modern-js/plugin-runtime';
|
|
152
|
+
import { useModuleApp } from '@modern-js/plugin-garfish/runtime';
|
|
153
153
|
|
|
154
154
|
function App() {
|
|
155
155
|
const { MApp } = useModuleApps();
|
|
@@ -293,7 +293,7 @@ A Utility Class named `custom-text-gray` is implemented in `src/routes/styles/ut
|
|
|
293
293
|
```
|
|
294
294
|
|
|
295
295
|
:::info note
|
|
296
|
-
Modern.js integrates with [PostCSS](/guides/css
|
|
296
|
+
Modern.js integrates with [PostCSS](/guides/basic-features/css) and supports modern CSS syntax features such as [custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties).
|
|
297
297
|
|
|
298
298
|
:::
|
|
299
299
|
|
|
@@ -255,23 +255,23 @@ The refactoring is complete, and the current project structure is:
|
|
|
255
255
|
├── package.json
|
|
256
256
|
├── pnpm-lock.yaml
|
|
257
257
|
├── src
|
|
258
|
-
│
|
|
259
|
-
│
|
|
260
|
-
│
|
|
261
|
-
│
|
|
262
|
-
│
|
|
263
|
-
│
|
|
264
|
-
│
|
|
265
|
-
│
|
|
266
|
-
│
|
|
267
|
-
│
|
|
268
|
-
│
|
|
269
|
-
│
|
|
270
|
-
│
|
|
271
|
-
│
|
|
272
|
-
│
|
|
273
|
-
│
|
|
274
|
-
│
|
|
258
|
+
│ ├── components
|
|
259
|
+
│ │ ├── Avatar
|
|
260
|
+
│ │ │ └── index.tsx
|
|
261
|
+
│ │ └── Item
|
|
262
|
+
│ │ └── index.tsx
|
|
263
|
+
│ ├── containers
|
|
264
|
+
│ │ └── Contacts.tsx
|
|
265
|
+
│ ├── models
|
|
266
|
+
│ │ └── contacts.ts
|
|
267
|
+
│ ├── modern-app-env.d.ts
|
|
268
|
+
│ ├── routes
|
|
269
|
+
│ │ ├── archives
|
|
270
|
+
│ │ │ └── page.tsx
|
|
271
|
+
│ │ ├── layout.tsx
|
|
272
|
+
│ │ └── page.tsx
|
|
273
|
+
│ └── styles
|
|
274
|
+
│ └── utils.css
|
|
275
275
|
└── tsconfig.json
|
|
276
276
|
```
|
|
277
277
|
|
|
@@ -29,29 +29,29 @@ When created, the project will look like this:
|
|
|
29
29
|
├── package.json
|
|
30
30
|
├── pnpm-lock.yaml
|
|
31
31
|
├── src
|
|
32
|
-
│
|
|
33
|
-
│
|
|
34
|
-
│
|
|
35
|
-
│
|
|
36
|
-
│
|
|
37
|
-
│
|
|
38
|
-
│
|
|
39
|
-
│
|
|
40
|
-
│
|
|
41
|
-
│
|
|
42
|
-
│
|
|
43
|
-
│
|
|
44
|
-
│
|
|
45
|
-
│
|
|
46
|
-
│
|
|
47
|
-
│
|
|
48
|
-
│
|
|
49
|
-
│
|
|
50
|
-
│
|
|
51
|
-
│
|
|
52
|
-
│
|
|
53
|
-
│
|
|
54
|
-
│
|
|
32
|
+
│ ├── modern-app-env.d.ts
|
|
33
|
+
│ ├── landing-page
|
|
34
|
+
│ │ └── routes
|
|
35
|
+
│ │ ├── index.css
|
|
36
|
+
│ │ ├── layout.tsx
|
|
37
|
+
│ │ └── page.tsx
|
|
38
|
+
│ └── myapp
|
|
39
|
+
│ ├── components
|
|
40
|
+
│ │ ├── Avatar
|
|
41
|
+
│ │ │ └── index.tsx
|
|
42
|
+
│ │ └── Item
|
|
43
|
+
│ │ └── index.tsx
|
|
44
|
+
│ ├── containers
|
|
45
|
+
│ │ └── Contacts.tsx
|
|
46
|
+
│ ├── models
|
|
47
|
+
│ │ └── contacts.ts
|
|
48
|
+
│ ├── routes
|
|
49
|
+
│ │ ├── archives
|
|
50
|
+
│ │ │ └── page.tsx
|
|
51
|
+
│ │ ├── layout.tsx
|
|
52
|
+
│ │ └── page.tsx
|
|
53
|
+
│ └── styles
|
|
54
|
+
│ └── utils.css
|
|
55
55
|
└── tsconfig.json
|
|
56
56
|
```
|
|
57
57
|
|
package/package.json
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"modern",
|
|
12
12
|
"modern.js"
|
|
13
13
|
],
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.7.0",
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"registry": "https://registry.npmjs.org/",
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@modern-js/builder-doc": "^2.
|
|
20
|
+
"@modern-js/builder-doc": "^2.7.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"ts-node": "^10",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"fs-extra": "^10",
|
|
26
26
|
"@types/node": "^16",
|
|
27
27
|
"@types/fs-extra": "^9",
|
|
28
|
-
"@modern-js/builder-doc": "2.
|
|
28
|
+
"@modern-js/builder-doc": "2.7.0"
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "npx ts-node ./scripts/sync.ts"
|
package/scripts/summary.en.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"name":"assetPrefix","dirname":"dev"},{"name":"hmr","dirname":"dev"},{"name":"https","dirname":"dev"},{"name":"port","dirname":"dev"},{"name":"progressBar","dirname":"dev"},{"name":"startUrl","dirname":"dev"},{"name":"lazyCompilation","dirname":"experiments"},{"name":"appIcon","dirname":"html"},{"name":"crossorigin","dirname":"html"},{"name":"disableHtmlFolder","dirname":"html"},{"name":"favicon","dirname":"html"},{"name":"faviconByEntries","dirname":"html"},{"name":"inject","dirname":"html"},{"name":"injectByEntries","dirname":"html"},{"name":"meta","dirname":"html"},{"name":"metaByEntries","dirname":"html"},{"name":"mountId","dirname":"html"},{"name":"tags","dirname":"html"},{"name":"tagsByEntries","dirname":"html"},{"name":"template","dirname":"html"},{"name":"templateByEntries","dirname":"html"},{"name":"templateParameters","dirname":"html"},{"name":"templateParametersByEntries","dirname":"html"},{"name":"title","dirname":"html"},{"name":"titleByEntries","dirname":"html"},{"name":"assetPrefix","dirname":"output"},{"name":"assetsRetry","dirname":"output"},{"name":"charset","dirname":"output"},{"name":"cleanDistPath","dirname":"output"},{"name":"convertToRem","dirname":"output"},{"name":"copy","dirname":"output"},{"name":"cssModuleLocalIdentName","dirname":"output"},{"name":"dataUriLimit","dirname":"output"},{"name":"disableCssExtract","dirname":"output"},{"name":"disableCssModuleExtension","dirname":"output"},{"name":"disableFilenameHash","dirname":"output"},{"name":"disableInlineRuntimeChunk","dirname":"output"},{"name":"disableMinimize","dirname":"output"},{"name":"disableSourceMap","dirname":"output"},{"name":"disableTsChecker","dirname":"output"},{"name":"distPath","dirname":"output"},{"name":"enableAssetFallback","dirname":"output"},{"name":"enableAssetManifest","dirname":"output"},{"name":"enableCssModuleTSDeclaration","dirname":"output"},{"name":"enableInlineScripts","dirname":"output"},{"name":"enableInlineStyles","dirname":"output"},{"name":"enableLatestDecorators","dirname":"output"},{"name":"externals","dirname":"output"},{"name":"filename","dirname":"output"},{"name":"legalComments","dirname":"output"},{"name":"overrideBrowserslist","dirname":"output"},{"name":"polyfill","dirname":"output"},{"name":"svgDefaultExport","dirname":"output"},{"name":"buildCache","dirname":"performance"},{"name":"bundleAnalyze","dirname":"performance"},{"name":"chunkSplit","dirname":"performance"},{"name":"printFileSize","dirname":"performance"},{"name":"profile","dirname":"performance"},{"name":"removeConsole","dirname":"performance"},{"name":"removeMomentLocale","dirname":"performance"},{"name":"checkSyntax","dirname":"security"},{"name":"sri","dirname":"security"},{"name":"alias","dirname":"source"},{"name":"compileJsDataURI","dirname":"source"},{"name":"define","dirname":"source"},{"name":"exclude","dirname":"source"},{"name":"globalVars","dirname":"source"},{"name":"include","dirname":"source"},{"name":"moduleScopes","dirname":"source"},{"name":"preEntry","dirname":"source"},{"name":"resolveExtensionPrefix","dirname":"source"},{"name":"resolveMainFields","dirname":"source"},{"name":"autoprefixer","dirname":"tools"},{"name":"babel","dirname":"tools"},{"name":"cssExtract","dirname":"tools"},{"name":"cssLoader","dirname":"tools"},{"name":"devServer","dirname":"tools"},{"name":"htmlPlugin","dirname":"tools"},{"name":"inspector","dirname":"tools"},{"name":"less","dirname":"tools"},{"name":"minifyCss","dirname":"tools"},{"name":"postcss","dirname":"tools"},{"name":"pug","dirname":"tools"},{"name":"rspack","dirname":"tools"},{"name":"sass","dirname":"tools"},{"name":"styleLoader","dirname":"tools"},{"name":"styledComponents","dirname":"tools"},{"name":"terser","dirname":"tools"},{"name":"tsChecker","dirname":"tools"},{"name":"tsLoader","dirname":"tools"},{"name":"webpack","dirname":"tools"},{"name":"webpackChain","dirname":"tools"}]
|
|
1
|
+
[{"name":"assetPrefix","dirname":"dev"},{"name":"beforeStartUrl","dirname":"dev"},{"name":"hmr","dirname":"dev"},{"name":"host","dirname":"dev"},{"name":"https","dirname":"dev"},{"name":"port","dirname":"dev"},{"name":"progressBar","dirname":"dev"},{"name":"startUrl","dirname":"dev"},{"name":"lazyCompilation","dirname":"experiments"},{"name":"appIcon","dirname":"html"},{"name":"crossorigin","dirname":"html"},{"name":"disableHtmlFolder","dirname":"html"},{"name":"favicon","dirname":"html"},{"name":"faviconByEntries","dirname":"html"},{"name":"inject","dirname":"html"},{"name":"injectByEntries","dirname":"html"},{"name":"meta","dirname":"html"},{"name":"metaByEntries","dirname":"html"},{"name":"mountId","dirname":"html"},{"name":"tags","dirname":"html"},{"name":"tagsByEntries","dirname":"html"},{"name":"template","dirname":"html"},{"name":"templateByEntries","dirname":"html"},{"name":"templateParameters","dirname":"html"},{"name":"templateParametersByEntries","dirname":"html"},{"name":"title","dirname":"html"},{"name":"titleByEntries","dirname":"html"},{"name":"assetPrefix","dirname":"output"},{"name":"assetsRetry","dirname":"output"},{"name":"charset","dirname":"output"},{"name":"cleanDistPath","dirname":"output"},{"name":"convertToRem","dirname":"output"},{"name":"copy","dirname":"output"},{"name":"cssModuleLocalIdentName","dirname":"output"},{"name":"dataUriLimit","dirname":"output"},{"name":"disableCssExtract","dirname":"output"},{"name":"disableCssModuleExtension","dirname":"output"},{"name":"disableFilenameHash","dirname":"output"},{"name":"disableInlineRuntimeChunk","dirname":"output"},{"name":"disableMinimize","dirname":"output"},{"name":"disableSourceMap","dirname":"output"},{"name":"disableTsChecker","dirname":"output"},{"name":"distPath","dirname":"output"},{"name":"enableAssetFallback","dirname":"output"},{"name":"enableAssetManifest","dirname":"output"},{"name":"enableCssModuleTSDeclaration","dirname":"output"},{"name":"enableInlineScripts","dirname":"output"},{"name":"enableInlineStyles","dirname":"output"},{"name":"enableLatestDecorators","dirname":"output"},{"name":"externals","dirname":"output"},{"name":"filename","dirname":"output"},{"name":"legalComments","dirname":"output"},{"name":"overrideBrowserslist","dirname":"output"},{"name":"polyfill","dirname":"output"},{"name":"svgDefaultExport","dirname":"output"},{"name":"buildCache","dirname":"performance"},{"name":"bundleAnalyze","dirname":"performance"},{"name":"chunkSplit","dirname":"performance"},{"name":"printFileSize","dirname":"performance"},{"name":"profile","dirname":"performance"},{"name":"removeConsole","dirname":"performance"},{"name":"removeMomentLocale","dirname":"performance"},{"name":"checkSyntax","dirname":"security"},{"name":"sri","dirname":"security"},{"name":"alias","dirname":"source"},{"name":"compileJsDataURI","dirname":"source"},{"name":"define","dirname":"source"},{"name":"exclude","dirname":"source"},{"name":"globalVars","dirname":"source"},{"name":"include","dirname":"source"},{"name":"moduleScopes","dirname":"source"},{"name":"preEntry","dirname":"source"},{"name":"resolveExtensionPrefix","dirname":"source"},{"name":"resolveMainFields","dirname":"source"},{"name":"autoprefixer","dirname":"tools"},{"name":"babel","dirname":"tools"},{"name":"cssExtract","dirname":"tools"},{"name":"cssLoader","dirname":"tools"},{"name":"devServer","dirname":"tools"},{"name":"htmlPlugin","dirname":"tools"},{"name":"inspector","dirname":"tools"},{"name":"less","dirname":"tools"},{"name":"minifyCss","dirname":"tools"},{"name":"postcss","dirname":"tools"},{"name":"pug","dirname":"tools"},{"name":"rspack","dirname":"tools"},{"name":"sass","dirname":"tools"},{"name":"styleLoader","dirname":"tools"},{"name":"styledComponents","dirname":"tools"},{"name":"terser","dirname":"tools"},{"name":"tsChecker","dirname":"tools"},{"name":"tsLoader","dirname":"tools"},{"name":"webpack","dirname":"tools"},{"name":"webpackChain","dirname":"tools"}]
|