@modern-js/main-doc 2.6.0 → 2.8.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/CHANGELOG.md +17 -0
- package/README.md +2 -2
- package/en/apis/app/commands.mdx +2 -0
- package/en/apis/app/hooks/config/upload.mdx +10 -0
- package/en/apis/app/runtime/model/connect.mdx +1 -1
- package/en/apis/app/runtime/model/model_.mdx +1 -1
- package/en/apis/app/runtime/model/use-model.mdx +1 -1
- package/en/apis/app/runtime/web-server/hook.mdx +2 -2
- package/en/apis/app/runtime/web-server/middleware.mdx +33 -9
- package/en/components/enable-bff.mdx +4 -4
- package/en/components/init-rspack-app.mdx +7 -0
- package/en/configure/app/bff/enable-handle-web.mdx +24 -0
- package/en/configure/app/server/enable-framework-ext.mdx +1 -1
- package/en/configure/app/server/ssr.mdx +18 -0
- package/en/guides/advanced-features/bff/_category_.json +1 -1
- package/en/guides/advanced-features/eslint.mdx +30 -32
- package/en/guides/advanced-features/low-level.mdx +1 -1
- package/en/guides/advanced-features/rspack-start.mdx +13 -17
- package/en/guides/advanced-features/ssr.mdx +210 -5
- package/en/guides/advanced-features/web-server.mdx +87 -20
- package/en/guides/{css/tailwindcss.mdx → basic-features/css.mdx} +60 -3
- package/en/guides/basic-features/env-vars.mdx +4 -0
- package/en/guides/concept/builder.mdx +1 -1
- package/en/guides/topic-detail/framework-plugin/extend.mdx +20 -19
- package/en/guides/topic-detail/framework-plugin/hook-list.mdx +156 -155
- package/en/guides/topic-detail/framework-plugin/hook.mdx +58 -43
- package/en/guides/topic-detail/framework-plugin/implement.mdx +47 -49
- package/en/guides/topic-detail/framework-plugin/introduction.mdx +22 -23
- package/en/guides/topic-detail/framework-plugin/plugin-api.mdx +13 -13
- package/en/guides/topic-detail/framework-plugin/relationship.mdx +30 -30
- package/en/guides/topic-detail/generator/plugin/develop.mdx +1 -1
- package/en/guides/topic-detail/micro-frontend/c01-introduction.mdx +17 -17
- package/en/guides/topic-detail/micro-frontend/c02-development.mdx +76 -78
- package/en/guides/topic-detail/micro-frontend/c03-main-app.mdx +57 -51
- package/en/guides/topic-detail/micro-frontend/c04-communicate.mdx +11 -11
- package/en/guides/topic-detail/micro-frontend/c05-mixed-stack.mdx +13 -13
- package/en/guides/topic-detail/model/auto-actions.mdx +18 -21
- package/en/guides/topic-detail/model/computed-state.mdx +27 -25
- package/en/guides/topic-detail/model/define-model.mdx +14 -14
- package/en/guides/topic-detail/model/faq.mdx +12 -13
- package/en/guides/topic-detail/model/manage-effects.mdx +43 -52
- package/en/guides/topic-detail/model/model-communicate.mdx +47 -45
- package/en/guides/topic-detail/model/performance.mdx +22 -23
- package/en/guides/topic-detail/model/quick-start.mdx +29 -28
- package/en/guides/topic-detail/model/redux-integration.mdx +7 -7
- package/en/guides/topic-detail/model/test-model.mdx +11 -11
- package/en/guides/topic-detail/model/typescript-best-practice.mdx +16 -15
- package/en/guides/topic-detail/model/use-model.mdx +40 -45
- package/en/guides/topic-detail/model/use-out-of-modernjs.mdx +16 -16
- package/en/guides/troubleshooting/cli.mdx +2 -2
- package/en/tutorials/first-app/c03-css.mdx +1 -1
- package/package.json +5 -5
- package/zh/apis/app/commands.mdx +2 -0
- package/zh/apis/app/hooks/config/upload.mdx +12 -2
- package/zh/apis/app/runtime/model/connect.mdx +1 -1
- package/zh/apis/app/runtime/model/model_.mdx +1 -1
- package/zh/apis/app/runtime/model/use-model.mdx +1 -1
- package/zh/apis/app/runtime/web-server/hook.mdx +2 -4
- package/zh/apis/app/runtime/web-server/middleware.mdx +30 -10
- package/zh/apis/monorepo/commands/gen-release-note.mdx +3 -3
- package/zh/components/enable-bff.mdx +4 -4
- package/zh/components/init-rspack-app.mdx +7 -0
- package/zh/components/release-note.mdx +1 -1
- package/zh/configure/app/bff/enable-handle-web.mdx +24 -0
- package/zh/configure/app/server/enable-framework-ext.mdx +1 -1
- package/zh/configure/app/server/ssr.mdx +19 -1
- package/zh/guides/advanced-features/bff/_category_.json +1 -1
- package/zh/guides/advanced-features/rspack-start.mdx +13 -17
- package/zh/guides/advanced-features/ssr.mdx +210 -4
- package/zh/guides/advanced-features/web-server.mdx +81 -16
- package/zh/guides/{css/tailwindcss.mdx → basic-features/css.mdx} +60 -3
- package/zh/guides/basic-features/env-vars.mdx +4 -0
- package/zh/guides/concept/builder.mdx +1 -1
- package/zh/guides/topic-detail/changesets/github.mdx +2 -2
- package/zh/guides/topic-detail/changesets/release-note.mdx +1 -1
- package/zh/guides/topic-detail/framework-plugin/plugin-api.mdx +2 -2
- package/zh/guides/topic-detail/generator/plugin/develop.mdx +1 -1
- package/zh/guides/topic-detail/model/faq.mdx +1 -1
- package/zh/guides/topic-detail/model/manage-effects.mdx +1 -1
- package/zh/guides/topic-detail/model/model-communicate.mdx +1 -1
- package/zh/guides/topic-detail/model/performance.mdx +1 -1
- package/zh/guides/topic-detail/model/quick-start.mdx +2 -2
- package/zh/guides/topic-detail/model/use-model.mdx +3 -3
- package/zh/tutorials/first-app/c03-css.mdx +1 -1
- 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/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
|
:::
|
@@ -1,23 +1,37 @@
|
|
1
1
|
---
|
2
2
|
title: Custom Web Server
|
3
|
-
sidebar_position:
|
3
|
+
sidebar_position: 3
|
4
4
|
---
|
5
5
|
# Custom Web Server
|
6
6
|
|
7
|
-
Modern.js
|
7
|
+
As a client side-centric development framework, Modern.js has weak customization capabilities on the server side. In some development scenarios, special server level logic needs to be customized, such as user authentication, request preprocessing, and adding page rendering skeletons.
|
8
8
|
|
9
|
-
|
9
|
+
Some developers may be wondering, Modern.js already provides [BFF](/guides/advanced-features/bff/function.html), why do you need **Custom Web Server**.
|
10
10
|
|
11
|
-
|
11
|
+
Because by default, page routing does not go through BFF, it has no way to provide server-side custom logic for page access. The reason for this design is that we do not want the service that controls the page to be bound to the BFF service, so as to avoid the BFF framework restricting how the page is deployed.
|
12
12
|
|
13
|
-
|
13
|
+
For example, hosting pages separately from BFF, deploying page services to non-Node environments, or customizing for deployment platforms, etc.
|
14
|
+
|
15
|
+
For the above reasons, Modern.js provides three ways that projects can customize server level capabilities progressively according to their needs.
|
16
|
+
|
17
|
+
:::warning
|
18
|
+
The three extension methods cannot work at the same time, and developers need to choose the appropriate method according to the scenario.
|
19
|
+
:::
|
20
|
+
|
21
|
+
## Extending Web Server with API
|
22
|
+
|
23
|
+
The first way is to customize the server level at a specific life cycle through the server level runtime API provided by Modern.js. The purpose of providing this way is that in some cases, developers do not need to control the full Web Server, but only need to add server level logic.
|
24
|
+
|
25
|
+
Because the full web server cannot be controlled this way, and the extension logic **only takes effect when the page is requested**. Therefore, it is relatively simple to apply to server level logic, and you do not want to create additional BFFs or BFFs and pages without common server level logic scenarios.
|
26
|
+
|
27
|
+
You can execute the'pnpm run new 'command in the project root directory to enable the "Custom Web Serve" function:
|
14
28
|
|
15
29
|
```bash
|
16
|
-
?
|
17
|
-
?
|
30
|
+
? Action Create project element
|
31
|
+
? New "Custom Web Server" source code directory
|
18
32
|
```
|
19
33
|
|
20
|
-
|
34
|
+
After executing the command, register the `@modern-js/plugin-server` plugin in `modern.config.ts`:
|
21
35
|
|
22
36
|
```ts title="modern.config.ts"
|
23
37
|
import serverPlugin from '@modern-js/plugin-server';
|
@@ -27,31 +41,84 @@ export default defineConfig({
|
|
27
41
|
});
|
28
42
|
```
|
29
43
|
|
30
|
-
|
44
|
+
After the function is turned on, the `server/index.ts` file will be automatically created in the project directory, and custom logic can be written in this file. Modern.js provides two types of APIs, **Hook** and **Middleware**, to extend Web Server.
|
45
|
+
|
46
|
+
### Hook
|
31
47
|
|
32
|
-
|
48
|
+
The Hook provided by Modern.js is used to control the built-in logic in the Web Server, and all page requests go through the Hook.
|
33
49
|
|
34
|
-
|
50
|
+
There are currently two Hooks provided, namely `AfterMatch` and `AfterRender`, which can be used to modify the rendering results. It can be written in `server/index.ts` like this:
|
35
51
|
|
36
|
-
|
52
|
+
```ts
|
53
|
+
import type { AfterMatchHook, AfterRenderHook } from '@modern-js/runtime/server';
|
54
|
+
|
55
|
+
export const afterMatch: AfterMatchHook = (ctx, next) => {
|
56
|
+
next();
|
57
|
+
}
|
58
|
+
|
59
|
+
export const afterRender: AfterRenderHook = (ctx, next) => {
|
60
|
+
next();
|
61
|
+
}
|
62
|
+
```
|
37
63
|
|
38
|
-
|
64
|
+
Projects should have the following best practices when using Hook:
|
39
65
|
|
40
|
-
|
66
|
+
1. Permission verification in afterMatch.
|
67
|
+
2. Do Rewrite and Redirect in afterMatch.
|
68
|
+
3. Do HTML content injection in afterRender.
|
41
69
|
|
42
|
-
|
70
|
+
:::note
|
71
|
+
For more detail, see [Hook](/apis/app/runtime/web-server/hook)。
|
72
|
+
:::
|
43
73
|
|
44
74
|
### Middleware
|
45
75
|
|
46
|
-
Middleware
|
76
|
+
For some projects, there may be more requirements at the server level, Modern.js provides Middleware to add pre-middleware for Web Server. It can only run in a Node environment, so if the project is deployed to another environment, such as a Worker environment, Middleware cannot be used.
|
47
77
|
|
48
|
-
|
78
|
+
Modern.js provides a set of APIs by default for projects to use:
|
49
79
|
|
50
|
-
|
80
|
+
```ts
|
81
|
+
import { Middlewre } from '@modern-js/runtime/server';
|
51
82
|
|
52
|
-
|
83
|
+
export const middleware = (context, next) => {
|
84
|
+
const { source: { req, res } } = context;
|
85
|
+
console.log(req.url);
|
86
|
+
next();
|
87
|
+
};
|
88
|
+
```
|
53
89
|
|
54
90
|
:::note
|
55
|
-
|
91
|
+
For more detail, see [Middleware] (/apis/app/runtime/web-server/middleware).
|
92
|
+
:::
|
93
|
+
|
94
|
+
Projects should have the following best practices when using Middleware:
|
95
|
+
|
96
|
+
1. In Middleware, you can directly operate origin request and response objects, do event tracking, and inject Node services (databases, Redis, etc.) that may be used for SSR rendering.
|
97
|
+
2. Marking and crawler optimization can be done in Middleware.
|
98
|
+
3. In Middleware, you can ignore the default rendering and customize the rendering process.
|
99
|
+
|
100
|
+
**In general, in CSR projects, using Hook can basically meet all the needs of simple scenarios. In SSR projects, Middleware can be used for more complex Node extensions.**
|
101
|
+
|
102
|
+
## Managed Page Requests with BFF
|
103
|
+
|
104
|
+
The second way is to use BFF to Managed page rendering. In this way, all requests will first hit the BFF service.
|
105
|
+
|
106
|
+
This method can uniformly control the server level logic of all requests through BFF. Therefore, it is suitable for scenarios where the server level logic is complex, and BFF and pages need common server level logic. But it still relies on the Web Server of Modern.js as a whole, and cannot run the logic on existing services.
|
107
|
+
|
108
|
+
To use this method, we need to first enable the "BFF" function through `pnpm new`. Then add [`bff.enableHandleWeb`](/configure/app/bff/enable-handle-web.html) configuration in the configuration file:
|
56
109
|
|
110
|
+
```ts
|
111
|
+
export default defineConfig({
|
112
|
+
bff: {
|
113
|
+
enableHandleWeb: true,
|
114
|
+
},
|
115
|
+
});
|
116
|
+
```
|
117
|
+
|
118
|
+
When this value is set to `true`, page request traffic also goes through the BFF, and the logic built into Modern.js for page rendering defaults to running as the last middleware for the BFF service.
|
119
|
+
|
120
|
+
## Fully Customized Web Server
|
121
|
+
|
122
|
+
:::note
|
123
|
+
Comming soon..
|
57
124
|
:::
|
@@ -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
|
|
@@ -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:
|
@@ -6,7 +6,7 @@ sidebar_position: 2
|
|
6
6
|
|
7
7
|
**Modern.js uses [Modern.js Builder](https://modernjs.dev/builder) to build your Web App.**
|
8
8
|
|
9
|
-
Modern.js Builder is one of the core components of Modern.js. It is A
|
9
|
+
Modern.js Builder is one of the core components of Modern.js. It is A build engine for web development. and can be used independently of Modern.js. Modern.js Builder supports multiple bundlers such as webpack and Rspack, and it uses webpack by default.
|
10
10
|
|
11
11
|
## Build Architecture
|
12
12
|
|
@@ -1,24 +1,24 @@
|
|
1
1
|
---
|
2
|
-
title:
|
2
|
+
title: Extending
|
3
3
|
sidebar_position: 5
|
4
4
|
---
|
5
|
-
#
|
5
|
+
# Extending Plugin Hooks
|
6
6
|
|
7
|
-
|
7
|
+
This section describes how to extend plugin Hooks by dynamically registering [Hook models](/guides/topic-detail/framework-plugin/hook).
|
8
8
|
|
9
|
-
##
|
9
|
+
## Example
|
10
10
|
|
11
|
-
|
11
|
+
Here is a simple example to demonstrate how to extend plugin Hooks by adding Hooks that manage console output.
|
12
12
|
|
13
|
-
|
13
|
+
First, we initialize an empty project file and add basic dependencies:
|
14
14
|
|
15
15
|
```bash
|
16
16
|
$ npx @modern-js/create modern-js-demo
|
17
17
|
```
|
18
18
|
|
19
|
-
###
|
19
|
+
### Creating Hooks
|
20
20
|
|
21
|
-
|
21
|
+
First, let's create a Hook model:
|
22
22
|
|
23
23
|
```ts title=config/plugin/myPlugin.ts
|
24
24
|
import { createWaterfall } from '@modern-js/plugin';
|
@@ -26,7 +26,7 @@ import { createWaterfall } from '@modern-js/plugin';
|
|
26
26
|
const message = createWaterfall<string[]>();
|
27
27
|
```
|
28
28
|
|
29
|
-
|
29
|
+
then register:
|
30
30
|
|
31
31
|
```ts title=config/plugin/myPlugin.ts
|
32
32
|
import type { CliPlugin } from '@modern-js/core';
|
@@ -40,7 +40,7 @@ export default (): CliPlugin => ({
|
|
40
40
|
});
|
41
41
|
```
|
42
42
|
|
43
|
-
|
43
|
+
add Hook types:
|
44
44
|
|
45
45
|
```ts title=config/plugin/myPlugin.ts
|
46
46
|
declare module '@modern-js/core' {
|
@@ -50,9 +50,9 @@ declare module '@modern-js/core' {
|
|
50
50
|
}
|
51
51
|
```
|
52
52
|
|
53
|
-
###
|
53
|
+
### Using Hooks
|
54
54
|
|
55
|
-
|
55
|
+
Create a plugin and add command handling logic through the `commands` Hook function:
|
56
56
|
|
57
57
|
```ts title=config/plugin/myPlugin.ts
|
58
58
|
import type { CliPlugin } from '@modern-js/core';
|
@@ -74,7 +74,7 @@ export default (): CliPlugin => ({
|
|
74
74
|
});
|
75
75
|
```
|
76
76
|
|
77
|
-
|
77
|
+
now `config/plugin/myPlugin.ts` is:
|
78
78
|
|
79
79
|
```ts title=config/plugin/myPlugin.ts
|
80
80
|
import { createWaterfall } from '@modern-js/plugin';
|
@@ -109,7 +109,7 @@ declare module '@modern-js/core' {
|
|
109
109
|
}
|
110
110
|
```
|
111
111
|
|
112
|
-
|
112
|
+
Then add this plugin in `modern.config.ts`:
|
113
113
|
|
114
114
|
```ts title="modern.config.ts"
|
115
115
|
import { defineConfig } from '@modern-js/app-tools';
|
@@ -120,9 +120,9 @@ export default defineConfig({
|
|
120
120
|
});
|
121
121
|
```
|
122
122
|
|
123
|
-
|
123
|
+
Now, run `npx modern message`, and the related logic will be executed, but no information is collected, so the console output is empty.
|
124
124
|
|
125
|
-
|
125
|
+
add:
|
126
126
|
|
127
127
|
```ts title=config/plugin/otherPlugin.ts
|
128
128
|
import type { CliPlugin } from '@modern-js/core';
|
@@ -140,7 +140,7 @@ export default (): CliPlugin => ({
|
|
140
140
|
});
|
141
141
|
```
|
142
142
|
|
143
|
-
|
143
|
+
add to config:
|
144
144
|
|
145
145
|
```ts title="modern.config.ts"
|
146
146
|
import { defineConfig } from '@modern-js/app-tools';
|
@@ -152,7 +152,7 @@ export default defineConfig({
|
|
152
152
|
});
|
153
153
|
```
|
154
154
|
|
155
|
-
|
155
|
+
run `npx modern message`, then we can get follow in console:
|
156
156
|
|
157
157
|
```bash
|
158
158
|
$ modern message
|
@@ -160,4 +160,5 @@ $ modern message
|
|
160
160
|
[foo] line 1
|
161
161
|
```
|
162
162
|
|
163
|
-
|
163
|
+
By using the above approach, you can extend plugin Hooks with various capabilities.
|
164
|
+
|