schema-dsl 2.0.0 → 2.0.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.
Files changed (145) hide show
  1. package/CHANGELOG.md +130 -113
  2. package/LICENSE +21 -21
  3. package/README.md +628 -628
  4. package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
  5. package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
  6. package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
  7. package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
  8. package/dist/index.cjs +75 -29
  9. package/dist/index.d.cts +10 -4
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.js +75 -29
  12. package/dist/plugins/custom-format.cjs +33 -17
  13. package/dist/plugins/custom-format.d.cts +1 -1
  14. package/dist/plugins/custom-format.d.ts +1 -1
  15. package/dist/plugins/custom-format.js +33 -17
  16. package/dist/plugins/custom-type-example.cjs +33 -17
  17. package/dist/plugins/custom-type-example.d.cts +1 -1
  18. package/dist/plugins/custom-type-example.d.ts +1 -1
  19. package/dist/plugins/custom-type-example.js +33 -17
  20. package/dist/plugins/custom-validator.cjs +0 -2
  21. package/dist/plugins/custom-validator.d.cts +1 -1
  22. package/dist/plugins/custom-validator.d.ts +1 -1
  23. package/dist/plugins/custom-validator.js +0 -2
  24. package/docs/FEATURE-INDEX.md +553 -553
  25. package/docs/add-custom-locale.md +496 -496
  26. package/docs/add-keyword.md +24 -24
  27. package/docs/api-reference.md +1047 -1047
  28. package/docs/api.md +13 -13
  29. package/docs/best-practices-project-structure.md +417 -417
  30. package/docs/best-practices.md +712 -712
  31. package/docs/cache-manager.md +344 -344
  32. package/docs/compile.md +45 -45
  33. package/docs/conditional-api.md +1307 -1307
  34. package/docs/custom-extensions-guide.md +339 -339
  35. package/docs/design-philosophy.md +606 -606
  36. package/docs/doc-index.md +324 -324
  37. package/docs/dsl-syntax.md +714 -714
  38. package/docs/dynamic-locale.md +608 -608
  39. package/docs/enum.md +482 -482
  40. package/docs/error-handling.md +1975 -1975
  41. package/docs/export-guide.md +501 -501
  42. package/docs/export-limitations.md +567 -567
  43. package/docs/faq.md +596 -596
  44. package/docs/frontend-i18n-guide.md +307 -307
  45. package/docs/i18n-user-guide.md +487 -487
  46. package/docs/i18n.md +476 -476
  47. package/docs/index.md +48 -48
  48. package/docs/json-schema-basics.md +40 -40
  49. package/docs/label-vs-description.md +271 -271
  50. package/docs/markdown-exporter.md +406 -406
  51. package/docs/mongodb-exporter.md +302 -302
  52. package/docs/multi-language.md +26 -26
  53. package/docs/multi-type-support.md +322 -322
  54. package/docs/mysql-exporter.md +280 -280
  55. package/docs/number-operators.md +449 -449
  56. package/docs/optional-marker-guide.md +326 -326
  57. package/docs/performance-guide.md +49 -49
  58. package/docs/plugin-system.md +381 -381
  59. package/docs/plugin-type-registration.md +34 -34
  60. package/docs/postgresql-exporter.md +311 -311
  61. package/docs/public/favicon.svg +4 -4
  62. package/docs/quick-start.md +435 -435
  63. package/docs/runtime-locale-support.md +532 -532
  64. package/docs/schema-helper.md +345 -345
  65. package/docs/schema-utils-advanced-issues.md +23 -23
  66. package/docs/schema-utils-best-practices.md +20 -20
  67. package/docs/schema-utils-chaining.md +150 -150
  68. package/docs/schema-utils.md +524 -524
  69. package/docs/security-checklist.md +20 -20
  70. package/docs/string-extensions.md +488 -488
  71. package/docs/troubleshooting.md +486 -486
  72. package/docs/type-converter.md +310 -310
  73. package/docs/type-reference.md +242 -242
  74. package/docs/typescript-guide.md +584 -584
  75. package/docs/union-type-guide.md +157 -157
  76. package/docs/union-types.md +284 -284
  77. package/docs/validate-async.md +491 -491
  78. package/docs/validate-batch.md +49 -49
  79. package/docs/validate-dsl-object-support.md +578 -578
  80. package/docs/validate.md +506 -506
  81. package/docs/validation-guide.md +502 -502
  82. package/docs/validator.md +39 -39
  83. package/package.json +131 -131
  84. package/plugins/custom-format.cjs +8 -8
  85. package/plugins/custom-type-example.cjs +8 -8
  86. package/plugins/custom-validator.cjs +8 -8
  87. package/src/adapters/DslAdapter.ts +111 -111
  88. package/src/adapters/index.ts +1 -1
  89. package/src/config/constants.ts +83 -83
  90. package/src/config/index.ts +2 -2
  91. package/src/config/patterns.ts +77 -77
  92. package/src/core/CacheManager.ts +169 -159
  93. package/src/core/ConditionalBuilder.ts +382 -382
  94. package/src/core/ConditionalRuntime.ts +27 -27
  95. package/src/core/ConditionalValidator.ts +254 -254
  96. package/src/core/DslBuilder.ts +687 -677
  97. package/src/core/ErrorCodes.ts +38 -38
  98. package/src/core/ErrorFormatter.ts +271 -271
  99. package/src/core/JSONSchemaCore.ts +65 -65
  100. package/src/core/Locale.ts +187 -187
  101. package/src/core/MessageTemplate.ts +42 -42
  102. package/src/core/ObjectDslBuilder.ts +64 -64
  103. package/src/core/PluginManager.ts +326 -326
  104. package/src/core/StringExtensions.ts +140 -140
  105. package/src/core/TemplateEngine.ts +44 -44
  106. package/src/core/Validator.ts +448 -448
  107. package/src/errors/I18nError.ts +159 -159
  108. package/src/errors/ValidationError.ts +105 -105
  109. package/src/exporters/BaseExporter.ts +60 -60
  110. package/src/exporters/MarkdownExporter.ts +305 -305
  111. package/src/exporters/MongoDBExporter.ts +126 -126
  112. package/src/exporters/MySQLExporter.ts +156 -155
  113. package/src/exporters/PostgreSQLExporter.ts +222 -222
  114. package/src/exporters/index.ts +18 -18
  115. package/src/index.ts +651 -633
  116. package/src/locales/en-US.ts +160 -160
  117. package/src/locales/es-ES.ts +160 -160
  118. package/src/locales/fr-FR.ts +160 -160
  119. package/src/locales/index.ts +103 -103
  120. package/src/locales/ja-JP.ts +160 -160
  121. package/src/locales/types.ts +156 -156
  122. package/src/locales/zh-CN.ts +160 -160
  123. package/src/parser/ConstraintParser.ts +101 -101
  124. package/src/parser/DslParser.ts +470 -470
  125. package/src/parser/SchemaCompiler.ts +66 -66
  126. package/src/parser/TypeRegistry.ts +250 -250
  127. package/src/parser/index.ts +6 -6
  128. package/src/plugins/custom-format.ts +124 -126
  129. package/src/plugins/custom-type-example.ts +106 -108
  130. package/src/plugins/custom-validator.ts +138 -140
  131. package/src/types/conditional.ts +28 -28
  132. package/src/types/config.ts +59 -59
  133. package/src/types/dsl.ts +131 -131
  134. package/src/types/error.ts +60 -60
  135. package/src/types/index.ts +17 -17
  136. package/src/types/infer.ts +127 -127
  137. package/src/types/plugin.ts +58 -58
  138. package/src/types/safe-regex.d.ts +9 -9
  139. package/src/types/schema.ts +66 -66
  140. package/src/types/validate.ts +71 -71
  141. package/src/utils/SchemaHelper.ts +196 -196
  142. package/src/utils/SchemaUtils.ts +365 -346
  143. package/src/utils/TypeConverter.ts +215 -215
  144. package/src/utils/index.ts +10 -10
  145. package/src/validators/CustomKeywords.ts +477 -477
@@ -1,712 +1,712 @@
1
- # schema-dsl 最佳实践
2
-
3
- > **用途**: 帮助你写出高质量、高性能的 Schema 代码
4
- > **更新**: 2026-05-08
5
-
6
- ---
7
-
8
- ## 📑 目录
9
-
10
- - [Schema 设计原则](#schema-设计原则)
11
- - [性能优化](#性能优化)
12
- - [安全性考虑](#安全性考虑)
13
- - [错误处理](#错误处理)
14
- - [代码组织](#代码组织)
15
- - [生产环境建议](#生产环境建议)
16
-
17
- ---
18
-
19
- ## Schema 设计原则
20
-
21
- ### 1. 简单字段用纯 DSL
22
-
23
- **推荐**:
24
- ```javascript
25
- const schema = dsl({
26
- username: 'string:3-32!',
27
- age: 'number:18-120',
28
- email: 'email!',
29
- role: 'admin|user|guest'
30
- });
31
- ```
32
-
33
- **不推荐**(过度复杂):
34
- ```javascript
35
- const schema = dsl({
36
- username: dsl('string').min(3).max(32).required(),
37
- // 太冗长了!
38
- });
39
- ```
40
-
41
- **原则**: 能用 DSL 字符串表达的,就不要用链式调用。
42
-
43
- ---
44
-
45
- ### 2. 复杂验证用链式调用
46
-
47
- **适合链式调用的场景**:
48
- - 需要正则验证
49
- - 需要自定义错误消息
50
- - 需要自定义验证器
51
- - 需要标签(label)
52
-
53
- **示例**:
54
- ```javascript
55
- const schema = dsl({
56
- // 简单字段:纯 DSL
57
- age: 'number:18-120',
58
-
59
- // 复杂字段:链式调用
60
- username: 'string:3-32!'
61
- .pattern(/^[a-zA-Z0-9_]+$/)
62
- .label('用户名')
63
- .messages({
64
- 'pattern': '只能包含字母、数字和下划线',
65
- 'min': '至少3个字符',
66
- 'max': '最多32个字符'
67
- }),
68
-
69
- email: 'email!'
70
- .custom((value) => {
71
- if (value.endsWith('@blocked.example')) return '该邮箱域名不允许注册';
72
- })
73
- .label('邮箱地址')
74
- });
75
- ```
76
-
77
- ---
78
-
79
- ### 3. 使用预设验证器
80
-
81
- schema-dsl 提供了常用的预设验证器,开箱即用:
82
-
83
- ```javascript
84
- const schema = dsl({
85
- // ✅ 使用预设验证器(推荐)
86
- username: dsl('string!').username(), // 自动设置 3-32 长度 + 正则
87
- password: dsl('string!').password('strong'), // 强密码验证
88
- phone: dsl('string!').phone('cn'), // 中国手机号
89
-
90
- // ❌ 手动实现(不推荐)
91
- username: 'string:3-32!'
92
- .pattern(/^[a-zA-Z][a-zA-Z0-9_]*$/)
93
- });
94
- ```
95
-
96
- **可用预设**:
97
- - `username(preset?)` - 用户名验证
98
- - `password(strength?)` - 密码强度验证
99
- - `phone(country?)` - 手机号验证
100
- - `slug()` - URL slug 验证
101
-
102
- ---
103
-
104
- ### 4. 避免过深的嵌套
105
-
106
- **不推荐**(嵌套过深):
107
- ```javascript
108
- const schema = dsl({
109
- user: {
110
- profile: {
111
- personal: {
112
- address: {
113
- detail: {
114
- street: 'string' // 嵌套 5 层
115
- }
116
- }
117
- }
118
- }
119
- }
120
- });
121
- ```
122
-
123
- **推荐**(拆分或扁平化):
124
- ```javascript
125
- // 方案1: 拆分为多个 Schema
126
- const addressSchema = dsl({
127
- street: 'string!',
128
- city: 'string!',
129
- zipCode: 'string'
130
- });
131
-
132
- const userSchema = dsl({
133
- name: 'string!',
134
- email: 'email!',
135
- address: addressSchema
136
- });
137
-
138
- // 方案2: 扁平化
139
- const schema = dsl({
140
- 'user_name': 'string!',
141
- 'user_email': 'email!',
142
- 'address_street': 'string!',
143
- 'address_city': 'string!'
144
- });
145
- ```
146
-
147
- **原则**: 嵌套深度建议不超过 3-4 层。
148
-
149
- ---
150
-
151
- ## 性能优化
152
-
153
- ### 1. 预编译 Schema
154
-
155
- **不推荐**(每次都编译):
156
- ```javascript
157
- app.post('/api/user', (req, res) => {
158
- const schema = dsl({ username: 'string!' });
159
- const result = validate(schema, req.body); // 每次都编译
160
- });
161
- ```
162
-
163
- **推荐**(预编译):
164
- ```javascript
165
- // 在应用启动时编译一次
166
- const validator = new Validator();
167
- const userSchema = dsl({ username: 'string!' });
168
- const validateUser = validator.compile(userSchema);
169
-
170
- app.post('/api/user', (req, res) => {
171
- const result = validateUser(req.body); // 直接使用
172
- });
173
- ```
174
-
175
- **收益**: 复用已编译结果可以显著减少重复编译成本,尤其适合热点路由和高频校验路径。
176
-
177
- ---
178
-
179
- ### 2. 启用缓存
180
-
181
- ```javascript
182
- const validator = new Validator({
183
- cache: true // ✅ 简写:启用默认编译缓存配置
184
- });
185
-
186
- // 需要更细粒度时,使用对象配置
187
- const tunedValidator = new Validator({
188
- cache: {
189
- enabled: true,
190
- maxSize: 500,
191
- ttl: 60 * 60 * 1000
192
- }
193
- });
194
-
195
- // 或者使用全局单例(默认启用缓存)
196
- const { validate } = require('schema-dsl');
197
- validate(schema, data); // 自动缓存
198
- ```
199
-
200
- ---
201
-
202
- ### 3. 批量验证
203
-
204
- **不推荐**(循环验证):
205
- ```javascript
206
- const errors = [];
207
- records.forEach(record => {
208
- const result = validate(schema, record);
209
- if (!result.valid) {
210
- errors.push(result.errors);
211
- }
212
- });
213
- ```
214
-
215
- **推荐**(批量验证):
216
- ```javascript
217
- const { SchemaUtils, Validator } = require('schema-dsl');
218
-
219
- const validator = new Validator();
220
- const result = SchemaUtils.validateBatch(schema, records, validator.getAjv());
221
- // 当你已经复用 Validator 底层 Ajv 实例时,这条路径适合批量校验
222
- ```
223
-
224
- > ℹ️ 如果你确实要直接传入自己创建的 Ajv 实例,请先确保它已经注册了与 schema-dsl 生成 schema 匹配的格式和关键字;对大多数项目来说,直接复用 `validator.getAjv()` 更稳妥。
225
-
226
- ---
227
-
228
- ### 4. 优化正则表达式
229
-
230
- **不推荐**(可能导致 ReDoS):
231
- ```javascript
232
- // 危险的正则:灾难性回溯
233
- .pattern(/^(a+)+$/)
234
- .pattern(/^(a*)*$/)
235
- .pattern(/^(a|a)*$/)
236
- ```
237
-
238
- **推荐**(安全高效):
239
- ```javascript
240
- // 简单明确的正则
241
- .pattern(/^[a-zA-Z0-9_]+$/)
242
- .pattern(/^[a-z]{3,10}$/)
243
- ```
244
-
245
- **工具**: 使用 [regexploit](https://www.npmjs.com/package/regexploit) 检测危险正则。
246
-
247
- ---
248
-
249
- ### 5. 避免在循环中创建 Schema
250
-
251
- **不推荐**:
252
- ```javascript
253
- records.forEach(record => {
254
- const schema = dsl({ name: 'string!' }); // 每次都创建
255
- validate(schema, record);
256
- });
257
- ```
258
-
259
- **推荐**:
260
- ```javascript
261
- const schema = dsl({ name: 'string!' }); // 创建一次
262
- records.forEach(record => {
263
- validate(schema, record); // 重复使用
264
- });
265
- ```
266
-
267
- ---
268
-
269
- ## 安全性考虑
270
-
271
- ### 1. 限制用户输入的正则
272
-
273
- **危险**:
274
- ```javascript
275
- // ❌ 用户控制的正则表达式
276
- app.post('/api/validate', (req, res) => {
277
- const pattern = req.body.pattern; // 用户输入
278
- const schema = dsl('string').pattern(new RegExp(pattern)); // 危险!
279
- });
280
- ```
281
-
282
- **原因**: 用户可能输入恶意正则导致 ReDoS 攻击。
283
-
284
- **安全做法**:
285
- ```javascript
286
- // ✅ 使用预定义的正则
287
- const ALLOWED_PATTERNS = {
288
- username: /^[a-zA-Z0-9_]+$/,
289
- email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
290
- };
291
-
292
- app.post('/api/validate', (req, res) => {
293
- const patternName = req.body.pattern;
294
- const pattern = ALLOWED_PATTERNS[patternName];
295
- if (!pattern) {
296
- return res.status(400).json({ error: 'Invalid pattern' });
297
- }
298
- const schema = dsl('string').pattern(pattern);
299
- });
300
- ```
301
-
302
- ---
303
-
304
- ### 2. 清理错误消息
305
-
306
- 生产环境不要暴露敏感信息:
307
-
308
- ```javascript
309
- // 开发环境
310
- if (process.env.NODE_ENV === 'development') {
311
- return res.status(400).json({
312
- valid: false,
313
- errors: result.errors // 详细错误
314
- });
315
- }
316
-
317
- // 生产环境
318
- return res.status(400).json({
319
- valid: false,
320
- message: '输入数据验证失败' // 简化消息
321
- });
322
- ```
323
-
324
- ---
325
-
326
- ### 3. 限制 Schema 复杂度
327
-
328
- ```javascript
329
- const MAX_SCHEMA_SIZE = 10000;
330
-
331
- if (JSON.stringify(schema).length > MAX_SCHEMA_SIZE) {
332
- throw new Error('Schema 体积过大,建议拆分');
333
- }
334
-
335
- // 在 validate 前检查
336
- const depthCheck = DslBuilder.validateNestingDepth(schema, 10);
337
- if (!depthCheck.valid) {
338
- throw new Error(depthCheck.message);
339
- }
340
- ```
341
-
342
- ---
343
-
344
- ### 4. 防止原型污染
345
-
346
- ```javascript
347
- // 验证数据时避免原型污染
348
- const validator = new Validator({
349
- removeAdditional: true, // 移除额外属性
350
- useDefaults: false // 不自动填充默认值(如果不需要)
351
- });
352
- ```
353
-
354
- ---
355
-
356
- ## 错误处理
357
-
358
- ### 1. 统一错误格式
359
-
360
- **推荐的错误处理中间件**:
361
- ```javascript
362
- // Express 中间件
363
- function validateMiddleware(schema) {
364
- return (req, res, next) => {
365
- const result = validate(schema, req.body);
366
-
367
- if (!result.valid) {
368
- return res.status(400).json({
369
- code: 'VALIDATION_ERROR',
370
- message: '请求数据验证失败',
371
- errors: result.errors.map(err => ({
372
- field: err.path,
373
- message: err.message
374
- }))
375
- });
376
- }
377
-
378
- next();
379
- };
380
- }
381
-
382
- // 使用
383
- app.post('/api/user',
384
- validateMiddleware(userSchema),
385
- userController.create
386
- );
387
- ```
388
-
389
- ---
390
-
391
- ### 2. 友好的错误消息
392
-
393
- **使用 label 和自定义消息**:
394
- ```javascript
395
- const schema = dsl({
396
- username: 'string:3-32!'
397
- .label('用户名')
398
- .messages({
399
- 'required': '{{#label}}不能为空',
400
- 'min': '{{#label}}至少需要{{#limit}}个字符',
401
- 'max': '{{#label}}最多{{#limit}}个字符',
402
- 'pattern': '{{#label}}格式不正确'
403
- }),
404
-
405
- email: 'email!'
406
- .label('邮箱地址')
407
- .messages({
408
- 'required': '请填写{{#label}}',
409
- 'format': '{{#label}}格式不正确'
410
- })
411
- });
412
- ```
413
-
414
- **效果**:
415
- ```text
416
- ❌ 用户名不能为空
417
- ❌ 用户名至少需要3个字符
418
- ✅ 清晰明了,用户友好
419
- ```
420
-
421
- ---
422
-
423
- ### 3. 处理外部异步校验错误
424
-
425
- > `.custom()` 当前仅支持同步函数;涉及数据库、RPC、HTTP 等异步检查时,请在基础校验通过后于业务层单独执行。
426
-
427
- ```javascript
428
- const schema = dsl({
429
- email: 'email!'.label('邮箱地址')
430
- });
431
-
432
- async function validateUser(data) {
433
- const result = validate(schema, data);
434
- if (!result.valid) return result;
435
-
436
- try {
437
- const exists = await checkEmailExists(data.email);
438
- if (exists) {
439
- return {
440
- valid: false,
441
- errors: [{ field: 'email', keyword: 'business', message: '邮箱已被占用' }]
442
- };
443
- }
444
- } catch (error) {
445
- console.error('Email check failed:', error);
446
- }
447
-
448
- return result;
449
- }
450
- ```
451
-
452
- ---
453
-
454
- ## 代码组织
455
-
456
- ### 1. 集中管理 Schema
457
-
458
- **推荐的项目结构**:
459
- ```text
460
- src/
461
- ├── schemas/
462
- │ ├── index.js # 导出所有 Schema
463
- │ ├── user.schema.js # 用户相关 Schema
464
- │ ├── post.schema.js # 文章相关 Schema
465
- │ └── common.schema.js # 通用 Schema
466
- ├── routes/
467
- │ ├── user.routes.js
468
- │ └── post.routes.js
469
- └── controllers/
470
- ```
471
-
472
- **schemas/user.schema.js**:
473
- ```javascript
474
- const { dsl } = require('schema-dsl');
475
-
476
- // 可复用的字段
477
- const commonFields = {
478
- username: dsl('string!').username().label('用户名'),
479
- email: 'email!',
480
- password: dsl('string!').password('strong').label('密码')
481
- };
482
-
483
- // 注册 Schema
484
- exports.registerSchema = dsl({
485
- ...commonFields,
486
- confirmPassword: 'string!',
487
- agreeTerms: 'boolean!'
488
- });
489
-
490
- // 登录 Schema
491
- exports.loginSchema = dsl({
492
- email: commonFields.email,
493
- password: commonFields.password
494
- });
495
-
496
- // 更新 Schema
497
- exports.updateSchema = dsl({
498
- username: commonFields.username,
499
- email: commonFields.email
500
- // 不包含密码
501
- });
502
- ```
503
-
504
- **schemas/index.js**:
505
- ```javascript
506
- const userSchemas = require('./user.schema');
507
- const postSchemas = require('./post.schema');
508
-
509
- module.exports = {
510
- user: userSchemas,
511
- post: postSchemas
512
- };
513
- ```
514
-
515
- **routes/user.routes.js**:
516
- ```javascript
517
- const schemas = require('../schemas');
518
- const { validate } = require('schema-dsl');
519
-
520
- router.post('/register', (req, res) => {
521
- const result = validate(schemas.user.registerSchema, req.body);
522
- // ...
523
- });
524
- ```
525
-
526
- ---
527
-
528
- ### 2. Schema 复用
529
-
530
- **使用 SchemaUtils**:
531
- ```javascript
532
- const { SchemaUtils, dsl } = require('schema-dsl');
533
-
534
- // 创建可复用字段库
535
- const fields = SchemaUtils.createLibrary({
536
- email: () => 'email!',
537
- phone: () => dsl('string!').phone('cn'),
538
- password: () => dsl('string!').password('strong')
539
- });
540
-
541
- // 在多个 Schema 中复用
542
- const registerSchema = dsl({
543
- email: fields.email(),
544
- password: fields.password(),
545
- username: 'string:3-32!'
546
- });
547
-
548
- const profileSchema = dsl({
549
- email: fields.email(),
550
- phone: fields.phone(),
551
- bio: 'string:500'
552
- });
553
- ```
554
-
555
- ---
556
-
557
- ## 生产环境建议
558
-
559
- ### 1. 环境配置
560
-
561
- ```javascript
562
- // config/validator.js
563
- const { Validator } = require('schema-dsl');
564
-
565
- const config = {
566
- development: {
567
- verbose: true,
568
- allErrors: true,
569
- cache: false // ✅ 简写:关闭缓存,便于调试
570
- },
571
- production: {
572
- verbose: false,
573
- allErrors: false, // 只返回第一个错误
574
- cache: {
575
- enabled: true,
576
- maxSize: 1000,
577
- ttl: 60 * 60 * 1000
578
- }
579
- }
580
- };
581
-
582
- module.exports = new Validator(
583
- config[process.env.NODE_ENV || 'development']
584
- );
585
- ```
586
-
587
- ---
588
-
589
- ### 2. 监控和日志
590
-
591
- ```javascript
592
- const validator = new Validator();
593
-
594
- // 包装 validate 方法,添加监控
595
- const originalValidate = validator.validate.bind(validator);
596
- validator.validate = function(schema, data, options) {
597
- const startTime = Date.now();
598
- const result = originalValidate(schema, data, options);
599
- const duration = Date.now() - startTime;
600
-
601
- // 记录慢查询
602
- if (duration > 100) {
603
- console.warn(`Slow validation: ${duration}ms`);
604
- }
605
-
606
- // 记录验证失败
607
- if (!result.valid) {
608
- logger.info('Validation failed', {
609
- errors: result.errors.length,
610
- paths: result.errors.map(e => e.path)
611
- });
612
- }
613
-
614
- return result;
615
- };
616
- ```
617
-
618
- ---
619
-
620
- ### 3. 健康检查
621
-
622
- ```javascript
623
- // routes/health.js
624
- app.get('/health', (req, res) => {
625
- const { validator } = require('../config/validator');
626
-
627
- // 检查验证器是否正常
628
- try {
629
- const testSchema = dsl({ test: 'string!' });
630
- const result = validator.validate(testSchema, { test: 'ok' });
631
-
632
- if (!result.valid) {
633
- throw new Error('Validator test failed');
634
- }
635
-
636
- res.json({
637
- status: 'ok',
638
- validator: 'operational',
639
- cacheStats: validator.getCacheStats()
640
- });
641
- } catch (error) {
642
- res.status(500).json({
643
- status: 'error',
644
- message: error.message
645
- });
646
- }
647
- });
648
- ```
649
-
650
- ---
651
-
652
- ### 4. 定期维护
653
-
654
- ```javascript
655
- // 定期清理缓存
656
- const cron = require('node-cron');
657
-
658
- // 每天凌晨清理一次
659
- cron.schedule('0 0 * * *', () => {
660
- validator.clearCache();
661
- console.log('Validator cache cleared');
662
- });
663
-
664
- // 或者根据内存使用情况清理
665
- setInterval(() => {
666
- const memUsage = process.memoryUsage();
667
- if (memUsage.heapUsed > 500 * 1024 * 1024) { // 超过 500MB
668
- validator.clearCache();
669
- }
670
- }, 60000); // 每分钟检查一次
671
- ```
672
-
673
- ---
674
-
675
- ## 性能基准参考
676
-
677
- 缓存命中前后通常能显著降低重复编译开销,但绝对耗时会受到机器性能、Node 版本、schema 复杂度、数据规模和命中率影响,不建议把某组固定毫秒数当成通用基准。
678
-
679
- **更稳定的结论**:
680
-
681
- - 复用同一个 schema 对象或 `Validator` 实例,通常比每次请求都重新编译更快
682
- - schema 越复杂、重复验证次数越多,缓存收益通常越明显
683
- - 批量验证总耗时主要取决于单条 schema 复杂度和数据规模,不应使用固定毫秒数做容量承诺
684
-
685
- 如需当前可复查的吞吐量对比,请以维护中的 benchmark 结果和 FAQ 中同步的性能数据为准。
686
-
687
- ---
688
-
689
- ## 总结
690
-
691
- 遵循这些最佳实践,你的 schema-dsl 代码将具备:
692
-
693
- ✅ **高性能** - 通过预编译和缓存
694
- ✅ **高安全性** - 避免常见安全陷阱
695
- ✅ **高可维护性** - 清晰的代码组织
696
- ✅ **高可用性** - 完善的错误处理
697
-
698
- ---
699
-
700
- ## 延伸阅读
701
-
702
- - [性能优化指南](performance-guide.md)
703
- - [安全检查清单](security-checklist.md)
704
- - [故障排查指南](troubleshooting.md)
705
-
706
- ---
707
-
708
- ## 对应示例文件
709
-
710
- **示例入口**: [best-practices.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/best-practices.ts)
711
- **说明**: 展示“简单字段用纯 DSL、复杂字段局部使用 Builder、字段库复用”的推荐组合,以及成功 / 失败两条验证路径。
712
-
1
+ # schema-dsl 最佳实践
2
+
3
+ > **用途**: 帮助你写出高质量、高性能的 Schema 代码
4
+ > **更新**: 2026-05-08
5
+
6
+ ---
7
+
8
+ ## 📑 目录
9
+
10
+ - [Schema 设计原则](#schema-设计原则)
11
+ - [性能优化](#性能优化)
12
+ - [安全性考虑](#安全性考虑)
13
+ - [错误处理](#错误处理)
14
+ - [代码组织](#代码组织)
15
+ - [生产环境建议](#生产环境建议)
16
+
17
+ ---
18
+
19
+ ## Schema 设计原则
20
+
21
+ ### 1. 简单字段用纯 DSL
22
+
23
+ **推荐**:
24
+ ```javascript
25
+ const schema = dsl({
26
+ username: 'string:3-32!',
27
+ age: 'number:18-120',
28
+ email: 'email!',
29
+ role: 'admin|user|guest'
30
+ });
31
+ ```
32
+
33
+ **不推荐**(过度复杂):
34
+ ```javascript
35
+ const schema = dsl({
36
+ username: dsl('string').min(3).max(32).required(),
37
+ // 太冗长了!
38
+ });
39
+ ```
40
+
41
+ **原则**: 能用 DSL 字符串表达的,就不要用链式调用。
42
+
43
+ ---
44
+
45
+ ### 2. 复杂验证用链式调用
46
+
47
+ **适合链式调用的场景**:
48
+ - 需要正则验证
49
+ - 需要自定义错误消息
50
+ - 需要自定义验证器
51
+ - 需要标签(label)
52
+
53
+ **示例**:
54
+ ```javascript
55
+ const schema = dsl({
56
+ // 简单字段:纯 DSL
57
+ age: 'number:18-120',
58
+
59
+ // 复杂字段:链式调用
60
+ username: 'string:3-32!'
61
+ .pattern(/^[a-zA-Z0-9_]+$/)
62
+ .label('用户名')
63
+ .messages({
64
+ 'pattern': '只能包含字母、数字和下划线',
65
+ 'min': '至少3个字符',
66
+ 'max': '最多32个字符'
67
+ }),
68
+
69
+ email: 'email!'
70
+ .custom((value) => {
71
+ if (value.endsWith('@blocked.example')) return '该邮箱域名不允许注册';
72
+ })
73
+ .label('邮箱地址')
74
+ });
75
+ ```
76
+
77
+ ---
78
+
79
+ ### 3. 使用预设验证器
80
+
81
+ schema-dsl 提供了常用的预设验证器,开箱即用:
82
+
83
+ ```javascript
84
+ const schema = dsl({
85
+ // ✅ 使用预设验证器(推荐)
86
+ username: dsl('string!').username(), // 自动设置 3-32 长度 + 正则
87
+ password: dsl('string!').password('strong'), // 强密码验证
88
+ phone: dsl('string!').phone('cn'), // 中国手机号
89
+
90
+ // ❌ 手动实现(不推荐)
91
+ username: 'string:3-32!'
92
+ .pattern(/^[a-zA-Z][a-zA-Z0-9_]*$/)
93
+ });
94
+ ```
95
+
96
+ **可用预设**:
97
+ - `username(preset?)` - 用户名验证
98
+ - `password(strength?)` - 密码强度验证
99
+ - `phone(country?)` - 手机号验证
100
+ - `slug()` - URL slug 验证
101
+
102
+ ---
103
+
104
+ ### 4. 避免过深的嵌套
105
+
106
+ **不推荐**(嵌套过深):
107
+ ```javascript
108
+ const schema = dsl({
109
+ user: {
110
+ profile: {
111
+ personal: {
112
+ address: {
113
+ detail: {
114
+ street: 'string' // 嵌套 5 层
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ });
121
+ ```
122
+
123
+ **推荐**(拆分或扁平化):
124
+ ```javascript
125
+ // 方案1: 拆分为多个 Schema
126
+ const addressSchema = dsl({
127
+ street: 'string!',
128
+ city: 'string!',
129
+ zipCode: 'string'
130
+ });
131
+
132
+ const userSchema = dsl({
133
+ name: 'string!',
134
+ email: 'email!',
135
+ address: addressSchema
136
+ });
137
+
138
+ // 方案2: 扁平化
139
+ const schema = dsl({
140
+ 'user_name': 'string!',
141
+ 'user_email': 'email!',
142
+ 'address_street': 'string!',
143
+ 'address_city': 'string!'
144
+ });
145
+ ```
146
+
147
+ **原则**: 嵌套深度建议不超过 3-4 层。
148
+
149
+ ---
150
+
151
+ ## 性能优化
152
+
153
+ ### 1. 预编译 Schema
154
+
155
+ **不推荐**(每次都编译):
156
+ ```javascript
157
+ app.post('/api/user', (req, res) => {
158
+ const schema = dsl({ username: 'string!' });
159
+ const result = validate(schema, req.body); // 每次都编译
160
+ });
161
+ ```
162
+
163
+ **推荐**(预编译):
164
+ ```javascript
165
+ // 在应用启动时编译一次
166
+ const validator = new Validator();
167
+ const userSchema = dsl({ username: 'string!' });
168
+ const validateUser = validator.compile(userSchema);
169
+
170
+ app.post('/api/user', (req, res) => {
171
+ const result = validateUser(req.body); // 直接使用
172
+ });
173
+ ```
174
+
175
+ **收益**: 复用已编译结果可以显著减少重复编译成本,尤其适合热点路由和高频校验路径。
176
+
177
+ ---
178
+
179
+ ### 2. 启用缓存
180
+
181
+ ```javascript
182
+ const validator = new Validator({
183
+ cache: true // ✅ 简写:启用默认编译缓存配置
184
+ });
185
+
186
+ // 需要更细粒度时,使用对象配置
187
+ const tunedValidator = new Validator({
188
+ cache: {
189
+ enabled: true,
190
+ maxSize: 500,
191
+ ttl: 60 * 60 * 1000
192
+ }
193
+ });
194
+
195
+ // 或者使用全局单例(默认启用缓存)
196
+ const { validate } = require('schema-dsl');
197
+ validate(schema, data); // 自动缓存
198
+ ```
199
+
200
+ ---
201
+
202
+ ### 3. 批量验证
203
+
204
+ **不推荐**(循环验证):
205
+ ```javascript
206
+ const errors = [];
207
+ records.forEach(record => {
208
+ const result = validate(schema, record);
209
+ if (!result.valid) {
210
+ errors.push(result.errors);
211
+ }
212
+ });
213
+ ```
214
+
215
+ **推荐**(批量验证):
216
+ ```javascript
217
+ const { SchemaUtils, Validator } = require('schema-dsl');
218
+
219
+ const validator = new Validator();
220
+ const result = SchemaUtils.validateBatch(schema, records, validator.getAjv());
221
+ // 当你已经复用 Validator 底层 Ajv 实例时,这条路径适合批量校验
222
+ ```
223
+
224
+ > ℹ️ 如果你确实要直接传入自己创建的 Ajv 实例,请先确保它已经注册了与 schema-dsl 生成 schema 匹配的格式和关键字;对大多数项目来说,直接复用 `validator.getAjv()` 更稳妥。
225
+
226
+ ---
227
+
228
+ ### 4. 优化正则表达式
229
+
230
+ **不推荐**(可能导致 ReDoS):
231
+ ```javascript
232
+ // 危险的正则:灾难性回溯
233
+ .pattern(/^(a+)+$/)
234
+ .pattern(/^(a*)*$/)
235
+ .pattern(/^(a|a)*$/)
236
+ ```
237
+
238
+ **推荐**(安全高效):
239
+ ```javascript
240
+ // 简单明确的正则
241
+ .pattern(/^[a-zA-Z0-9_]+$/)
242
+ .pattern(/^[a-z]{3,10}$/)
243
+ ```
244
+
245
+ **工具**: 使用 [regexploit](https://www.npmjs.com/package/regexploit) 检测危险正则。
246
+
247
+ ---
248
+
249
+ ### 5. 避免在循环中创建 Schema
250
+
251
+ **不推荐**:
252
+ ```javascript
253
+ records.forEach(record => {
254
+ const schema = dsl({ name: 'string!' }); // 每次都创建
255
+ validate(schema, record);
256
+ });
257
+ ```
258
+
259
+ **推荐**:
260
+ ```javascript
261
+ const schema = dsl({ name: 'string!' }); // 创建一次
262
+ records.forEach(record => {
263
+ validate(schema, record); // 重复使用
264
+ });
265
+ ```
266
+
267
+ ---
268
+
269
+ ## 安全性考虑
270
+
271
+ ### 1. 限制用户输入的正则
272
+
273
+ **危险**:
274
+ ```javascript
275
+ // ❌ 用户控制的正则表达式
276
+ app.post('/api/validate', (req, res) => {
277
+ const pattern = req.body.pattern; // 用户输入
278
+ const schema = dsl('string').pattern(new RegExp(pattern)); // 危险!
279
+ });
280
+ ```
281
+
282
+ **原因**: 用户可能输入恶意正则导致 ReDoS 攻击。
283
+
284
+ **安全做法**:
285
+ ```javascript
286
+ // ✅ 使用预定义的正则
287
+ const ALLOWED_PATTERNS = {
288
+ username: /^[a-zA-Z0-9_]+$/,
289
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
290
+ };
291
+
292
+ app.post('/api/validate', (req, res) => {
293
+ const patternName = req.body.pattern;
294
+ const pattern = ALLOWED_PATTERNS[patternName];
295
+ if (!pattern) {
296
+ return res.status(400).json({ error: 'Invalid pattern' });
297
+ }
298
+ const schema = dsl('string').pattern(pattern);
299
+ });
300
+ ```
301
+
302
+ ---
303
+
304
+ ### 2. 清理错误消息
305
+
306
+ 生产环境不要暴露敏感信息:
307
+
308
+ ```javascript
309
+ // 开发环境
310
+ if (process.env.NODE_ENV === 'development') {
311
+ return res.status(400).json({
312
+ valid: false,
313
+ errors: result.errors // 详细错误
314
+ });
315
+ }
316
+
317
+ // 生产环境
318
+ return res.status(400).json({
319
+ valid: false,
320
+ message: '输入数据验证失败' // 简化消息
321
+ });
322
+ ```
323
+
324
+ ---
325
+
326
+ ### 3. 限制 Schema 复杂度
327
+
328
+ ```javascript
329
+ const MAX_SCHEMA_SIZE = 10000;
330
+
331
+ if (JSON.stringify(schema).length > MAX_SCHEMA_SIZE) {
332
+ throw new Error('Schema 体积过大,建议拆分');
333
+ }
334
+
335
+ // 在 validate 前检查
336
+ const depthCheck = DslBuilder.validateNestingDepth(schema, 10);
337
+ if (!depthCheck.valid) {
338
+ throw new Error(depthCheck.message);
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ### 4. 防止原型污染
345
+
346
+ ```javascript
347
+ // 验证数据时避免原型污染
348
+ const validator = new Validator({
349
+ removeAdditional: true, // 移除额外属性
350
+ useDefaults: false // 不自动填充默认值(如果不需要)
351
+ });
352
+ ```
353
+
354
+ ---
355
+
356
+ ## 错误处理
357
+
358
+ ### 1. 统一错误格式
359
+
360
+ **推荐的错误处理中间件**:
361
+ ```javascript
362
+ // Express 中间件
363
+ function validateMiddleware(schema) {
364
+ return (req, res, next) => {
365
+ const result = validate(schema, req.body);
366
+
367
+ if (!result.valid) {
368
+ return res.status(400).json({
369
+ code: 'VALIDATION_ERROR',
370
+ message: '请求数据验证失败',
371
+ errors: result.errors.map(err => ({
372
+ field: err.path,
373
+ message: err.message
374
+ }))
375
+ });
376
+ }
377
+
378
+ next();
379
+ };
380
+ }
381
+
382
+ // 使用
383
+ app.post('/api/user',
384
+ validateMiddleware(userSchema),
385
+ userController.create
386
+ );
387
+ ```
388
+
389
+ ---
390
+
391
+ ### 2. 友好的错误消息
392
+
393
+ **使用 label 和自定义消息**:
394
+ ```javascript
395
+ const schema = dsl({
396
+ username: 'string:3-32!'
397
+ .label('用户名')
398
+ .messages({
399
+ 'required': '{{#label}}不能为空',
400
+ 'min': '{{#label}}至少需要{{#limit}}个字符',
401
+ 'max': '{{#label}}最多{{#limit}}个字符',
402
+ 'pattern': '{{#label}}格式不正确'
403
+ }),
404
+
405
+ email: 'email!'
406
+ .label('邮箱地址')
407
+ .messages({
408
+ 'required': '请填写{{#label}}',
409
+ 'format': '{{#label}}格式不正确'
410
+ })
411
+ });
412
+ ```
413
+
414
+ **效果**:
415
+ ```text
416
+ ❌ 用户名不能为空
417
+ ❌ 用户名至少需要3个字符
418
+ ✅ 清晰明了,用户友好
419
+ ```
420
+
421
+ ---
422
+
423
+ ### 3. 处理外部异步校验错误
424
+
425
+ > `.custom()` 当前仅支持同步函数;涉及数据库、RPC、HTTP 等异步检查时,请在基础校验通过后于业务层单独执行。
426
+
427
+ ```javascript
428
+ const schema = dsl({
429
+ email: 'email!'.label('邮箱地址')
430
+ });
431
+
432
+ async function validateUser(data) {
433
+ const result = validate(schema, data);
434
+ if (!result.valid) return result;
435
+
436
+ try {
437
+ const exists = await checkEmailExists(data.email);
438
+ if (exists) {
439
+ return {
440
+ valid: false,
441
+ errors: [{ field: 'email', keyword: 'business', message: '邮箱已被占用' }]
442
+ };
443
+ }
444
+ } catch (error) {
445
+ console.error('Email check failed:', error);
446
+ }
447
+
448
+ return result;
449
+ }
450
+ ```
451
+
452
+ ---
453
+
454
+ ## 代码组织
455
+
456
+ ### 1. 集中管理 Schema
457
+
458
+ **推荐的项目结构**:
459
+ ```text
460
+ src/
461
+ ├── schemas/
462
+ │ ├── index.js # 导出所有 Schema
463
+ │ ├── user.schema.js # 用户相关 Schema
464
+ │ ├── post.schema.js # 文章相关 Schema
465
+ │ └── common.schema.js # 通用 Schema
466
+ ├── routes/
467
+ │ ├── user.routes.js
468
+ │ └── post.routes.js
469
+ └── controllers/
470
+ ```
471
+
472
+ **schemas/user.schema.js**:
473
+ ```javascript
474
+ const { dsl } = require('schema-dsl');
475
+
476
+ // 可复用的字段
477
+ const commonFields = {
478
+ username: dsl('string!').username().label('用户名'),
479
+ email: 'email!',
480
+ password: dsl('string!').password('strong').label('密码')
481
+ };
482
+
483
+ // 注册 Schema
484
+ exports.registerSchema = dsl({
485
+ ...commonFields,
486
+ confirmPassword: 'string!',
487
+ agreeTerms: 'boolean!'
488
+ });
489
+
490
+ // 登录 Schema
491
+ exports.loginSchema = dsl({
492
+ email: commonFields.email,
493
+ password: commonFields.password
494
+ });
495
+
496
+ // 更新 Schema
497
+ exports.updateSchema = dsl({
498
+ username: commonFields.username,
499
+ email: commonFields.email
500
+ // 不包含密码
501
+ });
502
+ ```
503
+
504
+ **schemas/index.js**:
505
+ ```javascript
506
+ const userSchemas = require('./user.schema');
507
+ const postSchemas = require('./post.schema');
508
+
509
+ module.exports = {
510
+ user: userSchemas,
511
+ post: postSchemas
512
+ };
513
+ ```
514
+
515
+ **routes/user.routes.js**:
516
+ ```javascript
517
+ const schemas = require('../schemas');
518
+ const { validate } = require('schema-dsl');
519
+
520
+ router.post('/register', (req, res) => {
521
+ const result = validate(schemas.user.registerSchema, req.body);
522
+ // ...
523
+ });
524
+ ```
525
+
526
+ ---
527
+
528
+ ### 2. Schema 复用
529
+
530
+ **使用 SchemaUtils**:
531
+ ```javascript
532
+ const { SchemaUtils, dsl } = require('schema-dsl');
533
+
534
+ // 创建可复用字段库
535
+ const fields = SchemaUtils.createLibrary({
536
+ email: () => 'email!',
537
+ phone: () => dsl('string!').phone('cn'),
538
+ password: () => dsl('string!').password('strong')
539
+ });
540
+
541
+ // 在多个 Schema 中复用
542
+ const registerSchema = dsl({
543
+ email: fields.email(),
544
+ password: fields.password(),
545
+ username: 'string:3-32!'
546
+ });
547
+
548
+ const profileSchema = dsl({
549
+ email: fields.email(),
550
+ phone: fields.phone(),
551
+ bio: 'string:500'
552
+ });
553
+ ```
554
+
555
+ ---
556
+
557
+ ## 生产环境建议
558
+
559
+ ### 1. 环境配置
560
+
561
+ ```javascript
562
+ // config/validator.js
563
+ const { Validator } = require('schema-dsl');
564
+
565
+ const config = {
566
+ development: {
567
+ verbose: true,
568
+ allErrors: true,
569
+ cache: false // ✅ 简写:关闭缓存,便于调试
570
+ },
571
+ production: {
572
+ verbose: false,
573
+ allErrors: false, // 只返回第一个错误
574
+ cache: {
575
+ enabled: true,
576
+ maxSize: 1000,
577
+ ttl: 60 * 60 * 1000
578
+ }
579
+ }
580
+ };
581
+
582
+ module.exports = new Validator(
583
+ config[process.env.NODE_ENV || 'development']
584
+ );
585
+ ```
586
+
587
+ ---
588
+
589
+ ### 2. 监控和日志
590
+
591
+ ```javascript
592
+ const validator = new Validator();
593
+
594
+ // 包装 validate 方法,添加监控
595
+ const originalValidate = validator.validate.bind(validator);
596
+ validator.validate = function(schema, data, options) {
597
+ const startTime = Date.now();
598
+ const result = originalValidate(schema, data, options);
599
+ const duration = Date.now() - startTime;
600
+
601
+ // 记录慢查询
602
+ if (duration > 100) {
603
+ console.warn(`Slow validation: ${duration}ms`);
604
+ }
605
+
606
+ // 记录验证失败
607
+ if (!result.valid) {
608
+ logger.info('Validation failed', {
609
+ errors: result.errors.length,
610
+ paths: result.errors.map(e => e.path)
611
+ });
612
+ }
613
+
614
+ return result;
615
+ };
616
+ ```
617
+
618
+ ---
619
+
620
+ ### 3. 健康检查
621
+
622
+ ```javascript
623
+ // routes/health.js
624
+ app.get('/health', (req, res) => {
625
+ const { validator } = require('../config/validator');
626
+
627
+ // 检查验证器是否正常
628
+ try {
629
+ const testSchema = dsl({ test: 'string!' });
630
+ const result = validator.validate(testSchema, { test: 'ok' });
631
+
632
+ if (!result.valid) {
633
+ throw new Error('Validator test failed');
634
+ }
635
+
636
+ res.json({
637
+ status: 'ok',
638
+ validator: 'operational',
639
+ cacheStats: validator.getCacheStats()
640
+ });
641
+ } catch (error) {
642
+ res.status(500).json({
643
+ status: 'error',
644
+ message: error.message
645
+ });
646
+ }
647
+ });
648
+ ```
649
+
650
+ ---
651
+
652
+ ### 4. 定期维护
653
+
654
+ ```javascript
655
+ // 定期清理缓存
656
+ const cron = require('node-cron');
657
+
658
+ // 每天凌晨清理一次
659
+ cron.schedule('0 0 * * *', () => {
660
+ validator.clearCache();
661
+ console.log('Validator cache cleared');
662
+ });
663
+
664
+ // 或者根据内存使用情况清理
665
+ setInterval(() => {
666
+ const memUsage = process.memoryUsage();
667
+ if (memUsage.heapUsed > 500 * 1024 * 1024) { // 超过 500MB
668
+ validator.clearCache();
669
+ }
670
+ }, 60000); // 每分钟检查一次
671
+ ```
672
+
673
+ ---
674
+
675
+ ## 性能基准参考
676
+
677
+ 缓存命中前后通常能显著降低重复编译开销,但绝对耗时会受到机器性能、Node 版本、schema 复杂度、数据规模和命中率影响,不建议把某组固定毫秒数当成通用基准。
678
+
679
+ **更稳定的结论**:
680
+
681
+ - 复用同一个 schema 对象或 `Validator` 实例,通常比每次请求都重新编译更快
682
+ - schema 越复杂、重复验证次数越多,缓存收益通常越明显
683
+ - 批量验证总耗时主要取决于单条 schema 复杂度和数据规模,不应使用固定毫秒数做容量承诺
684
+
685
+ 如需当前可复查的吞吐量对比,请以维护中的 benchmark 结果和 FAQ 中同步的性能数据为准。
686
+
687
+ ---
688
+
689
+ ## 总结
690
+
691
+ 遵循这些最佳实践,你的 schema-dsl 代码将具备:
692
+
693
+ ✅ **高性能** - 通过预编译和缓存
694
+ ✅ **高安全性** - 避免常见安全陷阱
695
+ ✅ **高可维护性** - 清晰的代码组织
696
+ ✅ **高可用性** - 完善的错误处理
697
+
698
+ ---
699
+
700
+ ## 延伸阅读
701
+
702
+ - [性能优化指南](performance-guide.md)
703
+ - [安全检查清单](security-checklist.md)
704
+ - [故障排查指南](troubleshooting.md)
705
+
706
+ ---
707
+
708
+ ## 对应示例文件
709
+
710
+ **示例入口**: [best-practices.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/best-practices.ts)
711
+ **说明**: 展示“简单字段用纯 DSL、复杂字段局部使用 Builder、字段库复用”的推荐组合,以及成功 / 失败两条验证路径。
712
+