code-abyss 1.6.16 → 1.7.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/README.md +8 -6
- package/bin/install.js +59 -163
- package/bin/lib/ccline.js +82 -0
- package/bin/lib/utils.js +61 -0
- package/package.json +5 -2
- package/skills/SKILL.md +24 -16
- package/skills/domains/ai/SKILL.md +2 -2
- package/skills/domains/ai/prompt-and-eval.md +279 -0
- package/skills/domains/architecture/SKILL.md +2 -3
- package/skills/domains/architecture/security-arch.md +87 -0
- package/skills/domains/data-engineering/SKILL.md +188 -26
- package/skills/domains/development/SKILL.md +1 -4
- package/skills/domains/devops/SKILL.md +3 -5
- package/skills/domains/devops/performance.md +63 -0
- package/skills/domains/devops/testing.md +97 -0
- package/skills/domains/frontend-design/SKILL.md +12 -3
- package/skills/domains/frontend-design/claymorphism/SKILL.md +117 -0
- package/skills/domains/frontend-design/claymorphism/references/tokens.css +52 -0
- package/skills/domains/frontend-design/engineering.md +287 -0
- package/skills/domains/frontend-design/glassmorphism/SKILL.md +138 -0
- package/skills/domains/frontend-design/glassmorphism/references/tokens.css +32 -0
- package/skills/domains/frontend-design/liquid-glass/SKILL.md +135 -0
- package/skills/domains/frontend-design/liquid-glass/references/tokens.css +81 -0
- package/skills/domains/frontend-design/neubrutalism/SKILL.md +141 -0
- package/skills/domains/frontend-design/neubrutalism/references/tokens.css +44 -0
- package/skills/domains/infrastructure/SKILL.md +174 -34
- package/skills/domains/mobile/SKILL.md +211 -21
- package/skills/domains/orchestration/SKILL.md +1 -0
- package/skills/domains/security/SKILL.md +4 -6
- package/skills/domains/security/blue-team.md +57 -0
- package/skills/domains/security/red-team.md +54 -0
- package/skills/domains/security/threat-intel.md +50 -0
- package/skills/orchestration/multi-agent/SKILL.md +195 -46
- package/skills/run_skill.js +139 -0
- package/skills/tools/gen-docs/SKILL.md +6 -4
- package/skills/tools/gen-docs/scripts/doc_generator.js +363 -0
- package/skills/tools/lib/shared.js +98 -0
- package/skills/tools/verify-change/SKILL.md +8 -6
- package/skills/tools/verify-change/scripts/change_analyzer.js +289 -0
- package/skills/tools/verify-module/SKILL.md +6 -4
- package/skills/tools/verify-module/scripts/module_scanner.js +171 -0
- package/skills/tools/verify-quality/SKILL.md +5 -3
- package/skills/tools/verify-quality/scripts/quality_checker.js +337 -0
- package/skills/tools/verify-security/SKILL.md +7 -5
- package/skills/tools/verify-security/scripts/security_scanner.js +283 -0
- package/skills/__pycache__/run_skill.cpython-312.pyc +0 -0
- package/skills/domains/COVERAGE_PLAN.md +0 -232
- package/skills/domains/ai/model-evaluation.md +0 -790
- package/skills/domains/ai/prompt-engineering.md +0 -703
- package/skills/domains/architecture/compliance.md +0 -299
- package/skills/domains/architecture/data-security.md +0 -184
- package/skills/domains/data-engineering/data-pipeline.md +0 -762
- package/skills/domains/data-engineering/data-quality.md +0 -894
- package/skills/domains/data-engineering/stream-processing.md +0 -791
- package/skills/domains/development/dart.md +0 -963
- package/skills/domains/development/kotlin.md +0 -834
- package/skills/domains/development/php.md +0 -659
- package/skills/domains/development/swift.md +0 -755
- package/skills/domains/devops/e2e-testing.md +0 -914
- package/skills/domains/devops/performance-testing.md +0 -734
- package/skills/domains/devops/testing-strategy.md +0 -667
- package/skills/domains/frontend-design/build-tools.md +0 -743
- package/skills/domains/frontend-design/performance.md +0 -734
- package/skills/domains/frontend-design/testing.md +0 -699
- package/skills/domains/infrastructure/gitops.md +0 -735
- package/skills/domains/infrastructure/iac.md +0 -855
- package/skills/domains/infrastructure/kubernetes.md +0 -1018
- package/skills/domains/mobile/android-dev.md +0 -979
- package/skills/domains/mobile/cross-platform.md +0 -795
- package/skills/domains/mobile/ios-dev.md +0 -931
- package/skills/domains/security/secrets-management.md +0 -834
- package/skills/domains/security/supply-chain.md +0 -931
- package/skills/domains/security/threat-modeling.md +0 -828
- package/skills/run_skill.py +0 -153
- package/skills/tests/README.md +0 -225
- package/skills/tests/SUMMARY.md +0 -362
- package/skills/tests/__init__.py +0 -3
- package/skills/tests/__pycache__/test_change_analyzer.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_doc_generator.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_module_scanner.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_quality_checker.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_security_scanner.cpython-312.pyc +0 -0
- package/skills/tests/test_change_analyzer.py +0 -558
- package/skills/tests/test_doc_generator.py +0 -538
- package/skills/tests/test_module_scanner.py +0 -376
- package/skills/tests/test_quality_checker.py +0 -516
- package/skills/tests/test_security_scanner.py +0 -426
- package/skills/tools/gen-docs/scripts/__pycache__/doc_generator.cpython-312.pyc +0 -0
- package/skills/tools/gen-docs/scripts/doc_generator.py +0 -520
- package/skills/tools/verify-change/scripts/__pycache__/change_analyzer.cpython-312.pyc +0 -0
- package/skills/tools/verify-change/scripts/change_analyzer.py +0 -529
- package/skills/tools/verify-module/scripts/__pycache__/module_scanner.cpython-312.pyc +0 -0
- package/skills/tools/verify-module/scripts/module_scanner.py +0 -321
- package/skills/tools/verify-quality/scripts/__pycache__/quality_checker.cpython-312.pyc +0 -0
- package/skills/tools/verify-quality/scripts/quality_checker.py +0 -481
- package/skills/tools/verify-security/scripts/__pycache__/security_scanner.cpython-312.pyc +0 -0
- package/skills/tools/verify-security/scripts/security_scanner.py +0 -374
|
@@ -271,3 +271,66 @@ pool_size = (core_count * 2) + effective_spindle_count
|
|
|
271
271
|
- [ ] 网络无丢包
|
|
272
272
|
```
|
|
273
273
|
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 性能测试(源自 performance-testing)
|
|
277
|
+
|
|
278
|
+
### 测试类型
|
|
279
|
+
|
|
280
|
+
| 类型 | 用户数 | 持续时间 | 目标 |
|
|
281
|
+
|------|--------|----------|------|
|
|
282
|
+
| 负载测试 | 预期峰值 | 30min-2h | 验证性能指标 |
|
|
283
|
+
| 压力测试 | 超出峰值 | 1-3h | 找到崩溃点 |
|
|
284
|
+
| 浸泡测试 | 正常负载 | 8-72h | 检测内存泄漏 |
|
|
285
|
+
| 峰值测试 | 瞬间激增 | 短时间 | 测试弹性 |
|
|
286
|
+
|
|
287
|
+
### k6 核心模式
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
// 阶梯式负载
|
|
291
|
+
export const options = {
|
|
292
|
+
stages: [
|
|
293
|
+
{ duration: '2m', target: 100 },
|
|
294
|
+
{ duration: '5m', target: 100 },
|
|
295
|
+
{ duration: '2m', target: 0 },
|
|
296
|
+
],
|
|
297
|
+
thresholds: {
|
|
298
|
+
http_req_duration: ['p(95)<500'],
|
|
299
|
+
http_req_failed: ['rate<0.01'],
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 性能基准阈值
|
|
305
|
+
|
|
306
|
+
| 场景 | P95响应时间 | 错误率 | 吞吐量 |
|
|
307
|
+
|------|-------------|--------|--------|
|
|
308
|
+
| API查询 | <200ms | <0.1% | >1000 RPS |
|
|
309
|
+
| API写入 | <500ms | <0.5% | >500 RPS |
|
|
310
|
+
| 页面加载 | <2s | <1% | >100 RPS |
|
|
311
|
+
|
|
312
|
+
### 工具选型
|
|
313
|
+
|
|
314
|
+
| 工具 | 语言 | 适用场景 |
|
|
315
|
+
|------|------|----------|
|
|
316
|
+
| k6 | JavaScript | 现代化、DevOps集成、云原生 |
|
|
317
|
+
| JMeter | Java/GUI | 功能全面、插件丰富 |
|
|
318
|
+
| Gatling | Scala | 高性能、大规模测试 |
|
|
319
|
+
| Locust | Python | Python生态、分布式 |
|
|
320
|
+
|
|
321
|
+
### 渐进式测试流程
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
1. 基准测试 → 单用户建立基准
|
|
325
|
+
2. 负载测试 → 预期负载验证性能
|
|
326
|
+
3. 压力测试 → 超出负载找极限
|
|
327
|
+
4. 浸泡测试 → 长时间检测泄漏
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### 测试环境要求
|
|
331
|
+
|
|
332
|
+
- 独立环境,配置与生产一致
|
|
333
|
+
- 数据分布模拟真实:70%轻度 / 20%中度 / 10%重度用户
|
|
334
|
+
- 数据隔离:`user_${__VU}_${__ITER}`
|
|
335
|
+
- CI集成:k6 GitHub Action + 阈值门禁
|
|
336
|
+
|
|
@@ -184,3 +184,100 @@ AAA:
|
|
|
184
184
|
3. 重构: 优化代码,保持测试通过
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 测试策略(源自 testing-strategy)
|
|
190
|
+
|
|
191
|
+
### 测试金字塔比例
|
|
192
|
+
|
|
193
|
+
| 层级 | 占比 | 执行时间 | 成本 |
|
|
194
|
+
|------|------|----------|------|
|
|
195
|
+
| 单元测试 | 70% | <1s | 低 |
|
|
196
|
+
| 集成测试 | 20% | 1-10s | 中 |
|
|
197
|
+
| E2E测试 | 10% | 10s-5m | 高 |
|
|
198
|
+
|
|
199
|
+
### 测试左移 Checklist
|
|
200
|
+
|
|
201
|
+
```yaml
|
|
202
|
+
需求阶段: 可测试性评审、验收标准定义、测试用例设计
|
|
203
|
+
开发阶段: TDD、单元测试同步编写、代码审查包含测试
|
|
204
|
+
提交阶段: Pre-commit Hook、本地测试必过、静态分析
|
|
205
|
+
CI阶段: 自动化测试、覆盖率门禁、性能基准测试
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 契约测试要点
|
|
209
|
+
|
|
210
|
+
- 消费者驱动契约 (CDC):Consumer 定义期望 → Provider 验证契约
|
|
211
|
+
- 工具:Pact(多语言)、Spring Cloud Contract(Java)
|
|
212
|
+
- 核心:Provider API <-> Contract <-> Consumer,双方独立验证
|
|
213
|
+
|
|
214
|
+
### 覆盖率策略
|
|
215
|
+
|
|
216
|
+
```yaml
|
|
217
|
+
类型: 行覆盖率、分支覆盖率、函数覆盖率、语句覆盖率
|
|
218
|
+
门禁: 全局 ≥80%,核心模块 ≥90%
|
|
219
|
+
排除: tests/、migrations/、__init__.py、config 文件
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 变异测试
|
|
223
|
+
|
|
224
|
+
- 修改源码(变异体)验证测试是否能捕获
|
|
225
|
+
- 工具:Stryker (JS)、Pitest (Java)
|
|
226
|
+
- 阈值:high 80% / low 60% / break 50%
|
|
227
|
+
|
|
228
|
+
### 测试最佳实践
|
|
229
|
+
|
|
230
|
+
- AAA 模式:Arrange → Act → Assert
|
|
231
|
+
- 命名:`should [预期行为] when [条件]`
|
|
232
|
+
- 单一职责:每个测试只验证一件事
|
|
233
|
+
- 数据隔离:Fixture/Factory 模式,每测试独立实例
|
|
234
|
+
- 并行执行:Jest `maxWorkers: '50%'`、pytest `-n auto`
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## E2E 测试(源自 e2e-testing)
|
|
239
|
+
|
|
240
|
+
### Playwright vs Cypress
|
|
241
|
+
|
|
242
|
+
| 特性 | Playwright | Cypress |
|
|
243
|
+
|------|-----------|---------|
|
|
244
|
+
| 多浏览器 | Chromium/Firefox/WebKit | Chromium/Firefox/Edge |
|
|
245
|
+
| 多标签页/iframe | 原生支持 | 有限 |
|
|
246
|
+
| 并行执行 | 原生支持 | 需付费 |
|
|
247
|
+
| 调试体验 | 一般 | 优秀 |
|
|
248
|
+
|
|
249
|
+
### 选择器优先级
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
1. data-testid (推荐)
|
|
253
|
+
2. role + accessible name
|
|
254
|
+
3. 稳定的 class/id
|
|
255
|
+
4. 文本内容 (谨慎)
|
|
256
|
+
5. CSS/XPath (避免)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### E2E Checklist
|
|
260
|
+
|
|
261
|
+
```yaml
|
|
262
|
+
架构:
|
|
263
|
+
- 页面对象模式 (POM) 封装页面操作
|
|
264
|
+
- 测试独立性:通过 API 准备数据,不依赖其他测试
|
|
265
|
+
- 智能等待:waitForSelector/waitForResponse,禁止 waitForTimeout
|
|
266
|
+
|
|
267
|
+
网络:
|
|
268
|
+
- Mock API:page.route() / cy.intercept() 隔离后端
|
|
269
|
+
- 等待响应:waitForResponse 确认数据加载
|
|
270
|
+
|
|
271
|
+
可视化回归:
|
|
272
|
+
- Playwright: toHaveScreenshot() + mask 动态内容
|
|
273
|
+
- Percy/Chromatic: 云端截图对比
|
|
274
|
+
|
|
275
|
+
认证:
|
|
276
|
+
- Playwright: storageState 复用登录态
|
|
277
|
+
- Cypress: cy.session() 缓存会话
|
|
278
|
+
|
|
279
|
+
CI集成:
|
|
280
|
+
- retries: CI 环境 2 次重试
|
|
281
|
+
- artifacts: 失败时保存截图/视频/trace
|
|
282
|
+
```
|
|
283
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: frontend-design
|
|
3
3
|
description: 前端设计美学秘典。UI美学、组件模式、UX原则。当魔尊需要前端设计、UI/UX指导、组件设计时使用。
|
|
4
|
+
license: MIT
|
|
4
5
|
user-invocable: true
|
|
5
6
|
---
|
|
6
7
|
|
|
@@ -14,9 +15,16 @@ user-invocable: true
|
|
|
14
15
|
| 组件模式 | [component-patterns.md](component-patterns.md) | 布局模板、响应式设计、交互模式、动画、表单设计、卡片组件、导航模式 |
|
|
15
16
|
| UX原则 | [ux-principles.md](ux-principles.md) | 可用性、无障碍、信息架构、用户流程、加载体验、反馈设计、移动端优先、性能感知 |
|
|
16
17
|
| 状态管理 | [state-management.md](state-management.md) | Redux、Zustand、Jotai、Recoil、Context API、性能优化 |
|
|
17
|
-
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
| 前端工程化 | [engineering.md](engineering.md) | 性能优化、Web Vitals、测试(Vitest/Playwright)、构建工具(Vite/Webpack) |
|
|
19
|
+
|
|
20
|
+
## 设计风格系统
|
|
21
|
+
|
|
22
|
+
| 风格 | 文档 | 涵盖 |
|
|
23
|
+
|------|------|------|
|
|
24
|
+
| Claymorphism | [claymorphism/SKILL.md](claymorphism/SKILL.md) | 软陶风格、大圆角、双内阴影、偏移外阴影、暗色模式 |
|
|
25
|
+
| Glassmorphism | [glassmorphism/SKILL.md](glassmorphism/SKILL.md) | 毛玻璃风格、backdrop-filter、透明度、模糊层级 |
|
|
26
|
+
| Neubrutalism | [neubrutalism/SKILL.md](neubrutalism/SKILL.md) | 新粗野主义、粗边框、实色偏移阴影、高饱和色彩 |
|
|
27
|
+
| Liquid Glass | [liquid-glass/SKILL.md](liquid-glass/SKILL.md) | Apple 液态玻璃、半透明深度感知、弹簧动画、环境响应 |
|
|
20
28
|
|
|
21
29
|
## 使用场景
|
|
22
30
|
|
|
@@ -26,3 +34,4 @@ user-invocable: true
|
|
|
26
34
|
- 无障碍改进
|
|
27
35
|
- 响应式布局
|
|
28
36
|
- 交互动画设计
|
|
37
|
+
- 设计风格选型(Claymorphism / Glassmorphism / Neubrutalism / Liquid Glass)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claymorphism
|
|
3
|
+
description: Claymorphism design system skill. Use when building soft, puffy, clay-like UI components with large radii, dual inner shadows, and offset outer shadows.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Claymorphism Design Spec
|
|
8
|
+
|
|
9
|
+
## 3 Core Elements
|
|
10
|
+
|
|
11
|
+
1. **Large Radius** — Generous `border-radius` (20–50px) for a puffy, inflated look
|
|
12
|
+
2. **Dual Inner Shadows** — Light inset from top-left + dark inset from bottom-right to simulate 3D clay surface
|
|
13
|
+
3. **Offset Outer Shadow** — Directional `box-shadow` offset (not centered) to ground the element
|
|
14
|
+
|
|
15
|
+
## CSS Tokens
|
|
16
|
+
|
|
17
|
+
Reference: [references/tokens.css](references/tokens.css)
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
@import 'references/tokens.css';
|
|
21
|
+
|
|
22
|
+
.clay-card {
|
|
23
|
+
background: var(--clay-bg-card);
|
|
24
|
+
border-radius: var(--clay-radius-lg);
|
|
25
|
+
box-shadow: var(--clay-shadow);
|
|
26
|
+
color: var(--clay-text);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Component Examples
|
|
31
|
+
|
|
32
|
+
### Card
|
|
33
|
+
```css
|
|
34
|
+
.clay-card {
|
|
35
|
+
background: var(--clay-bg-card);
|
|
36
|
+
border-radius: var(--clay-radius-lg);
|
|
37
|
+
box-shadow: var(--clay-shadow);
|
|
38
|
+
padding: 1.5rem;
|
|
39
|
+
color: var(--clay-text);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Button
|
|
44
|
+
```css
|
|
45
|
+
.clay-btn {
|
|
46
|
+
background: var(--clay-bg-button);
|
|
47
|
+
border: none;
|
|
48
|
+
border-radius: var(--clay-radius-pill);
|
|
49
|
+
box-shadow: var(--clay-shadow);
|
|
50
|
+
padding: 0.75rem 1.5rem;
|
|
51
|
+
color: var(--clay-text);
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
transition: box-shadow 0.2s;
|
|
54
|
+
}
|
|
55
|
+
.clay-btn:hover {
|
|
56
|
+
box-shadow: var(--clay-shadow-elevated);
|
|
57
|
+
}
|
|
58
|
+
.clay-btn:active {
|
|
59
|
+
box-shadow: var(--clay-shadow-pressed);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Input
|
|
64
|
+
```css
|
|
65
|
+
.clay-input {
|
|
66
|
+
background: var(--clay-bg);
|
|
67
|
+
border: none;
|
|
68
|
+
border-radius: var(--clay-radius);
|
|
69
|
+
box-shadow: var(--clay-shadow-pressed);
|
|
70
|
+
padding: 0.75rem 1rem;
|
|
71
|
+
color: var(--clay-text);
|
|
72
|
+
}
|
|
73
|
+
.clay-input:focus {
|
|
74
|
+
outline: 2px solid var(--clay-accent);
|
|
75
|
+
outline-offset: 2px;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Toggle
|
|
80
|
+
```css
|
|
81
|
+
.clay-toggle {
|
|
82
|
+
width: 56px;
|
|
83
|
+
height: 30px;
|
|
84
|
+
background: var(--clay-bg-card);
|
|
85
|
+
border-radius: var(--clay-radius-pill);
|
|
86
|
+
box-shadow: var(--clay-shadow-pressed);
|
|
87
|
+
}
|
|
88
|
+
.clay-toggle-knob {
|
|
89
|
+
width: 24px;
|
|
90
|
+
height: 24px;
|
|
91
|
+
background: var(--clay-bg);
|
|
92
|
+
border-radius: 50%;
|
|
93
|
+
box-shadow: var(--clay-shadow);
|
|
94
|
+
transition: transform 0.2s;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Dark Mode Notes
|
|
99
|
+
|
|
100
|
+
- Dark mode reduces inner highlight intensity (`rgba(255,255,255,0.05)` vs `0.6`) to avoid glowing artifacts
|
|
101
|
+
- Outer shadow opacity increases to maintain depth on dark backgrounds
|
|
102
|
+
- Background colors shift to warm dark tones — avoid pure black to preserve the clay feel
|
|
103
|
+
- All dark tokens are defined in `[data-theme="dark"]` in `tokens.css`
|
|
104
|
+
|
|
105
|
+
## Accessibility Notes
|
|
106
|
+
|
|
107
|
+
- Ensure **contrast ratio ≥ 4.5:1** for text — clay backgrounds are muted, verify against `--clay-text`
|
|
108
|
+
- Provide visible `:focus` outlines since clay shadows alone don't indicate focus
|
|
109
|
+
- Use `prefers-contrast: more` to flatten shadows and increase text contrast
|
|
110
|
+
|
|
111
|
+
```css
|
|
112
|
+
@media (prefers-contrast: more) {
|
|
113
|
+
.clay-card {
|
|
114
|
+
box-shadow: 0 0 0 2px var(--clay-text);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
/* Backgrounds — soft, muted pastels */
|
|
3
|
+
--clay-bg: #f0e6db;
|
|
4
|
+
--clay-bg-card: #e8ddd4;
|
|
5
|
+
--clay-bg-button: #d4c4b0;
|
|
6
|
+
--clay-accent: #c4a882;
|
|
7
|
+
|
|
8
|
+
/* Radius — large, rounded, puffy */
|
|
9
|
+
--clay-radius: 20px;
|
|
10
|
+
--clay-radius-lg: 32px;
|
|
11
|
+
--clay-radius-pill: 50px;
|
|
12
|
+
|
|
13
|
+
/* Shadows — dual inner + offset outer */
|
|
14
|
+
--clay-shadow:
|
|
15
|
+
8px 8px 16px rgba(0, 0, 0, 0.12),
|
|
16
|
+
inset -4px -4px 8px rgba(0, 0, 0, 0.08),
|
|
17
|
+
inset 4px 4px 8px rgba(255, 255, 255, 0.6);
|
|
18
|
+
--clay-shadow-elevated:
|
|
19
|
+
12px 12px 24px rgba(0, 0, 0, 0.15),
|
|
20
|
+
inset -6px -6px 12px rgba(0, 0, 0, 0.1),
|
|
21
|
+
inset 6px 6px 12px rgba(255, 255, 255, 0.7);
|
|
22
|
+
--clay-shadow-pressed:
|
|
23
|
+
2px 2px 6px rgba(0, 0, 0, 0.1),
|
|
24
|
+
inset -6px -6px 12px rgba(0, 0, 0, 0.12),
|
|
25
|
+
inset 6px 6px 12px rgba(255, 255, 255, 0.5);
|
|
26
|
+
|
|
27
|
+
/* Text */
|
|
28
|
+
--clay-text: #4a3f35;
|
|
29
|
+
--clay-text-muted: #8a7e72;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Dark mode */
|
|
33
|
+
[data-theme="dark"] {
|
|
34
|
+
--clay-bg: #2a2520;
|
|
35
|
+
--clay-bg-card: #352f28;
|
|
36
|
+
--clay-bg-button: #443c33;
|
|
37
|
+
--clay-accent: #6b5d4f;
|
|
38
|
+
--clay-shadow:
|
|
39
|
+
8px 8px 16px rgba(0, 0, 0, 0.35),
|
|
40
|
+
inset -4px -4px 8px rgba(0, 0, 0, 0.25),
|
|
41
|
+
inset 4px 4px 8px rgba(255, 255, 255, 0.05);
|
|
42
|
+
--clay-shadow-elevated:
|
|
43
|
+
12px 12px 24px rgba(0, 0, 0, 0.4),
|
|
44
|
+
inset -6px -6px 12px rgba(0, 0, 0, 0.3),
|
|
45
|
+
inset 6px 6px 12px rgba(255, 255, 255, 0.06);
|
|
46
|
+
--clay-shadow-pressed:
|
|
47
|
+
2px 2px 6px rgba(0, 0, 0, 0.3),
|
|
48
|
+
inset -6px -6px 12px rgba(0, 0, 0, 0.35),
|
|
49
|
+
inset 6px 6px 12px rgba(255, 255, 255, 0.04);
|
|
50
|
+
--clay-text: #d4c8bc;
|
|
51
|
+
--clay-text-muted: #8a7e72;
|
|
52
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-engineering
|
|
3
|
+
description: 前端工程化。性能优化(Web Vitals、懒加载、虚拟滚动)、测试(Vitest、Playwright、MSW)、构建工具(Vite、Webpack、esbuild)。当用户提到性能优化、前端测试、构建工具、代码分割时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 前端工程化 · Frontend Engineering
|
|
7
|
+
|
|
8
|
+
## 一、性能优化
|
|
9
|
+
|
|
10
|
+
### Core Web Vitals
|
|
11
|
+
|
|
12
|
+
| 指标 | 含义 | 目标值 |
|
|
13
|
+
|------|------|--------|
|
|
14
|
+
| LCP | Largest Contentful Paint | < 2.5s |
|
|
15
|
+
| FID | First Input Delay | < 100ms |
|
|
16
|
+
| CLS | Cumulative Layout Shift | < 0.1 |
|
|
17
|
+
| FCP | First Contentful Paint | < 1.8s |
|
|
18
|
+
| TTI | Time to Interactive | < 3.8s |
|
|
19
|
+
|
|
20
|
+
### 性能决策树
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
加载慢 → Bundle 大?代码分割 + Tree Shaking | 资源多?懒加载 + 预加载 | 网络慢?CDN + 压缩
|
|
24
|
+
渲染慢 → 列表长?虚拟滚动 | 重渲染?React.memo + useMemo | 布局抖动?固定尺寸
|
|
25
|
+
交互慢 → JS 阻塞?Web Worker + startTransition | 动画卡顿?CSS 动画 + rAF
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 代码分割
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// 路由级别 — React.lazy + Suspense
|
|
32
|
+
const Dashboard = lazy(() => import('./pages/Dashboard'))
|
|
33
|
+
|
|
34
|
+
// 组件级别 — 按需加载重量级组件
|
|
35
|
+
const HeavyChart = lazy(() => import('./components/HeavyChart'))
|
|
36
|
+
|
|
37
|
+
// Vite manualChunks
|
|
38
|
+
export default defineConfig({
|
|
39
|
+
build: {
|
|
40
|
+
rollupOptions: {
|
|
41
|
+
output: {
|
|
42
|
+
manualChunks: {
|
|
43
|
+
'react-vendor': ['react', 'react-dom'],
|
|
44
|
+
'ui': ['@mui/material'],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 虚拟滚动
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { FixedSizeList } from 'react-window'
|
|
56
|
+
|
|
57
|
+
function VirtualList({ items }: { items: Item[] }) {
|
|
58
|
+
return (
|
|
59
|
+
<FixedSizeList height={600} itemCount={items.length} itemSize={50} width="100%">
|
|
60
|
+
{({ index, style }) => <div style={style}>{items[index].name}</div>}
|
|
61
|
+
</FixedSizeList>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### React 性能要点
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// memo 避免重渲染
|
|
70
|
+
const Row = memo(function Row({ item, onClick }: Props) {
|
|
71
|
+
return <div onClick={() => onClick(item.id)}>{item.name}</div>
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// useMemo 缓存计算 + useCallback 缓存回调
|
|
75
|
+
const filtered = useMemo(() => data.filter(x => x.name.includes(q)), [data, q])
|
|
76
|
+
const handleClick = useCallback((id: string) => select(id), [])
|
|
77
|
+
|
|
78
|
+
// startTransition 低优先级更新
|
|
79
|
+
startTransition(() => setResults(heavySearch(query)))
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 资源优化 Checklist
|
|
83
|
+
|
|
84
|
+
- 图片:WebP 格式 + `loading="lazy"` + 响应式 `<picture>`
|
|
85
|
+
- 字体:`font-display: swap` + `preload` woff2
|
|
86
|
+
- 预加载:`dns-prefetch` → `preconnect` → `preload` → `prefetch`
|
|
87
|
+
- 压缩:Gzip/Brotli + HTTP/2
|
|
88
|
+
|
|
89
|
+
### 性能监控
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { onCLS, onFID, onLCP } from 'web-vitals'
|
|
93
|
+
onCLS(sendToAnalytics)
|
|
94
|
+
onFID(sendToAnalytics)
|
|
95
|
+
onLCP(sendToAnalytics)
|
|
96
|
+
|
|
97
|
+
// 自定义指标
|
|
98
|
+
performance.mark('start')
|
|
99
|
+
doWork()
|
|
100
|
+
performance.mark('end')
|
|
101
|
+
performance.measure('work', 'start', 'end')
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 二、测试
|
|
105
|
+
|
|
106
|
+
### 测试金字塔
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
/\ E2E (10%) — Playwright
|
|
110
|
+
/--\ 集成 (20%) — Testing Library + MSW
|
|
111
|
+
/----\ 单元 (70%) — Vitest
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Vitest 配置
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// vitest.config.ts
|
|
118
|
+
export default defineConfig({
|
|
119
|
+
test: {
|
|
120
|
+
globals: true,
|
|
121
|
+
environment: 'jsdom',
|
|
122
|
+
setupFiles: './src/test/setup.ts',
|
|
123
|
+
coverage: {
|
|
124
|
+
provider: 'v8',
|
|
125
|
+
thresholds: { lines: 80, functions: 80, branches: 75 },
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 单元测试
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
describe('formatCurrency', () => {
|
|
135
|
+
it('formats number', () => expect(formatCurrency(1234.56)).toBe('$1,234.56'))
|
|
136
|
+
it('handles zero', () => expect(formatCurrency(0)).toBe('$0.00'))
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 组件测试
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { render, screen, fireEvent } from '@testing-library/react'
|
|
144
|
+
|
|
145
|
+
it('calls onClick', () => {
|
|
146
|
+
const fn = vi.fn()
|
|
147
|
+
render(<Button onClick={fn}>Click</Button>)
|
|
148
|
+
fireEvent.click(screen.getByText('Click'))
|
|
149
|
+
expect(fn).toHaveBeenCalledTimes(1)
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### MSW Mock
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { http, HttpResponse } from 'msw'
|
|
157
|
+
import { setupServer } from 'msw/node'
|
|
158
|
+
|
|
159
|
+
const server = setupServer(
|
|
160
|
+
http.get('/api/users/:id', ({ params }) =>
|
|
161
|
+
HttpResponse.json({ id: params.id, name: 'John' })
|
|
162
|
+
),
|
|
163
|
+
)
|
|
164
|
+
beforeAll(() => server.listen())
|
|
165
|
+
afterEach(() => server.resetHandlers())
|
|
166
|
+
afterAll(() => server.close())
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Playwright E2E
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// playwright.config.ts 核心
|
|
173
|
+
export default defineConfig({
|
|
174
|
+
testDir: './e2e',
|
|
175
|
+
use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry' },
|
|
176
|
+
webServer: { command: 'npm run dev', url: 'http://localhost:3000' },
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// Page Object 模式
|
|
180
|
+
class LoginPage {
|
|
181
|
+
constructor(private page: Page) {}
|
|
182
|
+
async login(email: string, password: string) {
|
|
183
|
+
await this.page.fill('[name="email"]', email)
|
|
184
|
+
await this.page.fill('[name="password"]', password)
|
|
185
|
+
await this.page.click('[type="submit"]')
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 测试 Checklist
|
|
191
|
+
|
|
192
|
+
- 遵循 AAA 模式(Arrange / Act / Assert)
|
|
193
|
+
- 测试行为而非实现
|
|
194
|
+
- Mock 外部依赖(API、时间)
|
|
195
|
+
- 测试边界条件和错误路径
|
|
196
|
+
- CI 中自动运行 + 覆盖率门禁 80%+
|
|
197
|
+
|
|
198
|
+
## 三、构建工具
|
|
199
|
+
|
|
200
|
+
### 选型决策
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
新项目 React/Vue → Vite | Next.js → Turbopack | 零配置 → Parcel
|
|
204
|
+
库开发 → Rollup / esbuild
|
|
205
|
+
老项目复杂配置 → 保持 Webpack | 可迁移 → Vite
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 工具对比
|
|
209
|
+
|
|
210
|
+
| 工具 | 冷启动 | HMR | 生产构建 | 生态 |
|
|
211
|
+
|------|--------|-----|----------|------|
|
|
212
|
+
| Vite | < 1s | < 100ms | 10-30s | 成熟 |
|
|
213
|
+
| Webpack | 10-30s | 1-3s | 30-60s | 最丰富 |
|
|
214
|
+
| Turbopack | < 1s | < 100ms | 10-20s | 新兴 |
|
|
215
|
+
| esbuild | < 1s | N/A | 5-10s | 基础 |
|
|
216
|
+
|
|
217
|
+
### Vite 核心配置
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
export default defineConfig({
|
|
221
|
+
plugins: [react()],
|
|
222
|
+
resolve: { alias: { '@': path.resolve(__dirname, './src') } },
|
|
223
|
+
server: {
|
|
224
|
+
port: 3000,
|
|
225
|
+
proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } },
|
|
226
|
+
},
|
|
227
|
+
build: {
|
|
228
|
+
minify: 'terser',
|
|
229
|
+
terserOptions: { compress: { drop_console: true } },
|
|
230
|
+
rollupOptions: {
|
|
231
|
+
output: {
|
|
232
|
+
manualChunks: { 'react-vendor': ['react', 'react-dom'] },
|
|
233
|
+
entryFileNames: 'assets/[name].[hash].js',
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
optimizeDeps: { include: ['react', 'react-dom'] },
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Webpack 生产优化要点
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
optimization: {
|
|
245
|
+
minimize: true,
|
|
246
|
+
minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
|
|
247
|
+
splitChunks: {
|
|
248
|
+
chunks: 'all',
|
|
249
|
+
cacheGroups: {
|
|
250
|
+
react: { test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, priority: 20 },
|
|
251
|
+
vendor: { test: /[\\/]node_modules[\\/]/, priority: 10 },
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
runtimeChunk: 'single',
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Webpack → Vite 迁移要点
|
|
259
|
+
|
|
260
|
+
1. `npm install -D vite @vitejs/plugin-react`
|
|
261
|
+
2. `index.html` 移到根目录,加 `<script type="module" src="/src/main.tsx">`
|
|
262
|
+
3. `REACT_APP_*` → `VITE_*`,`process.env` → `import.meta.env`
|
|
263
|
+
4. `require()` → `import`
|
|
264
|
+
|
|
265
|
+
### 构建 Checklist
|
|
266
|
+
|
|
267
|
+
- 合理代码分割(路由级 + 第三方库分组)
|
|
268
|
+
- Tree Shaking + 压缩(terser / esbuild)
|
|
269
|
+
- 文件名哈希实现长期缓存
|
|
270
|
+
- Source map 仅 dev 或 hidden
|
|
271
|
+
- 定期 `webpack-bundle-analyzer` / `rollup-plugin-visualizer` 审计
|
|
272
|
+
- CI 缓存 `node_modules` + 构建产物
|
|
273
|
+
|
|
274
|
+
## 工具速查
|
|
275
|
+
|
|
276
|
+
| 类别 | 推荐工具 |
|
|
277
|
+
|------|----------|
|
|
278
|
+
| 构建 | Vite (新项目) / Webpack (复杂项目) |
|
|
279
|
+
| 单元测试 | Vitest |
|
|
280
|
+
| 组件测试 | Testing Library |
|
|
281
|
+
| E2E | Playwright |
|
|
282
|
+
| API Mock | MSW |
|
|
283
|
+
| 性能监控 | web-vitals + Lighthouse |
|
|
284
|
+
| Bundle 分析 | webpack-bundle-analyzer / rollup-plugin-visualizer |
|
|
285
|
+
| 视觉回归 | Playwright screenshots / Chromatic |
|
|
286
|
+
|
|
287
|
+
---
|