@modern-js/main-doc 2.68.1 → 2.68.2
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.
@@ -74,7 +74,7 @@ EdenX's `cache` function can be used in any frontend or server-side code.
|
|
74
74
|
|
75
75
|
## Detailed Usage
|
76
76
|
|
77
|
-
### Without
|
77
|
+
### Without options Parameter
|
78
78
|
|
79
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. EdenX will re-execute the `fn` function with each server request.
|
80
80
|
|
@@ -97,7 +97,7 @@ const loader = async () => {
|
|
97
97
|
```
|
98
98
|
|
99
99
|
|
100
|
-
### With
|
100
|
+
### With options Parameter
|
101
101
|
|
102
102
|
#### `maxAge` Parameter
|
103
103
|
|
@@ -166,13 +166,15 @@ const getComplexStatistics = cache(
|
|
166
166
|
}
|
167
167
|
);
|
168
168
|
|
169
|
-
revalidateTag('dashboard'); // Invalidates the cache for both getDashboardStats and getComplexStatistics functions
|
169
|
+
await revalidateTag('dashboard-stats'); // Invalidates the cache for both getDashboardStats and getComplexStatistics functions
|
170
170
|
```
|
171
171
|
|
172
172
|
|
173
173
|
#### `getKey` Parameter
|
174
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
|
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.
|
176
|
+
|
177
|
+
Its return value becomes part of the final cache key, but the key is still combined with a unique function identifier, making the cache **function-scoped**.
|
176
178
|
|
177
179
|
```ts
|
178
180
|
import { cache, CacheTime } from '@modern-js/runtime/cache';
|
@@ -238,7 +240,9 @@ await getUserById(42); // Uses 42 as the cache key
|
|
238
240
|
|
239
241
|
#### `customKey` parameter
|
240
242
|
|
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
|
243
|
+
The `customKey` parameter is used to **fully customize** the cache key. It is a function that receives an object with the following properties and returns a string as the cache key.
|
244
|
+
|
245
|
+
Its return value **directly becomes** the final cache key, **overriding** the default combination of a function identifier and parameter-based key. This allows you to create **globally unique** keys and share cache across different functions.
|
242
246
|
|
243
247
|
- `params`: Array of arguments passed to the cached function
|
244
248
|
- `fn`: Reference to the original function being cached
|
@@ -247,16 +251,15 @@ The `customKey` parameter is used to customize the cache key. It is a function t
|
|
247
251
|
:::info
|
248
252
|
|
249
253
|
Generally, the cache will be invalidated in the following scenarios:
|
250
|
-
1. The
|
251
|
-
2. The
|
252
|
-
3. The
|
253
|
-
4. The `revalidateTag` method has been called
|
254
|
+
1. The function's input parameters change
|
255
|
+
2. The maxAge condition is no longer satisfied
|
256
|
+
3. The `revalidateTag` method has been called
|
254
257
|
|
255
|
-
`customKey` can be used
|
258
|
+
By default, the framework generates a stable function ID based on the function's string representation and combines it with the generated parameter key. `customKey` can be used when you need complete control over the cache key generation, especially useful for sharing cache across different function instances. If you just need to customize how parameters are converted to cache keys, it is recommended to use `getKey`.
|
256
259
|
|
257
260
|
:::
|
258
261
|
|
259
|
-
This is very useful in some scenarios, such as when
|
262
|
+
This is very useful in some scenarios, such as when you want to share cache across different function instances or when you need predictable cache keys for external cache management.
|
260
263
|
|
261
264
|
```ts
|
262
265
|
import { cache } from '@modern-js/runtime/cache';
|
@@ -287,21 +290,26 @@ const getUserB = cache(
|
|
287
290
|
}
|
288
291
|
);
|
289
292
|
|
290
|
-
//
|
291
|
-
|
293
|
+
// Now you can share cache across different function implementations
|
294
|
+
await getUserA(123); // Fetches data and caches with key "user-123"
|
295
|
+
await getUserB(123); // Cache hit, returns cached data
|
296
|
+
|
297
|
+
// You can utilize the generatedKey parameter to modify the default key
|
292
298
|
const getUserC = cache(
|
293
299
|
fetchUserData,
|
294
300
|
{
|
295
|
-
|
296
|
-
customKey: () => USER_CACHE_KEY,
|
301
|
+
customKey: ({ generatedKey }) => `prefix-${generatedKey}`,
|
297
302
|
}
|
298
303
|
);
|
299
304
|
|
300
|
-
//
|
305
|
+
// For predictable cache keys that can be managed externally
|
301
306
|
const getUserD = cache(
|
302
|
-
|
307
|
+
async (userId: string) => {
|
308
|
+
return await fetchUserData(userId);
|
309
|
+
},
|
303
310
|
{
|
304
|
-
|
311
|
+
maxAge: CacheTime.MINUTE * 5,
|
312
|
+
customKey: ({ params }) => `app:user:${params[0]}`,
|
305
313
|
}
|
306
314
|
);
|
307
315
|
```
|
@@ -370,6 +378,8 @@ The `onCache` callback is useful for:
|
|
370
378
|
|
371
379
|
### Storage
|
372
380
|
|
381
|
+
#### Default Storage
|
382
|
+
|
373
383
|
Currently, both client and server caches are stored in memory.
|
374
384
|
The default storage limit for all cached functions is 1GB. When this limit is reached, the oldest cache is removed using an LRU algorithm.
|
375
385
|
|
@@ -386,3 +396,132 @@ configureCache({
|
|
386
396
|
maxSize: CacheSize.MB * 10, // 10MB
|
387
397
|
});
|
388
398
|
```
|
399
|
+
|
400
|
+
#### Custom Storage Container
|
401
|
+
|
402
|
+
In addition to the default memory storage, you can use custom storage containers such as Redis, file systems, databases, etc. This enables cache sharing across processes and servers.
|
403
|
+
|
404
|
+
##### Container Interface
|
405
|
+
|
406
|
+
Custom storage containers need to implement the `Container` interface:
|
407
|
+
|
408
|
+
```ts
|
409
|
+
interface Container {
|
410
|
+
get: (key: string) => Promise<string | undefined | null>;
|
411
|
+
set: (key: string, value: string, options?: { ttl?: number }) => Promise<any>;
|
412
|
+
has: (key: string) => Promise<boolean>;
|
413
|
+
delete: (key: string) => Promise<boolean>;
|
414
|
+
clear: () => Promise<void>;
|
415
|
+
}
|
416
|
+
```
|
417
|
+
|
418
|
+
##### Basic Usage
|
419
|
+
|
420
|
+
```ts
|
421
|
+
import { configureCache } from '@modern-js/runtime/cache';
|
422
|
+
|
423
|
+
// Use custom storage container
|
424
|
+
configureCache({
|
425
|
+
container: customContainer,
|
426
|
+
});
|
427
|
+
```
|
428
|
+
|
429
|
+
##### Using `customKey` to Ensure Cache Key Stability
|
430
|
+
|
431
|
+
:::note
|
432
|
+
|
433
|
+
When using custom storage containers (such as Redis), **it's recommended to configure `customKey`** to ensure cache key stability. This ensures:
|
434
|
+
|
435
|
+
1. **Cross-process sharing**: Different server instances can share the same cache
|
436
|
+
2. **Cache validity after application restart**: Cache remains valid after restarting the application
|
437
|
+
3. **Cache persistence after code deployment**: Cache for the same logic remains effective after code updates
|
438
|
+
|
439
|
+
:::
|
440
|
+
|
441
|
+
The default cache key generation mechanism is based on function references, which may not be stable enough in distributed environments. It's recommended to use `customKey` to provide stable cache keys:
|
442
|
+
|
443
|
+
```ts
|
444
|
+
import { cache, configureCache } from '@modern-js/runtime/cache';
|
445
|
+
|
446
|
+
// Configure Redis container
|
447
|
+
configureCache({
|
448
|
+
container: redisContainer,
|
449
|
+
});
|
450
|
+
|
451
|
+
// Recommended: Use customKey to ensure key stability
|
452
|
+
const getUser = cache(
|
453
|
+
async (userId: string) => {
|
454
|
+
return await fetchUserData(userId);
|
455
|
+
},
|
456
|
+
{
|
457
|
+
maxAge: CacheTime.MINUTE * 5,
|
458
|
+
// Use stable identifiers related to the cached function as cache keys
|
459
|
+
customKey: () => `fetchUserData`,
|
460
|
+
}
|
461
|
+
);
|
462
|
+
```
|
463
|
+
|
464
|
+
##### Redis Storage Example
|
465
|
+
|
466
|
+
Here's an example of using Redis as a storage backend:
|
467
|
+
|
468
|
+
```ts
|
469
|
+
import { Redis } from 'ioredis';
|
470
|
+
import { Container, configureCache } from '@modern-js/runtime/cache';
|
471
|
+
|
472
|
+
class RedisContainer implements Container {
|
473
|
+
private client: Redis;
|
474
|
+
|
475
|
+
constructor(client: Redis) {
|
476
|
+
this.client = client;
|
477
|
+
}
|
478
|
+
|
479
|
+
async get(key: string): Promise<string | null> {
|
480
|
+
const value = await this.redis.get(key);
|
481
|
+
return value ? JSON.parse(value) : null;
|
482
|
+
}
|
483
|
+
|
484
|
+
async set(
|
485
|
+
key: string,
|
486
|
+
value: string,
|
487
|
+
options?: { ttl?: number },
|
488
|
+
): Promise<'OK'> {
|
489
|
+
if (options?.ttl) {
|
490
|
+
return this.client.set(key, JSON.stringify(value), 'EX', options.ttl);
|
491
|
+
}
|
492
|
+
return this.client.set(key, JSON.stringify(value));
|
493
|
+
}
|
494
|
+
|
495
|
+
async has(key: string): Promise<boolean> {
|
496
|
+
const result = await this.client.exists(key);
|
497
|
+
return result === 1;
|
498
|
+
}
|
499
|
+
|
500
|
+
async delete(key: string): Promise<boolean> {
|
501
|
+
const result = await this.client.del(key);
|
502
|
+
return result > 0;
|
503
|
+
}
|
504
|
+
|
505
|
+
async clear(): Promise<void> {
|
506
|
+
// Be cautious with this in production. It will clear the entire Redis database.
|
507
|
+
// A more robust implementation might use a key prefix and delete keys matching that prefix.
|
508
|
+
await this.client.flushdb();
|
509
|
+
}
|
510
|
+
}
|
511
|
+
|
512
|
+
// Configure Redis storage
|
513
|
+
const redisClient = new Redis({
|
514
|
+
host: 'localhost',
|
515
|
+
port: 6379,
|
516
|
+
});
|
517
|
+
|
518
|
+
configureCache({
|
519
|
+
container: new RedisContainer(redisClient),
|
520
|
+
});
|
521
|
+
```
|
522
|
+
|
523
|
+
##### Important Notes
|
524
|
+
|
525
|
+
1. **Serialization**: All cached data will be serialized to strings for storage. The container only needs to handle string get/set operations.
|
526
|
+
|
527
|
+
2. **TTL Support**: If your storage backend supports TTL (Time To Live), you can use the `options.ttl` parameter in the `set` method (in seconds).
|
@@ -159,12 +159,14 @@ const getComplexStatistics = cache(
|
|
159
159
|
}
|
160
160
|
);
|
161
161
|
|
162
|
-
revalidateTag('dashboard'); // 会使 getDashboardStats 函数和 getComplexStatistics 函数的缓存都失效
|
162
|
+
await revalidateTag('dashboard-stats'); // 会使 getDashboardStats 函数和 getComplexStatistics 函数的缓存都失效
|
163
163
|
```
|
164
164
|
|
165
165
|
#### `getKey` 参数
|
166
166
|
|
167
|
-
`getKey`
|
167
|
+
`getKey` 参数用于**简化**缓存键的生成方式,例如你可能只需要依赖函数参数的一部分来区分缓存。它是一个函数,接收与原始函数相同的参数,返回一个字符串。
|
168
|
+
|
169
|
+
它的返回值会作为参数部分参与最终缓存键的生成,但最终的键仍然会包含函数的唯一标识,因此缓存是**函数级别**的。
|
168
170
|
|
169
171
|
```ts
|
170
172
|
import { cache, CacheTime } from '@modern-js/runtime/cache';
|
@@ -216,7 +218,9 @@ const getUser = cache(
|
|
216
218
|
|
217
219
|
#### `customKey` 参数
|
218
220
|
|
219
|
-
`customKey`
|
221
|
+
`customKey` 参数用于**完全定制**缓存的键,它是一个函数,接收一个包含以下属性的对象,返回值必须是字符串类型。
|
222
|
+
|
223
|
+
它的返回值将**直接**作为最终的缓存键,**覆盖**了默认的函数标识和参数组合。这允许你创建**全局唯一**的缓存键,从而实现跨函数共享缓存。
|
220
224
|
|
221
225
|
- `params`:调用缓存函数时传入的参数数组
|
222
226
|
- `fn`:原始被缓存的函数引用
|
@@ -225,16 +229,15 @@ const getUser = cache(
|
|
225
229
|
:::info
|
226
230
|
|
227
231
|
一般在以下场景,缓存会失效:
|
228
|
-
1.
|
229
|
-
2.
|
230
|
-
3.
|
231
|
-
4. 调用了 `revalidateTag`
|
232
|
+
1. 函数的入参发生变化
|
233
|
+
2. 不满足 maxAge 条件
|
234
|
+
3. 调用了 `revalidateTag`
|
232
235
|
|
233
|
-
|
236
|
+
默认情况下,框架会基于函数的字符串表示生成稳定的函数 ID,并与生成的参数键组合。`customKey` 可以用于完全控制缓存键的生成,特别适用于在不同函数实例间共享缓存。如果只是需要自定义参数如何转换为缓存键,推荐使用 `getKey`。
|
234
237
|
|
235
238
|
:::
|
236
239
|
|
237
|
-
|
240
|
+
这在某些场景下非常有用,比如当你希望在不同函数实例间共享缓存,或者需要可预测的缓存键用于外部缓存管理时。
|
238
241
|
|
239
242
|
```ts
|
240
243
|
import { cache } from '@modern-js/runtime/cache';
|
@@ -264,26 +267,26 @@ const getUserB = cache(
|
|
264
267
|
}
|
265
268
|
);
|
266
269
|
|
267
|
-
//
|
268
|
-
//
|
269
|
-
|
270
|
-
const dataB = await getUserB(1); // 这里会命中缓存,不会再次发起请求
|
270
|
+
// 现在你可以在不同的函数实现间共享缓存
|
271
|
+
await getUserA(123); // 获取数据并使用键 "user-123" 缓存
|
272
|
+
await getUserB(123); // 缓存命中,返回缓存的数据
|
271
273
|
|
272
|
-
//
|
273
|
-
const USER_CACHE_KEY = Symbol('user-cache');
|
274
|
+
// 可以利用 generatedKey 参数在默认键的基础上进行修改
|
274
275
|
const getUserC = cache(
|
275
276
|
fetchUserData,
|
276
277
|
{
|
277
|
-
|
278
|
-
customKey: () => USER_CACHE_KEY,
|
278
|
+
customKey: ({ generatedKey }) => `prefix-${generatedKey}`,
|
279
279
|
}
|
280
280
|
);
|
281
281
|
|
282
|
-
//
|
282
|
+
// 用于可预测的缓存键,便于外部管理
|
283
283
|
const getUserD = cache(
|
284
|
-
|
284
|
+
async (userId: string) => {
|
285
|
+
return await fetchUserData(userId);
|
286
|
+
},
|
285
287
|
{
|
286
|
-
|
288
|
+
maxAge: CacheTime.MINUTE * 5,
|
289
|
+
customKey: ({ params }) => `app:user:${params[0]}`,
|
287
290
|
}
|
288
291
|
);
|
289
292
|
```
|
@@ -352,6 +355,8 @@ await getUser(2); // 缓存未命中
|
|
352
355
|
|
353
356
|
### 存储
|
354
357
|
|
358
|
+
#### 默认存储
|
359
|
+
|
355
360
|
目前不管是客户端还是服务端,缓存都存储在内存中,默认情况下所有缓存函数共享的存储上限是 1GB,当达到存储上限后,使用 LRU 算法移除旧的缓存。
|
356
361
|
|
357
362
|
:::info
|
@@ -368,3 +373,130 @@ configureCache({
|
|
368
373
|
});
|
369
374
|
```
|
370
375
|
|
376
|
+
#### 自定义存储器
|
377
|
+
|
378
|
+
除了默认的内存存储,你还可以使用自定义的存储容器,例如 Redis、文件系统、数据库等。这样可以实现跨进程、跨服务器的缓存共享。
|
379
|
+
|
380
|
+
##### Container 接口
|
381
|
+
|
382
|
+
自定义存储容器需要实现 `Container` 接口:
|
383
|
+
|
384
|
+
```ts
|
385
|
+
interface Container {
|
386
|
+
get: (key: string) => Promise<string | undefined | null>;
|
387
|
+
set: (key: string, value: string, options?: { ttl?: number }) => Promise<any>;
|
388
|
+
has: (key: string) => Promise<boolean>;
|
389
|
+
delete: (key: string) => Promise<boolean>;
|
390
|
+
clear: () => Promise<void>;
|
391
|
+
}
|
392
|
+
```
|
393
|
+
|
394
|
+
##### 基本使用
|
395
|
+
|
396
|
+
```ts
|
397
|
+
import { configureCache } from '@modern-js/runtime/cache';
|
398
|
+
|
399
|
+
// 使用自定义存储容器
|
400
|
+
configureCache({
|
401
|
+
container: customContainer,
|
402
|
+
});
|
403
|
+
```
|
404
|
+
|
405
|
+
##### 使用 `customKey` 确保缓存键稳定性
|
406
|
+
|
407
|
+
:::warning 重要建议
|
408
|
+
当使用自定义存储容器(如 Redis)时,**建议配置 `customKey`** 来确保缓存键的稳定性。这样可以确保:
|
409
|
+
|
410
|
+
1. **跨进程共享**:不同服务器实例能够共享相同的缓存
|
411
|
+
2. **应用重启后缓存有效**:重启应用后仍能命中之前的缓存
|
412
|
+
3. **代码部署后缓存保持**:代码更新后相同逻辑的缓存仍然有效
|
413
|
+
:::
|
414
|
+
|
415
|
+
默认的缓存键生成机制基于函数引用,在分布式环境中可能不够稳定。建议使用 `customKey` 提供稳定的缓存键:
|
416
|
+
|
417
|
+
```ts
|
418
|
+
import { cache, configureCache } from '@modern-js/runtime/cache';
|
419
|
+
|
420
|
+
// 配置 Redis 容器
|
421
|
+
configureCache({
|
422
|
+
container: redisContainer,
|
423
|
+
});
|
424
|
+
|
425
|
+
// 推荐:使用 customKey 确保键的稳定性
|
426
|
+
const getUser = cache(
|
427
|
+
async (userId: string) => {
|
428
|
+
return await fetchUserData(userId);
|
429
|
+
},
|
430
|
+
{
|
431
|
+
maxAge: CacheTime.MINUTE * 5,
|
432
|
+
// 使用被缓存函数相关的稳定标识符作为缓存键
|
433
|
+
customKey: () => `fetchUserData`,
|
434
|
+
}
|
435
|
+
);
|
436
|
+
```
|
437
|
+
|
438
|
+
##### Redis 存储示例
|
439
|
+
|
440
|
+
以下是一个使用 Redis 作为存储后端的示例:
|
441
|
+
|
442
|
+
```ts
|
443
|
+
import { Redis } from 'ioredis';
|
444
|
+
import { Container, configureCache } from '@modern-js/runtime/cache';
|
445
|
+
|
446
|
+
class RedisContainer implements Container {
|
447
|
+
private client: Redis;
|
448
|
+
|
449
|
+
constructor(client: Redis) {
|
450
|
+
this.client = client;
|
451
|
+
}
|
452
|
+
|
453
|
+
async get(key: string): Promise<string | null> {
|
454
|
+
const value = await this.redis.get(key);
|
455
|
+
return value ? JSON.parse(value) : null;
|
456
|
+
}
|
457
|
+
|
458
|
+
async set(
|
459
|
+
key: string,
|
460
|
+
value: string,
|
461
|
+
options?: { ttl?: number },
|
462
|
+
): Promise<'OK'> {
|
463
|
+
if (options?.ttl) {
|
464
|
+
return this.client.set(key, JSON.stringify(value), 'EX', options.ttl);
|
465
|
+
}
|
466
|
+
return this.client.set(key, JSON.stringify(value));
|
467
|
+
}
|
468
|
+
|
469
|
+
async has(key: string): Promise<boolean> {
|
470
|
+
const result = await this.client.exists(key);
|
471
|
+
return result === 1;
|
472
|
+
}
|
473
|
+
|
474
|
+
async delete(key: string): Promise<boolean> {
|
475
|
+
const result = await this.client.del(key);
|
476
|
+
return result > 0;
|
477
|
+
}
|
478
|
+
|
479
|
+
async clear(): Promise<void> {
|
480
|
+
// 注意:在生产环境中要谨慎使用,这会清空整个 Redis 数据库
|
481
|
+
// 更好的实现方式是使用键前缀,然后删除匹配该前缀的所有键
|
482
|
+
await this.client.flushdb();
|
483
|
+
}
|
484
|
+
}
|
485
|
+
|
486
|
+
// 配置 Redis 存储
|
487
|
+
const redisClient = new Redis({
|
488
|
+
host: 'localhost',
|
489
|
+
port: 6379,
|
490
|
+
});
|
491
|
+
|
492
|
+
configureCache({
|
493
|
+
container: new RedisContainer(redisClient),
|
494
|
+
});
|
495
|
+
```
|
496
|
+
|
497
|
+
##### 注意事项
|
498
|
+
|
499
|
+
1. **序列化**:所有的缓存数据都会被序列化为字符串存储,容器只需要处理字符串的存取操作。
|
500
|
+
|
501
|
+
2. **TTL 支持**:如果你的存储后端支持 TTL(生存时间),可以在 `set` 方法中使用 `options.ttl` 参数(单位为秒)。
|
502
|
+
|
package/package.json
CHANGED
@@ -15,14 +15,14 @@
|
|
15
15
|
"modern",
|
16
16
|
"modern.js"
|
17
17
|
],
|
18
|
-
"version": "2.68.
|
18
|
+
"version": "2.68.2",
|
19
19
|
"publishConfig": {
|
20
20
|
"registry": "https://registry.npmjs.org/",
|
21
21
|
"access": "public"
|
22
22
|
},
|
23
23
|
"dependencies": {
|
24
24
|
"mermaid": "^11.4.1",
|
25
|
-
"@modern-js/sandpack-react": "2.68.
|
25
|
+
"@modern-js/sandpack-react": "2.68.2"
|
26
26
|
},
|
27
27
|
"devDependencies": {
|
28
28
|
"@rsbuild/plugin-sass": "1.3.3",
|