chanjs 2.6.4 → 2.6.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/App.js +1 -1
- package/base/Container.js +6 -3
- package/base/Controller.js +2 -1
- package/base/Service.js +20 -1
- package/common/index.js +0 -2
- package/doc/Aop.md +269 -0
- package/doc/Cache.md +160 -0
- package/doc/Common.md +182 -0
- package/doc/Controller.md +152 -0
- package/doc/Email.md +114 -0
- package/doc/Event.md +232 -0
- package/doc/Help.md +789 -0
- package/doc/Service.md +566 -0
- package/helper/cache.js +1 -0
- package/helper/index.js +71 -22
- package/index.js +0 -1
- package/middleware/waf.js +5 -6
- package/package.json +1 -1
- package//345/275/222/346/241/243.zip +0 -0
- package/utils/index.js +0 -4
- /package/{utils → helper}/checker.js +0 -0
- /package/{utils → helper}/keywords.js +0 -0
- /package/{utils → helper}/rate-limit.js +0 -0
- /package/{utils → helper}/response.js +0 -0
- /package/{utils → helper}/xss-filter.js +0 -0
package/App.js
CHANGED
|
@@ -9,7 +9,7 @@ import DatabaseManager from "./base/Database.js";
|
|
|
9
9
|
import { Paths } from "./config/index.js";
|
|
10
10
|
import { loadConfig, loaderSort, loadController } from "./helper/index.js";
|
|
11
11
|
import { Cors, setBody, setCookie, setFavicon, setHeader, setStatic, setTemplate, waf, log } from "./middleware/index.js";
|
|
12
|
-
import { notFoundResponse, parseDatabaseError, errorResponse } from "./
|
|
12
|
+
import { notFoundResponse, parseDatabaseError, errorResponse } from "./helper/index.js";
|
|
13
13
|
import { importFile } from "./global/import.js";
|
|
14
14
|
|
|
15
15
|
import "./global/index.js";
|
package/base/Container.js
CHANGED
|
@@ -5,10 +5,11 @@ import { Paths } from '../config/paths.js';
|
|
|
5
5
|
export class Container {
|
|
6
6
|
constructor(type = 'service') {
|
|
7
7
|
this.map = new Map();
|
|
8
|
+
// 组件类型,例如:'service','controller'
|
|
8
9
|
this.type = type;
|
|
10
|
+
// modules 目录路径
|
|
9
11
|
this.baseDir = Paths.modulesPath;
|
|
10
|
-
|
|
11
|
-
// 只添加 config 属性,db 由 Service 自己管理
|
|
12
|
+
// 只添加 config 属性,全局默认的配置
|
|
12
13
|
this.config = Chan.config;
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -20,12 +21,13 @@ export class Container {
|
|
|
20
21
|
*/
|
|
21
22
|
async get(moduleName, fileName) {
|
|
22
23
|
const key = `${moduleName}.${fileName}`;
|
|
23
|
-
|
|
24
|
+
// 如果组件实例已存在,直接返回
|
|
24
25
|
if (this.map.has(key)) {
|
|
25
26
|
return this.map.get(key);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
try {
|
|
30
|
+
// 没有,加载组件实例
|
|
29
31
|
await this.loadFile(moduleName, fileName);
|
|
30
32
|
const obj = this.map.get(key);
|
|
31
33
|
if (!obj) {
|
|
@@ -34,6 +36,7 @@ export class Container {
|
|
|
34
36
|
return obj;
|
|
35
37
|
} catch (err) {
|
|
36
38
|
console.error(`获取模块 ${moduleName} 下文件 ${fileName}.js 对应的组件失败:${err.message}`);
|
|
39
|
+
return null;
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
|
package/base/Controller.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { success, fail } from "../
|
|
1
|
+
import { success, fail } from "../helper/response.js";
|
|
2
2
|
import Container from "./Container.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,6 +7,7 @@ import Container from "./Container.js";
|
|
|
7
7
|
*/
|
|
8
8
|
export default class Controller extends Container {
|
|
9
9
|
constructor() {
|
|
10
|
+
// 调用父类构造函数,指定组件类型为 'controller'
|
|
10
11
|
super('controller');
|
|
11
12
|
}
|
|
12
13
|
|
package/base/Service.js
CHANGED
|
@@ -25,7 +25,26 @@ class Service extends Container {
|
|
|
25
25
|
this.tableName = tableName;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
this._dateFields = [
|
|
28
|
+
this._dateFields = [
|
|
29
|
+
'created_at',
|
|
30
|
+
'updated_at',
|
|
31
|
+
'deleted_at',
|
|
32
|
+
'publish_time',
|
|
33
|
+
'start_time',
|
|
34
|
+
'end_time',
|
|
35
|
+
'login_time',
|
|
36
|
+
'created_date',
|
|
37
|
+
'updated_date',
|
|
38
|
+
'createdAt',
|
|
39
|
+
'updatedAt',
|
|
40
|
+
'deletedAt',
|
|
41
|
+
'publishTime',
|
|
42
|
+
'startTime',
|
|
43
|
+
'endTime',
|
|
44
|
+
'loginTime',
|
|
45
|
+
'createdDate',
|
|
46
|
+
'updatedDate'
|
|
47
|
+
];
|
|
29
48
|
|
|
30
49
|
// 自动注册事件监听器(如果子类定义了 on 方法)
|
|
31
50
|
if (typeof this.on === 'function') {
|
package/common/index.js
CHANGED
|
@@ -3,5 +3,3 @@ export { getChildrenId } from "./category.js";
|
|
|
3
3
|
export { CODE } from "./code.js";
|
|
4
4
|
export { sendMail, genRegEmailHtml, genResetPasswordEmail } from "./email.js";
|
|
5
5
|
export { pages, getHtmlFilesSync } from "./pages.js";
|
|
6
|
-
export { createSmsClient } from "./sms.js";
|
|
7
|
-
export { filterBody, pc, filterImgFromStr } from "./utils.js";
|
package/doc/Aop.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Aop 轻量级切面类使用文档
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
`Aop` 是一个轻量级的 JavaScript 切面编程(AOP)工具类,支持为对象方法注册并执行 `before`(前置)、`after`(后置)、`error`(异常)三种类型的切面函数,适用于日志记录、参数校验、异常处理等横切关注点场景。
|
|
5
|
+
|
|
6
|
+
## 特性
|
|
7
|
+
- 支持链式调用注册切面函数
|
|
8
|
+
- 内置切面类型校验,仅支持 `before`/`after`/`error` 三种常用类型
|
|
9
|
+
- 支持切面规则启用/禁用,灵活控制切面执行
|
|
10
|
+
- 保留原方法上下文,保证方法执行正确性
|
|
11
|
+
- 完善的错误处理,切面执行异常不阻断主流程
|
|
12
|
+
|
|
13
|
+
## 安装与引入
|
|
14
|
+
### 方式1:ESModule 引入
|
|
15
|
+
```javascript
|
|
16
|
+
import { Aop, aop } from './aop.js';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 核心 API
|
|
20
|
+
|
|
21
|
+
### 1. 切面注册(set)
|
|
22
|
+
注册一个切面函数,返回当前实例支持链式调用。
|
|
23
|
+
|
|
24
|
+
#### 语法
|
|
25
|
+
```javascript
|
|
26
|
+
aop.set(name, fn)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
#### 参数
|
|
30
|
+
| 参数 | 类型 | 说明 |
|
|
31
|
+
|------|------|------|
|
|
32
|
+
| name | string | 切面名称(非空字符串) |
|
|
33
|
+
| fn | Function | 切面执行函数,入参为切面上下文对象 |
|
|
34
|
+
|
|
35
|
+
#### 示例
|
|
36
|
+
```javascript
|
|
37
|
+
// 注册日志切面
|
|
38
|
+
aop.set('logger', async (params) => {
|
|
39
|
+
console.log(`[${params.methodName}] 执行时间:${new Date().toISOString()}`);
|
|
40
|
+
console.log(`参数:`, params.args);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 注册参数校验切面(链式调用)
|
|
44
|
+
aop.set('paramCheck', async (params) => {
|
|
45
|
+
const [id] = params.args;
|
|
46
|
+
if (!id || typeof id !== 'number') {
|
|
47
|
+
throw new Error(`[${params.methodName}] 参数id必须是数字`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. 获取切面(get)
|
|
53
|
+
获取已注册的切面函数。
|
|
54
|
+
|
|
55
|
+
#### 语法
|
|
56
|
+
```javascript
|
|
57
|
+
aop.get(name)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### 参数
|
|
61
|
+
| 参数 | 类型 | 说明 |
|
|
62
|
+
|------|------|------|
|
|
63
|
+
| name | string | 切面名称 |
|
|
64
|
+
|
|
65
|
+
#### 返回值
|
|
66
|
+
| 类型 | 说明 |
|
|
67
|
+
|------|------|
|
|
68
|
+
| Function \| null | 存在则返回切面函数,否则返回 null |
|
|
69
|
+
|
|
70
|
+
#### 示例
|
|
71
|
+
```javascript
|
|
72
|
+
const logger = aop.get('logger');
|
|
73
|
+
if (logger) {
|
|
74
|
+
console.log('日志切面已注册');
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 3. 移除切面(remove)
|
|
79
|
+
移除指定名称的切面函数。
|
|
80
|
+
|
|
81
|
+
#### 语法
|
|
82
|
+
```javascript
|
|
83
|
+
aop.remove(name)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### 参数
|
|
87
|
+
| 参数 | 类型 | 说明 |
|
|
88
|
+
|------|------|------|
|
|
89
|
+
| name | string | 切面名称 |
|
|
90
|
+
|
|
91
|
+
#### 示例
|
|
92
|
+
```javascript
|
|
93
|
+
// 移除日志切面
|
|
94
|
+
aop.remove('logger');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 4. 清空所有切面(clear)
|
|
98
|
+
清空已注册的所有切面函数。
|
|
99
|
+
|
|
100
|
+
#### 语法
|
|
101
|
+
```javascript
|
|
102
|
+
aop.clear()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### 示例
|
|
106
|
+
```javascript
|
|
107
|
+
// 清空所有切面
|
|
108
|
+
aop.clear();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 5. 绑定切面到方法(wrap)
|
|
112
|
+
核心方法,为目标对象的指定方法绑定切面规则。
|
|
113
|
+
|
|
114
|
+
#### 语法
|
|
115
|
+
```javascript
|
|
116
|
+
aop.wrap(instance, config)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### 参数
|
|
120
|
+
| 参数 | 类型 | 说明 |
|
|
121
|
+
|------|------|------|
|
|
122
|
+
| instance | object | 目标实例(非空对象) |
|
|
123
|
+
| config | object | 切面配置,格式:`{ 方法名: [{ type: 切面类型, enabled: 是否启用, 切面名称: 配置 }] }` |
|
|
124
|
+
|
|
125
|
+
#### 配置规则说明
|
|
126
|
+
| 字段 | 类型 | 说明 | 必填 | 默认值 |
|
|
127
|
+
|------|------|------|------|--------|
|
|
128
|
+
| type | string | 切面类型(before/after/error) | 是 | - |
|
|
129
|
+
| enabled | boolean | 是否启用该规则 | 否 | true |
|
|
130
|
+
| [切面名称] | any | 自定义配置,会透传给切面函数 | 否 | {} |
|
|
131
|
+
|
|
132
|
+
#### 返回值
|
|
133
|
+
| 类型 | 说明 |
|
|
134
|
+
|------|------|
|
|
135
|
+
| object | 绑定后的实例对象 |
|
|
136
|
+
|
|
137
|
+
## 完整使用示例
|
|
138
|
+
|
|
139
|
+
### 步骤1:定义目标类
|
|
140
|
+
```javascript
|
|
141
|
+
// 示例控制器类
|
|
142
|
+
class UserController {
|
|
143
|
+
async getUser(id) {
|
|
144
|
+
console.log(`获取用户信息,ID:${id}`);
|
|
145
|
+
if (id === 0) {
|
|
146
|
+
throw new Error('用户ID不能为0');
|
|
147
|
+
}
|
|
148
|
+
return { id, name: '张三', age: 20 };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 步骤2:注册切面函数
|
|
154
|
+
```javascript
|
|
155
|
+
import { aop } from './aop.js';
|
|
156
|
+
|
|
157
|
+
// 1. 注册参数校验切面
|
|
158
|
+
aop.set('paramCheck', async (params) => {
|
|
159
|
+
const [id] = params.args;
|
|
160
|
+
console.log(`[paramCheck] 校验参数:id = ${id}`);
|
|
161
|
+
if (typeof id !== 'number') {
|
|
162
|
+
throw new Error(`参数id必须是数字,当前值:${id}`);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// 2. 注册日志切面
|
|
167
|
+
aop.set('logger', async (params) => {
|
|
168
|
+
const { methodName, args, result, error } = params;
|
|
169
|
+
if (params.type === 'before') {
|
|
170
|
+
console.log(`[logger] 方法${methodName}开始执行,参数:`, args);
|
|
171
|
+
} else if (params.type === 'after') {
|
|
172
|
+
console.log(`[logger] 方法${methodName}执行完成,结果:`, result);
|
|
173
|
+
} else if (params.type === 'error') {
|
|
174
|
+
console.log(`[logger] 方法${methodName}执行异常:`, error.message);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// 3. 注册异常处理切面
|
|
179
|
+
aop.set('errorHandler', async (params) => {
|
|
180
|
+
console.log(`[errorHandler] 捕获异常:${params.error.message},执行兜底逻辑`);
|
|
181
|
+
// 可在这里添加异常上报、数据清理等逻辑
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 步骤3:绑定切面到方法并使用
|
|
186
|
+
```javascript
|
|
187
|
+
// 创建实例
|
|
188
|
+
const userController = new UserController();
|
|
189
|
+
|
|
190
|
+
// 绑定切面规则
|
|
191
|
+
aop.wrap(userController, {
|
|
192
|
+
getUser: [
|
|
193
|
+
// 前置切面:参数校验 + 日志
|
|
194
|
+
{ type: 'before', enabled: true, paramCheck: true, logger: true },
|
|
195
|
+
// 后置切面:日志
|
|
196
|
+
{ type: 'after', enabled: true, logger: true },
|
|
197
|
+
// 异常切面:日志 + 异常处理
|
|
198
|
+
{ type: 'error', enabled: true, logger: true, errorHandler: true }
|
|
199
|
+
]
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// 测试正常执行
|
|
203
|
+
async function testNormal() {
|
|
204
|
+
try {
|
|
205
|
+
const result = await userController.getUser(1);
|
|
206
|
+
console.log('最终结果:', result);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
console.log('测试异常:', e.message);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 测试异常执行
|
|
213
|
+
async function testError() {
|
|
214
|
+
try {
|
|
215
|
+
const result = await userController.getUser(0);
|
|
216
|
+
console.log('最终结果:', result);
|
|
217
|
+
} catch (e) {
|
|
218
|
+
console.log('测试异常:', e.message);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 执行测试
|
|
223
|
+
testNormal();
|
|
224
|
+
// testError();
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 执行结果(testNormal)
|
|
228
|
+
```
|
|
229
|
+
[paramCheck] 校验参数:id = 1
|
|
230
|
+
[logger] 方法getUser开始执行,参数: [1]
|
|
231
|
+
获取用户信息,ID:1
|
|
232
|
+
[logger] 方法getUser执行完成,结果: { id: 1, name: '张三', age: 20 }
|
|
233
|
+
最终结果: { id: 1, name: '张三', age: 20 }
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 执行结果(testError)
|
|
237
|
+
```
|
|
238
|
+
[paramCheck] 校验参数:id = 0
|
|
239
|
+
[logger] 方法getUser开始执行,参数: [0]
|
|
240
|
+
获取用户信息,ID:0
|
|
241
|
+
[logger] 方法getUser执行异常: 用户ID不能为0
|
|
242
|
+
[errorHandler] 捕获异常:用户ID不能为0,执行兜底逻辑
|
|
243
|
+
测试异常: 用户ID不能为0
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 切面函数上下文参数说明
|
|
247
|
+
切面函数的入参是一个上下文对象,包含以下字段:
|
|
248
|
+
|
|
249
|
+
| 字段 | 类型 | 说明 | 可用切面类型 |
|
|
250
|
+
|------|------|------|--------------|
|
|
251
|
+
| ctx | object | 原方法的执行上下文(实例对象) | all |
|
|
252
|
+
| methodName | string | 被包装的方法名称 | all |
|
|
253
|
+
| args | Array | 原方法的入参数组(浅拷贝) | all |
|
|
254
|
+
| originalMethod | Function | 原始未包装的方法 | all |
|
|
255
|
+
| result | any | 原方法执行结果 | after |
|
|
256
|
+
| error | Error | 原方法抛出的异常 | error |
|
|
257
|
+
| params | object | 切面规则中配置的自定义参数 | all |
|
|
258
|
+
|
|
259
|
+
## 注意事项
|
|
260
|
+
1. **异步支持**:切面函数和被包装的方法均支持异步(async/await),内部会自动处理异步执行顺序
|
|
261
|
+
2. **上下文保留**:原方法的 `this` 指向会被正确保留,无需额外处理
|
|
262
|
+
3. **异常处理**:`error` 类型切面执行完成后,异常会被重新抛出,不会阻断原有异常流程
|
|
263
|
+
4. **规则过滤**:`enabled: false` 的规则会被自动过滤,不执行对应的切面
|
|
264
|
+
5. **切面名称冲突**:不要使用 `type`/`enabled` 作为切面名称(内置关键字)
|
|
265
|
+
|
|
266
|
+
### 总结
|
|
267
|
+
1. `Aop` 类核心提供切面注册(`set`)和方法包装(`wrap`)能力,仅支持 `before`/`after`/`error` 三种切面类型。
|
|
268
|
+
2. 切面函数接收包含上下文、参数、结果/异常的完整入参,可灵活实现各类横切逻辑。
|
|
269
|
+
3. 通过配置规则的 `enabled` 字段可动态控制切面是否生效,异常切面执行后会重新抛出异常,保证原有错误流程不受影响。
|
package/doc/Cache.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Cache 工具类文档
|
|
2
|
+
## 概述
|
|
3
|
+
`Cache` 是一个基于内存实现的 LRU(最近最少使用)缓存类,支持 TTL(生存时间)过期策略和自动过期清理机制。该类通过两个 `Map` 分别存储缓存数据和访问顺序,既保证了缓存的快速存取,又能实现 LRU 淘汰策略,适用于需要临时缓存数据且对内存使用有控制需求的场景。
|
|
4
|
+
|
|
5
|
+
## 安装与引入
|
|
6
|
+
从 `chanjs/helper` 模块中引入 `Cache` 类和默认实例:
|
|
7
|
+
```javascript
|
|
8
|
+
import { Cache } from 'chanjs/helper';
|
|
9
|
+
// 或引入预创建的默认缓存实例
|
|
10
|
+
import { cache } from 'chanjs/helper';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 类构造器
|
|
14
|
+
### `constructor(options = {})`
|
|
15
|
+
创建缓存实例,初始化最大容量和默认过期时间。
|
|
16
|
+
|
|
17
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
18
|
+
|------|------|--------|------|
|
|
19
|
+
| options.maxSize | number | 1000 | 缓存最大条目数,达到该数量时会自动淘汰最久未使用的条目 |
|
|
20
|
+
| options.defaultTTL | number | 300000(5分钟) | 默认过期时间(毫秒),未指定 TTL 时使用该值 |
|
|
21
|
+
|
|
22
|
+
**示例**:
|
|
23
|
+
```javascript
|
|
24
|
+
// 创建自定义配置的缓存实例
|
|
25
|
+
const customCache = new Cache({
|
|
26
|
+
maxSize: 500, // 最大缓存500条
|
|
27
|
+
defaultTTL: 60000 // 默认1分钟过期
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// 使用默认配置的缓存实例(maxSize=1000,defaultTTL=5分钟)
|
|
31
|
+
const defaultCache = new Cache();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 核心方法
|
|
35
|
+
### 1. 设置缓存值
|
|
36
|
+
#### `set(key, value, ttl = this.defaultTTL)`
|
|
37
|
+
将键值对存入缓存,若缓存达到最大容量且键不存在,则自动淘汰最久未使用的条目,每次设置会更新访问时间。
|
|
38
|
+
|
|
39
|
+
| 参数 | 类型 | 默认值 | 说明 |
|
|
40
|
+
|------|------|--------|------|
|
|
41
|
+
| key | string | - | 缓存键(非空字符串) |
|
|
42
|
+
| value | * | - | 要缓存的任意类型数据 |
|
|
43
|
+
| ttl | number | 实例默认TTL | 该缓存条目的过期时间(毫秒) |
|
|
44
|
+
|
|
45
|
+
**示例**:
|
|
46
|
+
```javascript
|
|
47
|
+
// 设置60秒后过期的缓存
|
|
48
|
+
customCache.set('user:123', { name: '张三', age: 20 }, 60000);
|
|
49
|
+
|
|
50
|
+
// 使用默认过期时间(5分钟)
|
|
51
|
+
customCache.set('config:theme', { color: 'blue' });
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2. 获取缓存值
|
|
55
|
+
#### `get(key)`
|
|
56
|
+
获取指定键的缓存值,若键不存在或已过期则返回 `null`;过期条目会自动删除,命中的条目会更新访问时间。
|
|
57
|
+
|
|
58
|
+
| 参数 | 类型 | 说明 |
|
|
59
|
+
|------|------|------|
|
|
60
|
+
| key | string | 要获取的缓存键 |
|
|
61
|
+
|
|
62
|
+
| 返回值 | 类型 | 说明 |
|
|
63
|
+
|--------|------|------|
|
|
64
|
+
| - | * | 缓存的原始值(命中且未过期);`null`(未命中/已过期) |
|
|
65
|
+
|
|
66
|
+
**示例**:
|
|
67
|
+
```javascript
|
|
68
|
+
const user = customCache.get('user:123');
|
|
69
|
+
if (user !== null) {
|
|
70
|
+
console.log('缓存命中:', user); // { name: '张三', age: 20 }
|
|
71
|
+
} else {
|
|
72
|
+
console.log('缓存未命中或已过期');
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. 删除缓存条目
|
|
77
|
+
#### `del(key)`
|
|
78
|
+
从缓存中删除指定键的条目,同时删除对应的访问记录。
|
|
79
|
+
|
|
80
|
+
| 参数 | 类型 | 说明 |
|
|
81
|
+
|------|------|------|
|
|
82
|
+
| key | string | 要删除的缓存键 |
|
|
83
|
+
|
|
84
|
+
**示例**:
|
|
85
|
+
```javascript
|
|
86
|
+
customCache.del('user:123'); // 删除指定缓存
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 4. 清空所有缓存
|
|
90
|
+
#### `clear()`
|
|
91
|
+
删除所有缓存条目和访问记录,清空整个缓存。
|
|
92
|
+
|
|
93
|
+
**示例**:
|
|
94
|
+
```javascript
|
|
95
|
+
customCache.clear(); // 清空所有缓存
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 5. 检查缓存键是否存在
|
|
99
|
+
#### `has(key)`
|
|
100
|
+
检查指定键是否存在且未过期,若已过期则自动删除条目并返回 `false`。
|
|
101
|
+
|
|
102
|
+
| 参数 | 类型 | 说明 |
|
|
103
|
+
|------|------|------|
|
|
104
|
+
| key | string | 要检查的缓存键 |
|
|
105
|
+
|
|
106
|
+
| 返回值 | 类型 | 说明 |
|
|
107
|
+
|--------|------|------|
|
|
108
|
+
| - | boolean | `true`(存在且未过期);`false`(不存在/已过期) |
|
|
109
|
+
|
|
110
|
+
**示例**:
|
|
111
|
+
```javascript
|
|
112
|
+
if (customCache.has('config:theme')) {
|
|
113
|
+
console.log('缓存存在且有效');
|
|
114
|
+
} else {
|
|
115
|
+
console.log('缓存不存在或已过期');
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 6. 获取缓存有效条目数
|
|
120
|
+
#### `size()`
|
|
121
|
+
返回当前缓存中的有效条目数量(会先自动清理已过期条目)。
|
|
122
|
+
|
|
123
|
+
| 返回值 | 类型 | 说明 |
|
|
124
|
+
|--------|------|------|
|
|
125
|
+
| - | number | 有效缓存条目数 |
|
|
126
|
+
|
|
127
|
+
**示例**:
|
|
128
|
+
```javascript
|
|
129
|
+
console.log(`当前缓存有效条目数: ${customCache.size()}`);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 内部方法(私有)
|
|
133
|
+
### 1. `_evictLRU()`
|
|
134
|
+
淘汰最久未使用的缓存条目,当缓存达到最大容量时自动调用。遍历访问顺序 `Map`,找到访问时间最早的键并删除对应的缓存和访问记录。
|
|
135
|
+
|
|
136
|
+
### 2. `_cleanupExpired()`
|
|
137
|
+
清理所有已过期的缓存条目,在调用 `size()` 方法时自动触发。遍历缓存 `Map`,删除所有过期的条目及对应的访问记录。
|
|
138
|
+
|
|
139
|
+
## 预创建实例
|
|
140
|
+
模块导出了一个默认的 `cache` 实例,使用默认配置(`maxSize=1000`,`defaultTTL=5分钟`),可直接使用:
|
|
141
|
+
```javascript
|
|
142
|
+
import { cache } from 'chanjs/helper';
|
|
143
|
+
|
|
144
|
+
// 直接使用默认实例
|
|
145
|
+
cache.set('global:token', 'abc123', 1800000); // 30分钟过期
|
|
146
|
+
const token = cache.get('global:token');
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## 核心特性
|
|
150
|
+
1. **LRU 淘汰**:缓存达到最大容量时,自动删除最久未使用的条目;
|
|
151
|
+
2. **TTL 过期**:支持自定义过期时间,过期条目自动清理;
|
|
152
|
+
3. **自动清理**:获取缓存大小、检查键存在性时,自动清理过期条目;
|
|
153
|
+
4. **访问更新**:每次设置/获取缓存,都会更新该条目的访问时间,保证 LRU 策略准确性;
|
|
154
|
+
5. **内存安全**:通过最大条目数限制,避免缓存无限制占用内存。
|
|
155
|
+
|
|
156
|
+
## 注意事项
|
|
157
|
+
1. 缓存数据存储在内存中,应用重启后会丢失,不适用于持久化存储场景;
|
|
158
|
+
2. 键必须为字符串类型,非字符串键可能导致不可预期的问题;
|
|
159
|
+
3. 缓存值建议为可序列化的数据(如对象、字符串、数字等),避免存储复杂对象(如 DOM 元素、函数)导致内存泄漏;
|
|
160
|
+
4. 若需高频清理过期条目,可手动调用 `size()` 方法触发清理(不建议频繁调用,会遍历全量缓存)。
|