@modern-js/main-doc 2.67.3 → 2.67.5
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/docs/en/apis/app/hooks/src/routes.mdx +70 -0
- package/docs/en/configure/app/output/filename.mdx +8 -3
- package/docs/en/configure/app/plugins.mdx +13 -33
- package/docs/en/configure/app/runtime/master-app.mdx +1 -5
- package/docs/en/configure/app/runtime/plugins.mdx +58 -0
- package/docs/en/configure/app/usage.mdx +1 -3
- package/docs/en/guides/advanced-features/_meta.json +1 -0
- package/docs/en/guides/advanced-features/bff.mdx +1 -1
- package/docs/en/guides/advanced-features/compatibility.mdx +1 -1
- package/docs/en/guides/advanced-features/custom-server.mdx +218 -0
- package/docs/en/guides/advanced-features/page-performance/inline-assets.mdx +2 -0
- package/docs/en/guides/advanced-features/page-performance/optimize-bundle.mdx +1 -1
- package/docs/en/guides/advanced-features/web-server.mdx +174 -1
- package/docs/en/guides/basic-features/data/data-cache.mdx +216 -1
- package/docs/en/guides/basic-features/data/data-fetch.mdx +2 -1
- package/docs/en/guides/basic-features/deploy.mdx +3 -1
- package/docs/en/guides/basic-features/html.mdx +3 -3
- package/docs/en/guides/basic-features/render/ssr.mdx +2 -2
- package/docs/en/guides/concept/entries.mdx +1 -1
- package/docs/en/guides/get-started/quick-start.mdx +1 -1
- package/docs/en/plugin/cli-plugins/api.mdx +6 -0
- package/docs/en/plugin/runtime-plugins/api.mdx +37 -12
- package/docs/en/tutorials/first-app/c05-loader.mdx +1 -1
- package/docs/zh/configure/app/output/filename.mdx +8 -0
- package/docs/zh/configure/app/plugins.mdx +3 -24
- package/docs/zh/configure/app/runtime/master-app.mdx +1 -5
- package/docs/zh/configure/app/runtime/plugins.mdx +58 -0
- package/docs/zh/configure/app/usage.mdx +1 -2
- package/docs/zh/guides/advanced-features/_meta.json +1 -0
- package/docs/zh/guides/advanced-features/custom-server.mdx +216 -0
- package/docs/zh/guides/advanced-features/web-server.mdx +174 -1
- package/docs/zh/guides/basic-features/data/data-cache.mdx +205 -1
- package/docs/zh/guides/basic-features/routes.mdx +72 -0
- package/docs/zh/plugin/cli-plugins/api.mdx +6 -0
- package/docs/zh/plugin/runtime-plugins/api.mdx +37 -12
- package/package.json +2 -2
- package/src/i18n/index.ts +1 -1
- /package/docs/en/configure/app/source/{mainEntryName.mdx → main-entry-name.mdx} +0 -0
- /package/docs/zh/configure/app/source/{mainEntryName.mdx → main-entry-name.mdx} +0 -0
@@ -2,7 +2,11 @@
|
|
2
2
|
sidebar_position: 16
|
3
3
|
---
|
4
4
|
|
5
|
-
# Custom Web Server
|
5
|
+
# Custom Web Server (Not Recommended)
|
6
|
+
|
7
|
+
:::warning
|
8
|
+
Custom Web Server is compatible but no longer recommended. For extending Server capabilities, please refer to [Custom Server](/guides/advanced-features/custom-server.html). For migration guide, see [Migrate to the New Version of Custom Server](/guides/advanced-features/web-server.html#migrate-to-the-new-version-of-custom-server).
|
9
|
+
:::
|
6
10
|
|
7
11
|
Modern.js encapsulates most server-side capabilities required by projects, typically eliminating the need for server-side development. However, in certain scenarios such as user authentication, request preprocessing, or adding page skeletons, custom server-side logic may still be necessary.
|
8
12
|
|
@@ -96,3 +100,172 @@ Best practices when using Hooks:
|
|
96
100
|
:::info
|
97
101
|
For detailed API and more usage, see [Hook](/apis/app/runtime/web-server/hook).
|
98
102
|
:::
|
103
|
+
|
104
|
+
|
105
|
+
## Migrate to the New Version of Custom Server
|
106
|
+
|
107
|
+
### Migration Background
|
108
|
+
|
109
|
+
Modern.js Server is continuously evolving to provide more powerful features. We have optimized the definition and usage of middleware and Server plugins.
|
110
|
+
While the old custom Web Server approach is still compatible, we strongly recommend migrating according to this guide to fully leverage the advantages of the new version.
|
111
|
+
|
112
|
+
### Migration Steps
|
113
|
+
|
114
|
+
1. Upgrade Modern.js version to x.67.5 or above.
|
115
|
+
2. Configure middleware or plugins in `server/modern.server.ts` according to the new definition method.
|
116
|
+
3. Migrate the custom logic in `server/index.ts` to middleware or plugins, and update your code with reference to the differences between `Context` and `Next`.
|
117
|
+
|
118
|
+
### Context Differences
|
119
|
+
|
120
|
+
In the new version, the middleware handler type is Hono's `MiddlewareHandler`, meaning the `Context` type is `Hono Context`. The differences from the old custom Web Server's `Context` are as follows:
|
121
|
+
|
122
|
+
|
123
|
+
#### UnstableMiddleware
|
124
|
+
|
125
|
+
|
126
|
+
```ts
|
127
|
+
type Body = ReadableStream | ArrayBuffer | string | null;
|
128
|
+
|
129
|
+
type UnstableMiddlewareContext<
|
130
|
+
V extends Record<string, unknown> = Record<string, unknown>,
|
131
|
+
> = {
|
132
|
+
request: Request;
|
133
|
+
response: Response;
|
134
|
+
get: Get<V>;
|
135
|
+
set: Set<V>;
|
136
|
+
// Current Matched Routing Information
|
137
|
+
route: string;
|
138
|
+
header: (name: string, value: string, options?: { append?: boolean }) => void;
|
139
|
+
status: (code: number) => void;
|
140
|
+
redirect: (location: string, status?: number) => Response;
|
141
|
+
body: (data: Body, init?: ResponseInit) => Response;
|
142
|
+
html: (
|
143
|
+
data: string | Promise<string>,
|
144
|
+
init?: ResponseInit,
|
145
|
+
) => Response | Promise<Response>;
|
146
|
+
};
|
147
|
+
```
|
148
|
+
|
149
|
+
Differences between UnstableMiddleware Context and Hono Context:
|
150
|
+
|
151
|
+
| UnstableMiddleware | Hono | Description |
|
152
|
+
| :----------------------- | :---------------------------- | :------------------------------------------------------------------------ |
|
153
|
+
| `c.request` | `c.req.raw` | Refer to [HonoRequest raw](https://hono.dev/docs/api/request#raw) documentation |
|
154
|
+
| `c.response` | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation |
|
155
|
+
| `c.route` | `c.get('route')` | Get application context information. |
|
156
|
+
| `loaderContext.get` | `honoContext.get` | After injecting data using `c.set`, consume in dataLoader: the old version uses `loaderContext.get`, refer to the new version in [Plugin](/guides/advanced-features/custom-server.html#using-posture-2) example |
|
157
|
+
|
158
|
+
|
159
|
+
#### Middleware
|
160
|
+
|
161
|
+
```ts
|
162
|
+
type MiddlewareContext = {
|
163
|
+
response: {
|
164
|
+
set: (key: string, value: string) => void;
|
165
|
+
status: (code: number) => void;
|
166
|
+
getStatus: () => number;
|
167
|
+
cookies: {
|
168
|
+
set: (key: string, value: string, options?: any) => void;
|
169
|
+
clear: () => void;
|
170
|
+
};
|
171
|
+
raw: (
|
172
|
+
body: string,
|
173
|
+
{ status, headers }: { status: number; headers: Record<string, any> },
|
174
|
+
) => void;
|
175
|
+
locals: Record<string, any>;
|
176
|
+
};
|
177
|
+
request: {
|
178
|
+
url: string;
|
179
|
+
host: string;
|
180
|
+
pathname: string;
|
181
|
+
query: Record<string, any>;
|
182
|
+
cookie: string;
|
183
|
+
cookies: {
|
184
|
+
get: (key: string) => string;
|
185
|
+
};
|
186
|
+
headers: IncomingHttpHeaders;
|
187
|
+
};
|
188
|
+
source: {
|
189
|
+
req: IncomingMessage;
|
190
|
+
res: ServerResponse;
|
191
|
+
};
|
192
|
+
};
|
193
|
+
|
194
|
+
```
|
195
|
+
|
196
|
+
Differences between Middleware `Context` and Hono `Context`:
|
197
|
+
| UnstableMiddleware | Hono | Description |
|
198
|
+
| :----------------------- | :---------------------------- | :--------------------------------------------------------------------------- |
|
199
|
+
| `c.request.cookie` | `c.req.cookie()` | Refer to [Hono Cookie Helper](https://hono.dev/docs/helpers/cookie) documentation |
|
200
|
+
| `c.request.pathname` | `c.req.path` | Refer to [HonoRequest path](https://hono.dev/docs/api/request#path) documentation |
|
201
|
+
| `c.request.url` | - | Hono `c.req.url` provides the full request URL, calculate manually from URL |
|
202
|
+
| `c.request.host` | `c.req.header('Host')` | Obtain host through header |
|
203
|
+
| `c.request.query` | `c.req.query()` | Refer to [HonoRequest query](https://hono.dev/docs/api/request#query) documentation |
|
204
|
+
| `c.request.headers` | `c.req.header()` | Refer to [HonoRequest header](https://hono.dev/docs/api/request#header) documentation |
|
205
|
+
| `c.response.set` | `c.res.headers.set` | Example: `c.res.headers.set('custom-header', '1')` |
|
206
|
+
| `c.response.status` | `c.status` | Example: `c.status(201)` |
|
207
|
+
| `c.response.cookies` | `c.header` | Example: `c.header('Set-Cookie', 'user_id=123')` |
|
208
|
+
| `c.response.raw` | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation |
|
209
|
+
|
210
|
+
#### Hook
|
211
|
+
|
212
|
+
```ts
|
213
|
+
type HookContext = {
|
214
|
+
response: {
|
215
|
+
set: (key: string, value: string) => void;
|
216
|
+
status: (code: number) => void;
|
217
|
+
getStatus: () => number;
|
218
|
+
cookies: {
|
219
|
+
set: (key: string, value: string, options?: any) => void;
|
220
|
+
clear: () => void;
|
221
|
+
};
|
222
|
+
raw: (
|
223
|
+
body: string,
|
224
|
+
{ status, headers }: { status: number; headers: Record<string, any> },
|
225
|
+
) => void;
|
226
|
+
};
|
227
|
+
request: {
|
228
|
+
url: string;
|
229
|
+
host: string;
|
230
|
+
pathname: string;
|
231
|
+
query: Record<string, any>;
|
232
|
+
cookie: string;
|
233
|
+
cookies: {
|
234
|
+
get: (key: string) => string;
|
235
|
+
};
|
236
|
+
headers: IncomingHttpHeaders;
|
237
|
+
};
|
238
|
+
};
|
239
|
+
|
240
|
+
type AfterMatchContext = HookContext & {
|
241
|
+
router: {
|
242
|
+
redirect: (url: string, status: number) => void;
|
243
|
+
rewrite: (entry: string) => void;
|
244
|
+
};
|
245
|
+
};
|
246
|
+
|
247
|
+
type AfterRenderContext = {
|
248
|
+
template: {
|
249
|
+
get: () => string;
|
250
|
+
set: (html: string) => void;
|
251
|
+
prependHead: (fragment: string) => void;
|
252
|
+
appendHead: (fragment: string) => void;
|
253
|
+
prependBody: (fragment: string) => void;
|
254
|
+
appendBody: (fragment: string) => void;
|
255
|
+
};
|
256
|
+
};
|
257
|
+
```
|
258
|
+
|
259
|
+
Hook Context is mostly consistent with Middleware Context, so we need to pay extra attention to the additional parts of different Hooks.
|
260
|
+
|
261
|
+
| UnstableMiddleware | Hono | Description |
|
262
|
+
| :----------------------- | :---------------------------- | :------------------------------------ |
|
263
|
+
| `router.redirect` | `c.redirect` | Refer to [Hono Context redirect](https://hono.dev/docs/api/context#redirect) documentation |
|
264
|
+
| `router.rewrite` | - | No corresponding capability provided at the moment |
|
265
|
+
| template API | `c.res` | Refer to [Hono Context res](https://hono.dev/docs/api/context#res) documentation |
|
266
|
+
|
267
|
+
|
268
|
+
### Differences in Next API
|
269
|
+
|
270
|
+
In Middleware and Hooks, the render function executes even without invoking `next`.
|
271
|
+
In the new design, subsequent Middleware will only execute if the `next` function is invoked.
|
@@ -4,7 +4,7 @@ sidebar_position: 4
|
|
4
4
|
---
|
5
5
|
# Data Caching
|
6
6
|
|
7
|
-
The `cache` function allows you to cache the results of data fetching or computation.
|
7
|
+
The `cache` function allows you to cache the results of data fetching or computation, Compared to full-page [rendering cache](/guides/basic-features/render/ssr-cache), it provides more fine-grained control over data granularity and is applicable to various scenarios such as Client-Side Rendering (CSR), Server-Side Rendering (SSR), and API services (BFF).
|
8
8
|
|
9
9
|
:::info
|
10
10
|
X.65.5 and above versions are required
|
@@ -12,6 +12,15 @@ X.65.5 and above versions are required
|
|
12
12
|
|
13
13
|
## Basic Usage
|
14
14
|
|
15
|
+
:::note
|
16
|
+
|
17
|
+
If you use the `cache` function in BFF, you should import the cache funtion from `@modern-js/server-runtime/cache`
|
18
|
+
|
19
|
+
`import { cache } from '@modern-js/server-runtime/cache'`
|
20
|
+
|
21
|
+
:::
|
22
|
+
|
23
|
+
|
15
24
|
```ts
|
16
25
|
import { cache } from '@modern-js/runtime/cache';
|
17
26
|
import { fetchUserData } from './api';
|
@@ -34,6 +43,8 @@ const loader = async () => {
|
|
34
43
|
- `tag`: Tag to identify the cache, which can be used to invalidate the cache
|
35
44
|
- `maxAge`: Cache validity period (milliseconds)
|
36
45
|
- `revalidate`: Time window for revalidating the cache (milliseconds), similar to HTTP Cache-Control's stale-while-revalidate functionality
|
46
|
+
- `getKey`: Simplified cache key generation function based on function parameters
|
47
|
+
- `customKey`: Custom cache key function
|
37
48
|
|
38
49
|
The type of the `options` parameter is as follows:
|
39
50
|
|
@@ -42,6 +53,12 @@ interface CacheOptions {
|
|
42
53
|
tag?: string | string[];
|
43
54
|
maxAge?: number;
|
44
55
|
revalidate?: number;
|
56
|
+
getKey?: <Args extends any[]>(...args: Args) => string;
|
57
|
+
customKey?: <Args extends any[]>(options: {
|
58
|
+
params: Args;
|
59
|
+
fn: (...args: Args) => any;
|
60
|
+
generatedKey: string;
|
61
|
+
}) => string | symbol;
|
45
62
|
}
|
46
63
|
```
|
47
64
|
|
@@ -153,6 +170,204 @@ revalidateTag('dashboard-stats'); // Invalidates the cache for both getDashboard
|
|
153
170
|
```
|
154
171
|
|
155
172
|
|
173
|
+
#### `getKey` Parameter
|
174
|
+
|
175
|
+
The `getKey` parameter simplifies cache key generation, especially useful when you only need to rely on part of the function parameters to differentiate caches. It's a function that receives the same parameters as the original function and returns a string or number as the cache key:
|
176
|
+
|
177
|
+
```ts
|
178
|
+
import { cache, CacheTime } from '@modern-js/runtime/cache';
|
179
|
+
import { fetchUserData } from './api';
|
180
|
+
|
181
|
+
const getUser = cache(
|
182
|
+
async (userId, options) => {
|
183
|
+
// Here options might contain many configurations, but we only want to cache based on userId
|
184
|
+
return await fetchUserData(userId, options);
|
185
|
+
},
|
186
|
+
{
|
187
|
+
maxAge: CacheTime.MINUTE * 5,
|
188
|
+
// Only use the first parameter (userId) as the cache key
|
189
|
+
getKey: (userId, options) => userId,
|
190
|
+
}
|
191
|
+
);
|
192
|
+
|
193
|
+
// The following two calls will share the cache because getKey only uses userId
|
194
|
+
await getUser(123, { language: 'en' });
|
195
|
+
await getUser(123, { language: 'fr' }); // Cache hit, won't request again
|
196
|
+
|
197
|
+
// Different userId will use different cache
|
198
|
+
await getUser(456, { language: 'en' }); // Won't hit cache, will request again
|
199
|
+
```
|
200
|
+
|
201
|
+
You can also use Modern.js's `generateKey` function together with getKey to generate the cache key:
|
202
|
+
|
203
|
+
:::info
|
204
|
+
|
205
|
+
The `generateKey` function in Modern.js ensures that a consistent and unique key is generated even if object property orders change, guaranteeing stable caching.
|
206
|
+
|
207
|
+
:::
|
208
|
+
|
209
|
+
```ts
|
210
|
+
import { cache, CacheTime, generateKey } from '@modern-js/runtime/cache';
|
211
|
+
import { fetchUserData } from './api';
|
212
|
+
|
213
|
+
const getUser = cache(
|
214
|
+
async (userId, options) => {
|
215
|
+
return await fetchUserData(userId, options);
|
216
|
+
},
|
217
|
+
{
|
218
|
+
maxAge: CacheTime.MINUTE * 5,
|
219
|
+
getKey: (userId, options) => generateKey(userId),
|
220
|
+
}
|
221
|
+
);
|
222
|
+
```
|
223
|
+
|
224
|
+
Additionally, `getKey` can also return a numeric type as a cache key:
|
225
|
+
|
226
|
+
```ts
|
227
|
+
const getUserById = cache(
|
228
|
+
fetchUserDataById,
|
229
|
+
{
|
230
|
+
maxAge: CacheTime.MINUTE * 5,
|
231
|
+
// Directly use the numeric ID as the cache key
|
232
|
+
getKey: (id) => id,
|
233
|
+
}
|
234
|
+
);
|
235
|
+
|
236
|
+
await getUserById(42); // Uses 42 as the cache key
|
237
|
+
```
|
238
|
+
|
239
|
+
#### `customKey` parameter
|
240
|
+
|
241
|
+
The `customKey` parameter is used to customize the cache key. It is a function that receives an object with the following properties and returns a string or Symbol type as the cache key:
|
242
|
+
|
243
|
+
- `params`: Array of arguments passed to the cached function
|
244
|
+
- `fn`: Reference to the original function being cached
|
245
|
+
- `generatedKey`: Cache key automatically generated by the framework based on input parameters
|
246
|
+
|
247
|
+
:::info
|
248
|
+
|
249
|
+
Generally, the cache will be invalidated in the following scenarios:
|
250
|
+
1. The referenced cached function changes
|
251
|
+
2. The function's input parameters change
|
252
|
+
3. The maxAge condition is no longer satisfied
|
253
|
+
4. The `revalidateTag` method has been called
|
254
|
+
|
255
|
+
`customKey` can be used in scenarios where function references are different but shared caching is desired. If it's just for customizing the cache key, it is recommended to use `getKey`.
|
256
|
+
|
257
|
+
:::
|
258
|
+
|
259
|
+
This is very useful in some scenarios, such as when the function reference changes , but you want to still return the cached data.
|
260
|
+
|
261
|
+
```ts
|
262
|
+
import { cache } from '@modern-js/runtime/cache';
|
263
|
+
import { fetchUserData } from './api';
|
264
|
+
|
265
|
+
// Different function references, but share the same cache via customKey
|
266
|
+
const getUserA = cache(
|
267
|
+
fetchUserData,
|
268
|
+
{
|
269
|
+
maxAge: CacheTime.MINUTE * 5,
|
270
|
+
customKey: ({ params }) => {
|
271
|
+
// Return a stable string as the cache key
|
272
|
+
return `user-${params[0]}`;
|
273
|
+
},
|
274
|
+
}
|
275
|
+
);
|
276
|
+
|
277
|
+
// Even if the function reference changes,
|
278
|
+
// as long as customKey returns the same value, the cache will be hit
|
279
|
+
const getUserB = cache(
|
280
|
+
(...args) => fetchUserData(...args), // New function reference
|
281
|
+
{
|
282
|
+
maxAge: CacheTime.MINUTE * 5,
|
283
|
+
customKey: ({ params }) => {
|
284
|
+
// Return the same key as getUserA
|
285
|
+
return `user-${params[0]}`;
|
286
|
+
},
|
287
|
+
}
|
288
|
+
);
|
289
|
+
|
290
|
+
// You can also use Symbol as a cache key (usually used to share cache within the same application)
|
291
|
+
const USER_CACHE_KEY = Symbol('user-cache');
|
292
|
+
const getUserC = cache(
|
293
|
+
fetchUserData,
|
294
|
+
{
|
295
|
+
maxAge: CacheTime.MINUTE * 5,
|
296
|
+
customKey: () => USER_CACHE_KEY,
|
297
|
+
}
|
298
|
+
);
|
299
|
+
|
300
|
+
// You can utilize the generatedKey parameter to modify the default key
|
301
|
+
const getUserD = cache(
|
302
|
+
fetchUserData,
|
303
|
+
{
|
304
|
+
customKey: ({ generatedKey }) => `prefix-${generatedKey}`,
|
305
|
+
}
|
306
|
+
);
|
307
|
+
```
|
308
|
+
|
309
|
+
#### `onCache` Parameter
|
310
|
+
|
311
|
+
The `onCache` parameter allows you to track cache statistics such as hit rate. It's a callback function that receives information about each cache operation, including the status, key, parameters, and result.
|
312
|
+
|
313
|
+
```ts
|
314
|
+
import { cache, CacheTime } from '@modern-js/runtime/cache';
|
315
|
+
|
316
|
+
// Track cache statistics
|
317
|
+
const stats = {
|
318
|
+
total: 0,
|
319
|
+
hits: 0,
|
320
|
+
misses: 0,
|
321
|
+
stales: 0,
|
322
|
+
hitRate: () => stats.hits / stats.total
|
323
|
+
};
|
324
|
+
|
325
|
+
const getUser = cache(
|
326
|
+
fetchUserData,
|
327
|
+
{
|
328
|
+
maxAge: CacheTime.MINUTE * 5,
|
329
|
+
onCache({ status, key, params, result }) {
|
330
|
+
// status can be 'hit', 'miss', or 'stale'
|
331
|
+
stats.total++;
|
332
|
+
|
333
|
+
if (status === 'hit') {
|
334
|
+
stats.hits++;
|
335
|
+
} else if (status === 'miss') {
|
336
|
+
stats.misses++;
|
337
|
+
} else if (status === 'stale') {
|
338
|
+
stats.stales++;
|
339
|
+
}
|
340
|
+
|
341
|
+
console.log(`Cache ${status} for key: ${String(key)}`);
|
342
|
+
console.log(`Current hit rate: ${stats.hitRate() * 100}%`);
|
343
|
+
}
|
344
|
+
}
|
345
|
+
);
|
346
|
+
|
347
|
+
// Usage example
|
348
|
+
await getUser(1); // Cache miss
|
349
|
+
await getUser(1); // Cache hit
|
350
|
+
await getUser(2); // Cache miss
|
351
|
+
```
|
352
|
+
|
353
|
+
The `onCache` callback receives an object with the following properties:
|
354
|
+
|
355
|
+
- `status`: The cache operation status, which can be:
|
356
|
+
- `hit`: Cache hit, returning cached content
|
357
|
+
- `miss`: Cache miss, executing the function and caching the result
|
358
|
+
- `stale`: Cache hit but data is stale, returning cached content while revalidating in the background
|
359
|
+
- `key`: The cache key, which is either the result of `customKey` or the default generated key
|
360
|
+
- `params`: The parameters passed to the cached function
|
361
|
+
- `result`: The result data (either from cache or newly computed)
|
362
|
+
|
363
|
+
This callback is only invoked when the `options` parameter is provided. When using the cache function without options, the `onCache` callback is not called.
|
364
|
+
|
365
|
+
The `onCache` callback is useful for:
|
366
|
+
- Monitoring cache performance
|
367
|
+
- Calculating hit rates
|
368
|
+
- Logging cache operations
|
369
|
+
- Implementing custom metrics
|
370
|
+
|
156
371
|
### Storage
|
157
372
|
|
158
373
|
Currently, both client and server caches are stored in memory.
|
@@ -149,9 +149,10 @@ Having the `loader` function run only on the server in SSR applications brings s
|
|
149
149
|
- **Simplifies usage**: Guarantees consistent data-fetching methods in SSR applications, so developers don't have to distinguish between client and server code.
|
150
150
|
- **Reduces client bundle size**: Moves logic code and dependencies from the client to the server.
|
151
151
|
- **Improves maintainability**: Less direct influence of data logic on front-end UI and avoids issues of accidentally including server dependencies in the client bundle or vice versa.
|
152
|
+
|
152
153
|
:::
|
153
154
|
|
154
|
-
We recommend using the `fetch` API in `loader` functions to make requests.
|
155
|
+
We recommend using the `fetch` API in `loader` functions to make requests. Since `fetch` works similarly on the client and server, you can fetch data in a consistent manner whether in CSR or SSR:
|
155
156
|
|
156
157
|
```tsx
|
157
158
|
export async function loader() {
|
@@ -136,13 +136,14 @@ Full-stack projects refer to projects that use Custom Web Server, SSR or BFF. Th
|
|
136
136
|
:::info
|
137
137
|
1. Currently, Modern.js does not support deployment on Netlify Edge Functions. We will support it in future versions.
|
138
138
|
2. You can refer to the [deployment project example](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-ssr).
|
139
|
+
|
139
140
|
:::
|
140
141
|
|
141
142
|
|
142
143
|
### Monorepo
|
143
144
|
|
144
145
|
:::info
|
145
|
-
The following guide is mainly for full-stack projects, for pure CSR projects, just follow [Pure Frontend Project](#
|
146
|
+
The following guide is mainly for full-stack projects, for pure CSR projects, just follow [Pure Frontend Project](#pure-front-end-project-1) to deploy.
|
146
147
|
:::
|
147
148
|
|
148
149
|
For Monorepo projects, in addition to building our current project, you also need to build other sub-projects in the repository that the current project depends on.
|
@@ -330,6 +331,7 @@ For branch deployment, follow these steps:
|
|
330
331
|
:::info
|
331
332
|
1. Running `MODERNJS_DEPLOY=ghPages modern deploy` will build the production output for GitHub in the .output directory.
|
332
333
|
2. You can refer to the [project](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr)
|
334
|
+
|
333
335
|
:::
|
334
336
|
|
335
337
|
For GitHub Actions deployment, select Settings > Pages > Source > GitHub Actions, and add a workflow file to the project. You can refer to the [example](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/modern-js-deploy-csr).
|
@@ -8,7 +8,7 @@ Modern.js provides **JSX syntax** and **HTML(EJS) syntax** to customize the HTML
|
|
8
8
|
|
9
9
|
## JSX Syntax
|
10
10
|
|
11
|
-
According to Modern.js conventions, you can create a `Document.[jt]sx` file under `src/` or the entry directory and default export a component
|
11
|
+
According to Modern.js conventions, you can create a `Document.[jt]sx` file under `src/` or the entry directory and default export a component. The rendering result of this component can be used as the HTML template of the entry.
|
12
12
|
|
13
13
|
For example, consider the following directory structure:
|
14
14
|
|
@@ -218,9 +218,9 @@ The implementation of custom HTML fragments is to merge the fragments with the b
|
|
218
218
|
|
219
219
|
:::
|
220
220
|
|
221
|
-
###
|
221
|
+
### Customize the entire HTML Template
|
222
222
|
|
223
|
-
In some cases, HTML fragments may
|
223
|
+
In some cases, HTML fragments may not offer enough control. Modern.js provides a fully customized way.
|
224
224
|
|
225
225
|
:::caution Note
|
226
226
|
It is generally not recommended to directly override the default HTML template, as some functional options may be lost. If it is truly necessary to customize the entire HTML template, it is recommended to modify based on the built-in template as needed.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Server-Side Rendering
|
2
2
|
|
3
|
-
Server-Side Rendering (SSR) involves rendering the HTML content of a webpage-side and then sending the fully-rendered page to the browser. The browser only needs to display the page without additional rendering.
|
3
|
+
Server-Side Rendering (SSR) involves rendering the HTML content of a webpage server-side and then sending the fully-rendered page to the browser. The browser only needs to display the page without additional rendering.
|
4
4
|
|
5
5
|
The main advantages are:
|
6
6
|
|
@@ -320,4 +320,4 @@ In this case, the `epicMiddleware` instance is created outside the component, an
|
|
320
320
|
|
321
321
|
This code does not cause issues on the client-side. However, in SSR, the Middleware instance remains non-disposable. Each time the component renders, calling `epicMiddleware.run(rootEpic)` adds new event bindings internally, causing the entire object to grow continuously, ultimately affecting application performance.
|
322
322
|
|
323
|
-
Such issues are not easily noticed in CSR. When transitioning from CSR to SSR, if you're unsure whether your application has such hidden pitfalls, consider stress testing the application.
|
323
|
+
Such issues are not easily noticed in CSR. When transitioning from CSR to SSR, if you're unsure whether your application has such hidden pitfalls, consider stress testing the application.
|
@@ -76,7 +76,7 @@ By default, Modern.js scans the files under `src/` before starting the project,
|
|
76
76
|
|
77
77
|
:::tip
|
78
78
|
|
79
|
-
- You can
|
79
|
+
- You can customize the recognition directory for page entries by using [source.entriesDir](/configure/app/source/entries-dir).
|
80
80
|
- If you need to customize the entry points, please refer to [Custom Entries](#custom-entries).
|
81
81
|
|
82
82
|
:::
|
@@ -148,7 +148,7 @@ info Starting production server...
|
|
148
148
|
> Network: http://192.168.0.1:8080/
|
149
149
|
```
|
150
150
|
|
151
|
-
Open `http://localhost:
|
151
|
+
Open `http://localhost:8080/` in the browser, and the content should be consistent with that of `pnpm run dev`.
|
152
152
|
|
153
153
|
## Deployment
|
154
154
|
|
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
This document details the API for Modern.js CLI plugins. CLI plugins allow you to extend and customize the functionality of Modern.js projects during the build and development process.
|
4
4
|
|
5
|
+
:::info
|
6
|
+
|
7
|
+
CLI plugins need to be configured via the [`plugins`](/configure/app/plugins) field in `modern.config.ts`.
|
8
|
+
|
9
|
+
:::
|
10
|
+
|
5
11
|
## Plugin Basic Structure
|
6
12
|
|
7
13
|
A typical CLI plugin structure is as follows:
|
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
Modern.js's Runtime Plugins allow you to extend and modify the behavior of your application during its React code execution. With Runtime Plugins, you can easily perform initialization tasks, implement React Higher-Order Component (HOC) wrapping, and more.
|
4
4
|
|
5
|
+
:::info
|
6
|
+
|
7
|
+
Runtime plugins need to be configured via the [`plugins`](/configure/app/runtime/plugins) field in `src/modern.runtime.ts`.
|
8
|
+
|
9
|
+
:::
|
10
|
+
|
5
11
|
## Plugin Structure
|
6
12
|
|
7
13
|
A typical Runtime Plugin looks like this:
|
@@ -150,16 +156,35 @@ Allows you to wrap the application's root component with a custom React componen
|
|
150
156
|
You can combine multiple hooks to implement more complex functionality. For example, you can use `onBeforeRender` to fetch data and then use `wrapRoot` to pass the data to the entire application via Context:
|
151
157
|
|
152
158
|
```ts
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
});
|
157
|
-
|
158
|
-
|
159
|
-
return
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
159
|
+
import { RuntimePluginFuture, RuntimeReactContext } from '@modern-js/runtime';
|
160
|
+
import { useContext, createContext } from 'react';
|
161
|
+
|
162
|
+
export const ThemeContext = createContext<{ theme: string } | null>(null);
|
163
|
+
|
164
|
+
export const themePlugin = (): RuntimePluginFuture => {
|
165
|
+
return {
|
166
|
+
name: 'theme-plugin',
|
167
|
+
setup: api => {
|
168
|
+
api.onBeforeRender(async context => {
|
169
|
+
const userPreference = await fetch('/api/user/theme-settings').then(
|
170
|
+
res => res.json(),
|
171
|
+
);
|
172
|
+
context.data = {
|
173
|
+
theme: userPreference.theme,
|
174
|
+
};
|
175
|
+
});
|
176
|
+
|
177
|
+
api.wrapRoot(App => {
|
178
|
+
return props => {
|
179
|
+
const context = useContext(RuntimeReactContext);
|
180
|
+
return (
|
181
|
+
<ThemeContext.Provider value={context.data}>
|
182
|
+
<App {...props} />
|
183
|
+
</ThemeContext.Provider>
|
184
|
+
);
|
185
|
+
};
|
186
|
+
});
|
187
|
+
},
|
188
|
+
};
|
189
|
+
};
|
165
190
|
```
|
@@ -7,7 +7,7 @@ In the previous chapter, we learned how to add client route.
|
|
7
7
|
|
8
8
|
In this chapter, we will learn how to add **Loader** to the routing component.
|
9
9
|
|
10
|
-
|
10
|
+
So far, we have provided data to components through hardcoding. If you want to get data from the remote, you usually use `useEffect` to do it. But when SSR is enabled, `useEffect` will not be executed at the server level, so this SSR can only render a very limited UI.
|
11
11
|
|
12
12
|
Modern.js provides the ability of Data Loader to support homogeneous data acquisition in components to maximize the value of SSR.
|
13
13
|
|
@@ -3,6 +3,14 @@ title: filename
|
|
3
3
|
configName: output.filename
|
4
4
|
---
|
5
5
|
|
6
|
+
:::warning
|
7
|
+
|
8
|
+
在 Modern.js 开发阶段,或是使用 Modern.js 服务器部署应用时,应避免使用该配置修改 html 产物文件名,会导致页面 404。
|
9
|
+
|
10
|
+
通常情况下,无需修改 html 产物文件名。常见的需求是将 `main.html` 修改为 `index.html`,请使用 [source.mainEntryName](/configure/app/source/main-entry-name)。
|
11
|
+
|
12
|
+
:::
|
13
|
+
|
6
14
|
# output.filename
|
7
15
|
|
8
16
|
- **类型:**
|
@@ -1,39 +1,18 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 9
|
3
|
-
---
|
4
|
-
|
5
1
|
# plugins
|
6
2
|
|
7
3
|
- **类型:** `CliPlugin[]`
|
8
4
|
- **默认值:** `[]`
|
9
5
|
|
10
|
-
用于配置自定义的 Modern.js
|
11
|
-
|
12
|
-
自定义插件的编写方式请参考 [如何编写插件](/plugin/plugin-system)。
|
6
|
+
用于配置自定义的 Modern.js 框架 CLI 插件。自定义 CLI 插件的编写方式请参考 [如何编写 CLI 插件](/plugin/introduction.html#cli-插件)。
|
13
7
|
|
14
8
|
## 注意事项
|
15
9
|
|
16
|
-
|
10
|
+
该选项**用于配置框架 CLI 插件**,如果你需要配置其他类型的插件,请选择对应的配置方式:
|
17
11
|
|
18
12
|
- 配置 Rsbuild 插件,请使用 [builderPlugins](/configure/app/builder-plugins) 配置项。
|
19
13
|
- 配置 Rspack 或 webpack 插件,请使用 [tools.bundlerChain](/configure/app/tools/bundler-chain) 配置项。
|
20
14
|
- 配置 Babel 插件,请使用 [tools.babel](/configure/app/tools/babel) 配置项。
|
21
|
-
|
22
|
-
## 插件类型
|
23
|
-
|
24
|
-
Modern.js 中内置了三种不同的框架插件:
|
25
|
-
|
26
|
-
- `CLI 插件`,适用于本地开发、编译构建阶段,可以在命令行和编译阶段扩展各种能力。
|
27
|
-
- `Server 插件`,适用于服务端。
|
28
|
-
- `Runtime 插件`,适用于前端运行时。
|
29
|
-
|
30
|
-
目前 Modern.js 开放了自定义 CLI 插件的能力,Server 插件和 Runtime 插件会在后续开放。
|
31
|
-
|
32
|
-
## 插件执行顺序
|
33
|
-
|
34
|
-
默认情况下,自定义插件会按照 `plugins` 数组的顺序依次执行,Modern.js 内置插件的执行时机早于自定义插件。
|
35
|
-
|
36
|
-
当插件内部使用了控制顺序的相关字段,比如 `pre`、`post` 时,会基于声明的字段对执行顺序进行调整,详见 [插件结构](/plugin/plugin-system)。
|
15
|
+
- 配置框架 Runtime 插件,请使用 [runtime 配置文件 plugins](/configure/app/runtime/plugins) 配置项。
|
37
16
|
|
38
17
|
## 示例
|
39
18
|
|