@modern-js/main-doc 3.0.0-alpha.0 → 3.0.0-alpha.1
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/commands.mdx +6 -30
- package/docs/en/components/bff-upload.mdx +3 -5
- package/docs/en/components/bundler.mdx +1 -1
- package/docs/en/components/enable-bff.mdx +6 -2
- package/docs/en/components/enable-ssg.mdx +1 -0
- package/docs/en/components/esbuild.mdx +2 -2
- package/docs/en/components/extend-bff-function.mdx +2 -4
- package/docs/en/components/hono.mdx +119 -0
- package/docs/en/components/international/custom-instance-code.mdx +16 -0
- package/docs/en/components/international/init-options-desc.mdx +1 -0
- package/docs/en/components/international/install-command.mdx +15 -1
- package/docs/en/components/international/instance-code.mdx +26 -0
- package/docs/en/configure/app/builder-plugins.mdx +1 -2
- package/docs/en/configure/app/dev/server.mdx +108 -0
- package/docs/en/configure/app/experiments/source-build.mdx +0 -1
- package/docs/en/configure/app/output/assets-retry.mdx +1 -1
- package/docs/en/configure/app/output/disable-inline-runtime-chunk.mdx +2 -2
- package/docs/en/configure/app/output/filename.mdx +2 -4
- package/docs/en/configure/app/output/temp-dir.mdx +3 -3
- package/docs/en/configure/app/performance/build-cache.mdx +1 -1
- package/docs/en/configure/app/performance/profile.mdx +1 -1
- package/docs/en/configure/app/plugins.mdx +1 -3
- package/docs/en/configure/app/runtime/router.mdx +0 -4
- package/docs/en/configure/app/security/sri.mdx +0 -1
- package/docs/en/configure/app/source/alias.mdx +1 -1
- package/docs/en/configure/app/source/enable-async-entry.mdx +1 -1
- package/docs/en/configure/app/source/include.mdx +2 -14
- package/docs/en/configure/app/tools/dev-server.mdx +8 -8
- package/docs/en/configure/app/usage.mdx +0 -12
- package/docs/en/guides/_meta.json +5 -0
- package/docs/en/guides/advanced-features/bff/_meta.json +9 -1
- package/docs/en/guides/advanced-features/bff/cross-project.mdx +1 -1
- package/docs/en/guides/advanced-features/bff/frameworks.mdx +2 -15
- package/docs/en/guides/advanced-features/bff/function.mdx +4 -4
- package/docs/en/guides/advanced-features/bff/operators.mdx +628 -0
- package/docs/en/guides/advanced-features/bff/sdk.mdx +17 -9
- package/docs/en/guides/advanced-features/bff/upload.mdx +3 -1
- package/docs/en/guides/advanced-features/international/configuration.mdx +7 -16
- package/docs/en/guides/advanced-features/international/quick-start.mdx +4 -32
- package/docs/en/guides/advanced-features/page-performance/optimize-bundle.mdx +1 -1
- package/docs/en/guides/advanced-features/page-performance/react-compiler.mdx +18 -4
- package/docs/en/guides/advanced-features/rspack-start.mdx +1 -1
- package/docs/en/guides/advanced-features/server-monitor/monitors.mdx +62 -5
- package/docs/en/guides/basic-features/data/data-cache.mdx +60 -76
- package/docs/en/guides/basic-features/data/data-fetch.mdx +15 -14
- package/docs/en/guides/basic-features/debug/proxy.mdx +6 -9
- package/docs/en/guides/basic-features/render/rsc.mdx +24 -19
- package/docs/en/guides/basic-features/render/ssg.mdx +4 -9
- package/docs/en/guides/basic-features/render/ssr-cache.mdx +0 -4
- package/docs/en/guides/basic-features/static-assets/svg-assets.mdx +0 -4
- package/docs/en/guides/get-started/tech-stack.mdx +1 -1
- package/docs/en/guides/upgrade/_meta.json +1 -0
- package/docs/en/guides/upgrade/config.mdx +936 -0
- package/docs/en/guides/upgrade/entry.mdx +463 -0
- package/docs/en/guides/upgrade/other.mdx +83 -0
- package/docs/en/guides/upgrade/overview.mdx +33 -0
- package/docs/en/guides/upgrade/tailwindcss.mdx +130 -0
- package/docs/en/guides/upgrade/web-server.mdx +91 -0
- package/docs/en/plugin/_meta.json +5 -0
- package/docs/en/plugin/cli-plugins/_meta.json +1 -1
- package/docs/en/plugin/cli-plugins/api.mdx +13 -63
- package/docs/en/plugin/cli-plugins/life-cycle.mdx +0 -4
- package/docs/en/plugin/introduction.mdx +8 -20
- package/docs/en/plugin/plugin-system.mdx +3 -3
- package/docs/en/plugin/runtime-plugins/_meta.json +1 -1
- package/docs/en/plugin/runtime-plugins/api.mdx +1 -1
- package/docs/en/plugin/server-plugins/_meta.json +1 -0
- package/docs/en/plugin/server-plugins/api.mdx +210 -1
- package/docs/en/plugin/server-plugins/life-cycle.mdx +41 -1
- package/docs/zh/apis/app/commands.mdx +6 -30
- package/docs/zh/components/bff-operator-code.mdx +5 -0
- package/docs/zh/components/bff-upload.mdx +0 -2
- package/docs/zh/components/bundler.mdx +1 -1
- package/docs/zh/components/enable-bff.mdx +6 -2
- package/docs/zh/components/enable-ssg.mdx +3 -1
- package/docs/zh/components/esbuild.mdx +2 -2
- package/docs/zh/components/extend-bff-function.mdx +2 -4
- package/docs/zh/components/hono.mdx +119 -0
- package/docs/zh/components/international/custom-instance-code.mdx +16 -0
- package/docs/zh/components/international/init-options-desc.mdx +1 -0
- package/docs/zh/components/international/install-command.mdx +15 -0
- package/docs/zh/components/international/instance-code.mdx +26 -0
- package/docs/zh/configure/app/builder-plugins.mdx +1 -2
- package/docs/zh/configure/app/dev/server.mdx +109 -2
- package/docs/zh/configure/app/experiments/source-build.mdx +0 -1
- package/docs/zh/configure/app/output/assets-retry.mdx +1 -1
- package/docs/zh/configure/app/output/disable-inline-runtime-chunk.mdx +2 -2
- package/docs/zh/configure/app/output/filename.mdx +2 -4
- package/docs/zh/configure/app/output/temp-dir.mdx +3 -3
- package/docs/zh/configure/app/performance/build-cache.mdx +1 -1
- package/docs/zh/configure/app/performance/profile.mdx +1 -1
- package/docs/zh/configure/app/plugins.mdx +1 -2
- package/docs/zh/configure/app/runtime/router.mdx +0 -4
- package/docs/zh/configure/app/security/sri.mdx +0 -1
- package/docs/zh/configure/app/source/alias.mdx +1 -1
- package/docs/zh/configure/app/source/enable-async-entry.mdx +1 -1
- package/docs/zh/configure/app/source/include.mdx +2 -16
- package/docs/zh/configure/app/tools/dev-server.mdx +5 -5
- package/docs/zh/configure/app/usage.mdx +0 -12
- package/docs/zh/guides/advanced-features/bff/_meta.json +9 -1
- package/docs/zh/guides/advanced-features/bff/frameworks.mdx +2 -16
- package/docs/zh/guides/advanced-features/bff/operators.mdx +628 -0
- package/docs/zh/guides/advanced-features/bff/sdk.mdx +19 -12
- package/docs/zh/guides/advanced-features/bff/upload.mdx +3 -1
- package/docs/zh/guides/advanced-features/international/configuration.mdx +7 -16
- package/docs/zh/guides/advanced-features/international/quick-start.mdx +2 -25
- package/docs/zh/guides/advanced-features/page-performance/optimize-bundle.mdx +1 -1
- package/docs/zh/guides/advanced-features/page-performance/react-compiler.mdx +18 -4
- package/docs/zh/guides/advanced-features/server-monitor/monitors.mdx +60 -5
- package/docs/zh/guides/basic-features/data/data-cache.mdx +47 -54
- package/docs/zh/guides/basic-features/data/data-fetch.mdx +9 -12
- package/docs/zh/guides/basic-features/debug/proxy.mdx +4 -7
- package/docs/zh/guides/basic-features/render/rsc.mdx +23 -37
- package/docs/zh/guides/basic-features/render/ssr-cache.mdx +0 -4
- package/docs/zh/guides/basic-features/static-assets/svg-assets.mdx +0 -4
- package/docs/zh/guides/get-started/tech-stack.mdx +1 -1
- package/docs/zh/guides/troubleshooting/builder.mdx +1 -1
- package/docs/zh/guides/upgrade/config.mdx +132 -1
- package/docs/zh/plugin/_meta.json +5 -0
- package/docs/zh/plugin/cli-plugins/_meta.json +1 -1
- package/docs/zh/plugin/cli-plugins/api.mdx +15 -65
- package/docs/zh/plugin/cli-plugins/life-cycle.mdx +0 -4
- package/docs/zh/plugin/introduction.mdx +4 -16
- package/docs/zh/plugin/plugin-system.mdx +3 -14
- package/docs/zh/plugin/runtime-plugins/_meta.json +1 -1
- package/docs/zh/plugin/runtime-plugins/api.mdx +1 -1
- package/docs/zh/plugin/server-plugins/_meta.json +1 -0
- package/docs/zh/plugin/server-plugins/api.mdx +210 -1
- package/docs/zh/plugin/server-plugins/life-cycle.mdx +41 -1
- package/package.json +2 -2
- package/src/components/FrameworkCode/index.tsx +605 -0
- package/docs/en/configure/app/performance/bundle-analyze.mdx +0 -24
- package/docs/en/configure/app/tools/babel.mdx +0 -225
- package/docs/en/plugin/cli-plugins/migration.mdx +0 -83
- package/docs/en/plugin/runtime-plugins/migration.mdx +0 -110
- package/docs/zh/components/default-mwa-generate.mdx +0 -4
- package/docs/zh/configure/app/performance/bundle-analyze.mdx +0 -24
- package/docs/zh/configure/app/tools/babel.mdx +0 -224
- package/docs/zh/plugin/cli-plugins/migration.mdx +0 -83
- package/docs/zh/plugin/runtime-plugins/migration.mdx +0 -110
- /package/docs/en/components/{router-legacy-tip.mdx → upgrade-config-deploy.mdx} +0 -0
- /package/docs/zh/components/{router-legacy-tip.mdx → upgrade-config-deploy.mdx} +0 -0
|
@@ -105,6 +105,7 @@ i18nPlugin({
|
|
|
105
105
|
1. **If you configure `loadPath` or `addPath`**: The backend will be automatically enabled (`enabled: true`) without checking for locales directory, since you've already specified the resource path.
|
|
106
106
|
|
|
107
107
|
2. **If you don't configure backend**: The plugin will automatically detect if a `locales` directory exists in:
|
|
108
|
+
|
|
108
109
|
- Project root: `{projectRoot}/locales`
|
|
109
110
|
- Config public directory: `{projectRoot}/config/public/locales`
|
|
110
111
|
- Public directory configured via `server.publicDir`: `{projectRoot}/{publicDir}/locales`
|
|
@@ -331,27 +332,17 @@ export default defineRuntimeConfig({
|
|
|
331
332
|
|
|
332
333
|
### i18nInstance Configuration
|
|
333
334
|
|
|
334
|
-
If you need to use a custom
|
|
335
|
-
|
|
336
|
-
```ts
|
|
337
|
-
import { defineRuntimeConfig } from '@modern-js/runtime';
|
|
338
|
-
import i18next from 'i18next';
|
|
335
|
+
If you need to use a custom i18n instance, you can provide it in runtime configuration:
|
|
339
336
|
|
|
340
|
-
|
|
341
|
-
const customI18n = i18next.createInstance({
|
|
342
|
-
// Custom configuration
|
|
343
|
-
});
|
|
337
|
+
import CustomInstanceCode from '@site-docs/components/international/custom-instance-code';
|
|
344
338
|
|
|
345
|
-
|
|
346
|
-
i18n: {
|
|
347
|
-
i18nInstance: customI18n,
|
|
348
|
-
},
|
|
349
|
-
});
|
|
350
|
-
```
|
|
339
|
+
<CustomInstanceCode />
|
|
351
340
|
|
|
352
341
|
### initOptions Configuration
|
|
353
342
|
|
|
354
|
-
|
|
343
|
+
import InitOptionsDesc from '@site-docs/components/international/init-options-desc';
|
|
344
|
+
|
|
345
|
+
<InitOptionsDesc />
|
|
355
346
|
|
|
356
347
|
:::info
|
|
357
348
|
If `localePathRedirect` is enabled, the `detection` configuration should be set in CLI configuration, not in `initOptions`. This is because the server-side plugin needs to read the `detection` option from CLI configuration to perform language detection and path redirection.
|
|
@@ -10,14 +10,9 @@ This guide will help you quickly integrate internationalization functionality in
|
|
|
10
10
|
|
|
11
11
|
First, install the necessary dependencies:
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
pnpm add @modern-js/plugin-i18n i18next react-i18next
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
:::info
|
|
18
|
-
`i18next` and `react-i18next` are peer dependencies and need to be installed manually.
|
|
13
|
+
import InstallCommand from '@site-docs/components/international/install-command';
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
<InstallCommand />
|
|
21
16
|
|
|
22
17
|
## Basic Configuration
|
|
23
18
|
|
|
@@ -56,32 +51,9 @@ export default defineConfig({
|
|
|
56
51
|
|
|
57
52
|
### Configure Runtime Options in `src/modern.runtime.ts`
|
|
58
53
|
|
|
59
|
-
|
|
54
|
+
import InstanceCode from '@site-docs/components/international/instance-code';
|
|
60
55
|
|
|
61
|
-
|
|
62
|
-
import { defineRuntimeConfig } from '@modern-js/runtime';
|
|
63
|
-
import i18next from 'i18next';
|
|
64
|
-
|
|
65
|
-
// It's recommended to create a new i18next instance to avoid using the global default instance
|
|
66
|
-
const i18nInstance = i18next.createInstance();
|
|
67
|
-
|
|
68
|
-
export default defineRuntimeConfig({
|
|
69
|
-
i18n: {
|
|
70
|
-
i18nInstance: i18nInstance,
|
|
71
|
-
initOptions: {
|
|
72
|
-
fallbackLng: 'en',
|
|
73
|
-
supportedLngs: ['zh', 'en'],
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
:::info
|
|
80
|
-
`modern.runtime.ts` is a runtime configuration file used to configure i18next initialization options. Even for the most basic configuration, it's recommended to create this file to ensure the plugin works correctly.
|
|
81
|
-
|
|
82
|
-
It's recommended to use `i18next.createInstance()` to create a new instance instead of directly using the default exported `i18next`. This avoids global state pollution and ensures each application has an independent i18n instance.
|
|
83
|
-
|
|
84
|
-
:::
|
|
56
|
+
<InstanceCode />
|
|
85
57
|
|
|
86
58
|
### Create Language Resource Files
|
|
87
59
|
|
|
@@ -40,7 +40,7 @@ If you want to find out the large libraries in the project, you can add the `BUN
|
|
|
40
40
|
BUNDLE_ANALYZE=true pnpm build
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
This will generate a bundle analysis report to help you identify large dependencies.
|
|
44
44
|
|
|
45
45
|
## Adjust Browserslist
|
|
46
46
|
|
|
@@ -6,11 +6,25 @@ Before starting to use the React Compiler, it is recommended to read the [React
|
|
|
6
6
|
|
|
7
7
|
## How to Use
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### React 19
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
If you are using React 19, Modern.js has built-in support for React Compiler, and no additional configuration is required.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### React 17 or 18
|
|
14
|
+
|
|
15
|
+
If you are using React 17 or 18, you need to configure it as follows:
|
|
16
|
+
|
|
17
|
+
1. Install `react-compiler-runtime` to allow the compiled code to run on versions before 19:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install react-compiler-runtime
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. Install `babel-plugin-react-compiler`:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install babel-plugin-react-compiler
|
|
27
|
+
```
|
|
14
28
|
|
|
15
29
|
3. Register the Babel plugin in your Modern.js configuration file:
|
|
16
30
|
|
|
@@ -24,7 +38,7 @@ export default defineConfig({
|
|
|
24
38
|
[
|
|
25
39
|
'babel-plugin-react-compiler',
|
|
26
40
|
{
|
|
27
|
-
target: '18',
|
|
41
|
+
target: '18', // or '17', depending on your React version
|
|
28
42
|
},
|
|
29
43
|
],
|
|
30
44
|
]);
|
|
@@ -73,7 +73,7 @@ export default defineConfig({
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
:::tip
|
|
76
|
-
When Rspack build is enabled, `babel-loader` is not enabled by default. If you need to add some babel plugins, you can configure it through [
|
|
76
|
+
When Rspack build is enabled, `babel-loader` is not enabled by default. If you need to add some babel plugins, you can configure it through [babel plugin](https://rsbuild.rs/plugins/list/plugin-babel#babel-plugin). This will generate additional compilation overhead and slow down the Rspack build speed to a certain extent.
|
|
77
77
|
:::
|
|
78
78
|
|
|
79
79
|
## The relationship between Rspack and Modern.js versions
|
|
@@ -73,11 +73,7 @@ Modern.js comes with a built-in default Monitor, where different events trigger
|
|
|
73
73
|
|
|
74
74
|
## Register Monitors
|
|
75
75
|
|
|
76
|
-
Developers can register their own Monitors using the `push` API, but this can only be done in [server middleware](/guides/advanced-features/web-server#unstable-middleware) or server plugins. Registration is not available in Data Loaders, components, or init functions.
|
|
77
|
-
|
|
78
|
-
:::note
|
|
79
|
-
Server plugins are currently not available, and documentation will be added in the future.
|
|
80
|
-
:::
|
|
76
|
+
Developers can register their own Monitors using the `push` API, but this can only be done in [server middleware](/guides/advanced-features/web-server#unstable-middleware) or [server plugins](/plugin/server-plugins/api). Registration is not available in Data Loaders, components, or init functions.
|
|
81
77
|
|
|
82
78
|
```ts title="server/modern.server.ts"
|
|
83
79
|
import {
|
|
@@ -114,6 +110,67 @@ export default defineServerConfig({
|
|
|
114
110
|
});
|
|
115
111
|
```
|
|
116
112
|
|
|
113
|
+
Register Monitor in server plugins:
|
|
114
|
+
|
|
115
|
+
```ts title="server/plugins/my-monitor-plugin.ts"
|
|
116
|
+
import type { ServerPlugin } from '@modern-js/server-runtime';
|
|
117
|
+
import type { MonitorEvent } from '@modern-js/types';
|
|
118
|
+
|
|
119
|
+
const myMonitorPlugin = (): ServerPlugin => ({
|
|
120
|
+
name: '@my-org/my-monitor-plugin',
|
|
121
|
+
setup(api) {
|
|
122
|
+
api.onPrepare(() => {
|
|
123
|
+
const { middlewares } = api.getServerContext();
|
|
124
|
+
|
|
125
|
+
// Define monitor, ensuring it's only created once
|
|
126
|
+
const myMonitor = (event: MonitorEvent) => {
|
|
127
|
+
if (event.type === 'log') {
|
|
128
|
+
// Handle log events
|
|
129
|
+
console.log(`[${event.payload.level}] ${event.payload.message}`);
|
|
130
|
+
} else if (event.type === 'timing') {
|
|
131
|
+
// Handle timing events
|
|
132
|
+
console.log(
|
|
133
|
+
`Timing: ${event.payload.name} = ${event.payload.dur}ms`,
|
|
134
|
+
);
|
|
135
|
+
} else if (event.type === 'counter') {
|
|
136
|
+
// Handle counter events
|
|
137
|
+
console.log(`Counter: ${event.payload.name}`);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Use a flag to ensure monitor is only registered once
|
|
142
|
+
let monitorRegistered = false;
|
|
143
|
+
|
|
144
|
+
middlewares.push({
|
|
145
|
+
name: 'inject-monitor',
|
|
146
|
+
handler: async (c, next) => {
|
|
147
|
+
const monitors = c.get('monitors');
|
|
148
|
+
// Only register monitor on the first request
|
|
149
|
+
if (!monitorRegistered) {
|
|
150
|
+
monitors.push(myMonitor);
|
|
151
|
+
monitorRegistered = true;
|
|
152
|
+
}
|
|
153
|
+
await next();
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
export default myMonitorPlugin;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Then configure the plugin in `server/modern.server.ts`:
|
|
164
|
+
|
|
165
|
+
```ts title="server/modern.server.ts"
|
|
166
|
+
import { defineServerConfig } from '@modern-js/server-runtime';
|
|
167
|
+
import myMonitorPlugin from './plugins/my-monitor-plugin';
|
|
168
|
+
|
|
169
|
+
export default defineServerConfig({
|
|
170
|
+
plugins: [myMonitorPlugin()],
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
117
174
|
## Use Monitors
|
|
118
175
|
|
|
119
176
|
Modern.js allows developers to invoke Monitors in Data Loaders and components.
|
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
title: Data Caching
|
|
3
3
|
sidebar_position: 4
|
|
4
4
|
---
|
|
5
|
+
|
|
5
6
|
# Data Caching
|
|
6
7
|
|
|
7
8
|
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
9
|
|
|
9
|
-
:::info
|
|
10
|
-
X.65.5 and above versions are required
|
|
11
|
-
:::
|
|
12
|
-
|
|
13
10
|
## Basic Usage
|
|
14
11
|
|
|
15
12
|
:::note
|
|
@@ -20,7 +17,6 @@ If you use the `cache` function in BFF, you should import the cache funtion from
|
|
|
20
17
|
|
|
21
18
|
:::
|
|
22
19
|
|
|
23
|
-
|
|
24
20
|
```ts
|
|
25
21
|
import { cache } from '@modern-js/runtime/cache';
|
|
26
22
|
import { fetchUserData } from './api';
|
|
@@ -28,14 +24,13 @@ import { fetchUserData } from './api';
|
|
|
28
24
|
const getUser = cache(fetchUserData);
|
|
29
25
|
|
|
30
26
|
const loader = async () => {
|
|
31
|
-
const user = await getUser(
|
|
27
|
+
const user = await getUser('123'); // When the parameters of the function changes, the function will be re-executed
|
|
32
28
|
return {
|
|
33
29
|
user,
|
|
34
30
|
};
|
|
35
31
|
};
|
|
36
32
|
```
|
|
37
33
|
|
|
38
|
-
|
|
39
34
|
### Parameters
|
|
40
35
|
|
|
41
36
|
- `fn`: The data fetching or computation function to be cached
|
|
@@ -44,7 +39,7 @@ const loader = async () => {
|
|
|
44
39
|
- `maxAge`: Cache validity period (milliseconds)
|
|
45
40
|
- `revalidate`: Time window for revalidating the cache (milliseconds), similar to HTTP Cache-Control's stale-while-revalidate functionality
|
|
46
41
|
- `getKey`: Simplified cache key generation function based on function parameters
|
|
47
|
-
- `customKey`: Custom cache key function
|
|
42
|
+
- `customKey`: Custom cache key generation function, used to maintain cache when function references change
|
|
48
43
|
|
|
49
44
|
The type of the `options` parameter is as follows:
|
|
50
45
|
|
|
@@ -62,7 +57,6 @@ interface CacheOptions {
|
|
|
62
57
|
}
|
|
63
58
|
```
|
|
64
59
|
|
|
65
|
-
|
|
66
60
|
### Return Value
|
|
67
61
|
|
|
68
62
|
The `cache` function returns a new function with caching capabilities. Multiple calls to this function will not re-execute the `fn` function.
|
|
@@ -76,7 +70,7 @@ EdenX's `cache` function can be used in any frontend or server-side code.
|
|
|
76
70
|
|
|
77
71
|
### Without options Parameter
|
|
78
72
|
|
|
79
|
-
When no `options` parameter is provided, it's primarily useful in SSR projects, the cache lifecycle is limited to a single SSR rendering request. For example, when the same cachedFn is called in multiple data loaders, the cachedFn function won't be executed repeatedly. This allows data sharing between different data loaders while avoiding duplicate requests.
|
|
73
|
+
When no `options` parameter is provided, it's primarily useful in SSR projects, the cache lifecycle is limited to a single SSR rendering request. For example, when the same cachedFn is called in multiple data loaders, the cachedFn function won't be executed repeatedly. This allows data sharing between different data loaders while avoiding duplicate requests. Modern.js will re-execute the `fn` function with each server request.
|
|
80
74
|
|
|
81
75
|
:::info
|
|
82
76
|
Without the `options` parameter, it can be considered a replacement for React's [`cache`](https://react.dev/reference/react/cache) function and can be used in any server-side code (such as in data loaders of SSR projects), not limited to server components.
|
|
@@ -96,7 +90,6 @@ const loader = async () => {
|
|
|
96
90
|
};
|
|
97
91
|
```
|
|
98
92
|
|
|
99
|
-
|
|
100
93
|
### With options Parameter
|
|
101
94
|
|
|
102
95
|
#### `maxAge` Parameter
|
|
@@ -113,12 +106,11 @@ const getDashboardStats = cache(
|
|
|
113
106
|
return await fetchComplexStatistics();
|
|
114
107
|
},
|
|
115
108
|
{
|
|
116
|
-
maxAge: CacheTime.MINUTE * 2,
|
|
117
|
-
}
|
|
109
|
+
maxAge: CacheTime.MINUTE * 2, // Calling this function within 2 minutes will return cached data
|
|
110
|
+
},
|
|
118
111
|
);
|
|
119
112
|
```
|
|
120
113
|
|
|
121
|
-
|
|
122
114
|
#### `revalidate` Parameter
|
|
123
115
|
|
|
124
116
|
The `revalidate` parameter sets a time window for revalidating the cache after it expires. It can be used together with the `maxAge` parameter, similar to HTTP Cache-Control's stale-while-revalidate mode.
|
|
@@ -139,11 +131,10 @@ const getDashboardStats = cache(
|
|
|
139
131
|
{
|
|
140
132
|
maxAge: CacheTime.MINUTE * 2,
|
|
141
133
|
revalidate: CacheTime.MINUTE * 1,
|
|
142
|
-
}
|
|
134
|
+
},
|
|
143
135
|
);
|
|
144
136
|
```
|
|
145
137
|
|
|
146
|
-
|
|
147
138
|
#### `tag` Parameter
|
|
148
139
|
|
|
149
140
|
The `tag` parameter identifies the cache with a tag, which can be a string or an array of strings.
|
|
@@ -158,7 +149,7 @@ const getDashboardStats = cache(
|
|
|
158
149
|
},
|
|
159
150
|
{
|
|
160
151
|
tag: 'dashboard',
|
|
161
|
-
}
|
|
152
|
+
},
|
|
162
153
|
);
|
|
163
154
|
|
|
164
155
|
const getComplexStatistics = cache(
|
|
@@ -167,13 +158,12 @@ const getComplexStatistics = cache(
|
|
|
167
158
|
},
|
|
168
159
|
{
|
|
169
160
|
tag: 'dashboard',
|
|
170
|
-
}
|
|
161
|
+
},
|
|
171
162
|
);
|
|
172
163
|
|
|
173
|
-
await revalidateTag('dashboard
|
|
164
|
+
await revalidateTag('dashboard'); // Invalidates the cache for both getDashboardStats and getComplexStatistics functions
|
|
174
165
|
```
|
|
175
166
|
|
|
176
|
-
|
|
177
167
|
#### `getKey` Parameter
|
|
178
168
|
|
|
179
169
|
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.
|
|
@@ -193,15 +183,15 @@ const getUser = cache(
|
|
|
193
183
|
maxAge: CacheTime.MINUTE * 5,
|
|
194
184
|
// Only use the first parameter (userId) as the cache key
|
|
195
185
|
getKey: (userId, options) => userId,
|
|
196
|
-
}
|
|
186
|
+
},
|
|
197
187
|
);
|
|
198
188
|
|
|
199
189
|
// The following two calls will share the cache because getKey only uses userId
|
|
200
|
-
await getUser(123, { language: '
|
|
201
|
-
await getUser(123, { language: '
|
|
190
|
+
await getUser(123, { language: 'zh' });
|
|
191
|
+
await getUser(123, { language: 'en' }); // Cache hit, won't request again
|
|
202
192
|
|
|
203
193
|
// Different userId will use different cache
|
|
204
|
-
await getUser(456, { language: '
|
|
194
|
+
await getUser(456, { language: 'zh' }); // Won't hit cache, will request again
|
|
205
195
|
```
|
|
206
196
|
|
|
207
197
|
You can also use Modern.js's `generateKey` function together with getKey to generate the cache key:
|
|
@@ -223,21 +213,18 @@ const getUser = cache(
|
|
|
223
213
|
{
|
|
224
214
|
maxAge: CacheTime.MINUTE * 5,
|
|
225
215
|
getKey: (userId, options) => generateKey(userId),
|
|
226
|
-
}
|
|
216
|
+
},
|
|
227
217
|
);
|
|
228
218
|
```
|
|
229
219
|
|
|
230
220
|
Additionally, `getKey` can also return a numeric type as a cache key:
|
|
231
221
|
|
|
232
222
|
```ts
|
|
233
|
-
const getUserById = cache(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
getKey: (id) => id,
|
|
239
|
-
}
|
|
240
|
-
);
|
|
223
|
+
const getUserById = cache(fetchUserDataById, {
|
|
224
|
+
maxAge: CacheTime.MINUTE * 5,
|
|
225
|
+
// Directly use the numeric ID as the cache key
|
|
226
|
+
getKey: id => id,
|
|
227
|
+
});
|
|
241
228
|
|
|
242
229
|
await getUserById(42); // Uses 42 as the cache key
|
|
243
230
|
```
|
|
@@ -255,6 +242,7 @@ Its return value **directly becomes** the final cache key, **overriding** the de
|
|
|
255
242
|
:::info
|
|
256
243
|
|
|
257
244
|
Generally, the cache will be invalidated in the following scenarios:
|
|
245
|
+
|
|
258
246
|
1. The function's input parameters change
|
|
259
247
|
2. The maxAge condition is no longer satisfied
|
|
260
248
|
3. The `revalidateTag` method has been called
|
|
@@ -270,16 +258,13 @@ import { cache } from '@modern-js/runtime/cache';
|
|
|
270
258
|
import { fetchUserData } from './api';
|
|
271
259
|
|
|
272
260
|
// Different function references, but share the same cache via customKey
|
|
273
|
-
const getUserA = cache(
|
|
274
|
-
|
|
275
|
-
{
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
},
|
|
281
|
-
}
|
|
282
|
-
);
|
|
261
|
+
const getUserA = cache(fetchUserData, {
|
|
262
|
+
maxAge: CacheTime.MINUTE * 5,
|
|
263
|
+
customKey: ({ params }) => {
|
|
264
|
+
// Return a stable string as the cache key
|
|
265
|
+
return `user-${params[0]}`;
|
|
266
|
+
},
|
|
267
|
+
});
|
|
283
268
|
|
|
284
269
|
// Even if the function reference changes,
|
|
285
270
|
// as long as customKey returns the same value, the cache will be hit
|
|
@@ -291,7 +276,7 @@ const getUserB = cache(
|
|
|
291
276
|
// Return the same key as getUserA
|
|
292
277
|
return `user-${params[0]}`;
|
|
293
278
|
},
|
|
294
|
-
}
|
|
279
|
+
},
|
|
295
280
|
);
|
|
296
281
|
|
|
297
282
|
// Now you can share cache across different function implementations
|
|
@@ -299,12 +284,9 @@ await getUserA(123); // Fetches data and caches with key "user-123"
|
|
|
299
284
|
await getUserB(123); // Cache hit, returns cached data
|
|
300
285
|
|
|
301
286
|
// You can utilize the generatedKey parameter to modify the default key
|
|
302
|
-
const getUserC = cache(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
customKey: ({ generatedKey }) => `prefix-${generatedKey}`,
|
|
306
|
-
}
|
|
307
|
-
);
|
|
287
|
+
const getUserC = cache(fetchUserData, {
|
|
288
|
+
customKey: ({ generatedKey }) => `prefix-${generatedKey}`,
|
|
289
|
+
});
|
|
308
290
|
|
|
309
291
|
// For predictable cache keys that can be managed externally
|
|
310
292
|
const getUserD = cache(
|
|
@@ -314,7 +296,7 @@ const getUserD = cache(
|
|
|
314
296
|
{
|
|
315
297
|
maxAge: CacheTime.MINUTE * 5,
|
|
316
298
|
customKey: ({ params }) => `app:user:${params[0]}`,
|
|
317
|
-
}
|
|
299
|
+
},
|
|
318
300
|
);
|
|
319
301
|
```
|
|
320
302
|
|
|
@@ -331,30 +313,31 @@ const stats = {
|
|
|
331
313
|
hits: 0,
|
|
332
314
|
misses: 0,
|
|
333
315
|
stales: 0,
|
|
334
|
-
hitRate: () => stats.hits / stats.total
|
|
316
|
+
hitRate: () => stats.hits / stats.total,
|
|
335
317
|
};
|
|
336
318
|
|
|
337
|
-
const getUser = cache(
|
|
338
|
-
|
|
339
|
-
{
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
} else if (status === 'stale') {
|
|
350
|
-
stats.stales++;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
console.log(`Cache ${status} for key: ${String(key)}`);
|
|
354
|
-
console.log(`Current hit rate: ${stats.hitRate() * 100}%`);
|
|
319
|
+
const getUser = cache(fetchUserData, {
|
|
320
|
+
maxAge: CacheTime.MINUTE * 5,
|
|
321
|
+
onCache({ status, key, params, result }) {
|
|
322
|
+
// status can be 'hit', 'miss', or 'stale'
|
|
323
|
+
stats.total++;
|
|
324
|
+
|
|
325
|
+
if (status === 'hit') {
|
|
326
|
+
stats.hits++;
|
|
327
|
+
} else if (status === 'miss') {
|
|
328
|
+
stats.misses++;
|
|
329
|
+
} else if (status === 'stale') {
|
|
330
|
+
stats.stales++;
|
|
355
331
|
}
|
|
356
|
-
|
|
357
|
-
|
|
332
|
+
|
|
333
|
+
console.log(
|
|
334
|
+
`Cache ${
|
|
335
|
+
status === 'hit' ? 'hit' : status === 'miss' ? 'miss' : 'stale'
|
|
336
|
+
} for key: ${String(key)}`,
|
|
337
|
+
);
|
|
338
|
+
console.log(`Current hit rate: ${stats.hitRate() * 100}%`);
|
|
339
|
+
},
|
|
340
|
+
});
|
|
358
341
|
|
|
359
342
|
// Usage example
|
|
360
343
|
await getUser(1); // Cache miss
|
|
@@ -375,6 +358,7 @@ The `onCache` callback receives an object with the following properties:
|
|
|
375
358
|
This callback is only invoked when the `options` parameter is provided. When using the cache function without options, the `onCache` callback is not called.
|
|
376
359
|
|
|
377
360
|
The `onCache` callback is useful for:
|
|
361
|
+
|
|
378
362
|
- Monitoring cache performance
|
|
379
363
|
- Calculating hit rates
|
|
380
364
|
- Logging cache operations
|
|
@@ -432,7 +416,7 @@ configureCache({
|
|
|
432
416
|
|
|
433
417
|
##### Using `customKey` to Ensure Cache Key Stability
|
|
434
418
|
|
|
435
|
-
:::
|
|
419
|
+
:::warning Important Recommendation
|
|
436
420
|
|
|
437
421
|
When using custom storage containers (such as Redis), **it's recommended to configure `customKey`** to ensure cache key stability. This ensures:
|
|
438
422
|
|
|
@@ -461,7 +445,7 @@ const getUser = cache(
|
|
|
461
445
|
maxAge: CacheTime.MINUTE * 5,
|
|
462
446
|
// Use stable identifiers related to the cached function as cache keys
|
|
463
447
|
customKey: () => `fetchUserData`,
|
|
464
|
-
}
|
|
448
|
+
},
|
|
465
449
|
);
|
|
466
450
|
```
|
|
467
451
|
|
|
@@ -481,7 +465,7 @@ class RedisContainer implements Container {
|
|
|
481
465
|
}
|
|
482
466
|
|
|
483
467
|
async get(key: string): Promise<string | null> {
|
|
484
|
-
const value = await this.
|
|
468
|
+
const value = await this.client.get(key);
|
|
485
469
|
return value ? JSON.parse(value) : null;
|
|
486
470
|
}
|
|
487
471
|
|
|
@@ -524,7 +508,7 @@ configureCache({
|
|
|
524
508
|
});
|
|
525
509
|
```
|
|
526
510
|
|
|
527
|
-
#####
|
|
511
|
+
##### Notes
|
|
528
512
|
|
|
529
513
|
1. **Serialization**: All cached data will be serialized to strings for storage. The container only needs to handle string get/set operations.
|
|
530
514
|
|
|
@@ -8,10 +8,6 @@ Modern.js provides out-of-the-box data fetching capabilities. Developers can use
|
|
|
8
8
|
|
|
9
9
|
## What is Data Loader
|
|
10
10
|
|
|
11
|
-
:::note
|
|
12
|
-
In Modern.js v1 projects, data was fetched using `useLoader`. This is no longer the recommended approach; we suggest migrating to Data Loader.
|
|
13
|
-
:::
|
|
14
|
-
|
|
15
11
|
Modern.js recommends managing routes using [conventional routing](/guides/basic-features/routes/routes). Each route component (`layout.ts`, `page.ts`, or `$.tsx`) can have a same-named `.data` file. These files can export a `loader` function, known as Data Loader, which executes before the corresponding route component renders to provide data for the component. Here is an example:
|
|
16
12
|
|
|
17
13
|
```bash
|
|
@@ -39,15 +35,18 @@ export const loader = async (): Promise<ProfileData> => {
|
|
|
39
35
|
```
|
|
40
36
|
|
|
41
37
|
:::warning Compatibility
|
|
38
|
+
|
|
42
39
|
- In previous versions, Data Loader was defined in a `.loader` file. In the current version, we recommend defining it in a `.data` file, while maintaining compatibility with `.loader` files.
|
|
43
40
|
- In `.loader` files, the Data Loader can be exported as default. In `.data` files, it should be named `loader` export.
|
|
41
|
+
|
|
44
42
|
```ts
|
|
45
|
-
|
|
46
|
-
export default () => {}
|
|
43
|
+
// xxx.loader.ts
|
|
44
|
+
export default () => {};
|
|
47
45
|
|
|
48
46
|
// xxx.data.ts
|
|
49
|
-
export const loader = () => {}
|
|
47
|
+
export const loader = () => {};
|
|
50
48
|
```
|
|
49
|
+
|
|
51
50
|
:::
|
|
52
51
|
|
|
53
52
|
In the route component, you can use the `useLoaderData` function to fetch data:
|
|
@@ -100,7 +99,7 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
|
|
100
99
|
`request` is an instance of [Fetch Request](https://developer.mozilla.org/en-US/docs/Web/API/Request). A common use case is to get query parameters from `request`:
|
|
101
100
|
|
|
102
101
|
```tsx
|
|
103
|
-
import { LoaderFunctionArgs } from '@modern-js/runtime/router;
|
|
102
|
+
import { LoaderFunctionArgs } from '@modern-js/runtime/router';
|
|
104
103
|
|
|
105
104
|
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
106
105
|
const url = new URL(request.url);
|
|
@@ -146,20 +145,22 @@ In SSR applications, the `loader` function runs only on the server, hence it is
|
|
|
146
145
|
|
|
147
146
|
:::note
|
|
148
147
|
Having the `loader` function run only on the server in SSR applications brings several benefits:
|
|
148
|
+
|
|
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
|
|
|
155
|
-
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. Modern.js provides a default polyfill for the `fetch` API, allowing it to be used on the server side. This means you can fetch data in a consistent manner whether in CSR or SSR:
|
|
156
156
|
|
|
157
157
|
```tsx
|
|
158
158
|
export async function loader() {
|
|
159
159
|
const res = await fetch('URL_ADDRESS');
|
|
160
|
+
const data = await res.json();
|
|
160
161
|
return {
|
|
161
|
-
message:
|
|
162
|
-
}
|
|
162
|
+
message: data.message,
|
|
163
|
+
};
|
|
163
164
|
}
|
|
164
165
|
```
|
|
165
166
|
|
|
@@ -204,14 +205,14 @@ In the following example, the page's status code will match this `response`, and
|
|
|
204
205
|
// routes/user/profile/page.data.ts
|
|
205
206
|
export async function loader() {
|
|
206
207
|
const user = await fetchUser();
|
|
207
|
-
if(!user){
|
|
208
|
+
if (!user) {
|
|
208
209
|
throw new Response('The user was not found', { status: 404 });
|
|
209
210
|
}
|
|
210
211
|
return user;
|
|
211
212
|
}
|
|
212
213
|
|
|
213
214
|
// routes/error.tsx
|
|
214
|
-
import { useRouteError } from '@modern-js/runtime/router;
|
|
215
|
+
import { useRouteError } from '@modern-js/runtime/router';
|
|
215
216
|
const ErrorBoundary = () => {
|
|
216
217
|
const error = useRouteError() as { data: string };
|
|
217
218
|
return <div className="error">{error.data}</div>;
|
|
@@ -240,7 +241,7 @@ export function UserLayout() {
|
|
|
240
241
|
}
|
|
241
242
|
```
|
|
242
243
|
|
|
243
|
-
`useRouteLoaderData` accepts a parameter `routeId`. In conventional routing, Modern.js automatically generates the `routeId`, which is the path of the corresponding component relative to `src/routes`.
|
|
244
|
+
`useRouteLoaderData` accepts a parameter `routeId`. In conventional routing, Modern.js automatically generates the `routeId`, which is the path of the corresponding component relative to `src/routes`. For example, in the example above, if a child component wants to get data returned by the loader in `routes/user/layout.tsx`, the `routeId` value is `user/layout`.
|
|
244
245
|
|
|
245
246
|
In a multi-entry scenario, the `routeId` value needs to include the corresponding entry name, which is typically the directory name if not explicitly specified. For example, with the following directory structure:
|
|
246
247
|
|