code-abyss 1.6.15 → 1.7.0

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 (82) hide show
  1. package/bin/install.js +25 -4
  2. package/package.json +2 -2
  3. package/skills/SKILL.md +24 -16
  4. package/skills/domains/ai/SKILL.md +2 -2
  5. package/skills/domains/ai/prompt-and-eval.md +279 -0
  6. package/skills/domains/architecture/SKILL.md +2 -3
  7. package/skills/domains/architecture/security-arch.md +87 -0
  8. package/skills/domains/data-engineering/SKILL.md +188 -26
  9. package/skills/domains/development/SKILL.md +1 -4
  10. package/skills/domains/devops/SKILL.md +3 -5
  11. package/skills/domains/devops/performance.md +63 -0
  12. package/skills/domains/devops/testing.md +97 -0
  13. package/skills/domains/frontend-design/SKILL.md +12 -3
  14. package/skills/domains/frontend-design/claymorphism/SKILL.md +117 -0
  15. package/skills/domains/frontend-design/claymorphism/references/tokens.css +52 -0
  16. package/skills/domains/frontend-design/engineering.md +287 -0
  17. package/skills/domains/frontend-design/glassmorphism/SKILL.md +138 -0
  18. package/skills/domains/frontend-design/glassmorphism/references/tokens.css +32 -0
  19. package/skills/domains/frontend-design/liquid-glass/SKILL.md +135 -0
  20. package/skills/domains/frontend-design/liquid-glass/references/tokens.css +81 -0
  21. package/skills/domains/frontend-design/neubrutalism/SKILL.md +141 -0
  22. package/skills/domains/frontend-design/neubrutalism/references/tokens.css +44 -0
  23. package/skills/domains/infrastructure/SKILL.md +174 -34
  24. package/skills/domains/mobile/SKILL.md +211 -21
  25. package/skills/domains/orchestration/SKILL.md +1 -0
  26. package/skills/domains/security/SKILL.md +4 -6
  27. package/skills/domains/security/blue-team.md +57 -0
  28. package/skills/domains/security/red-team.md +54 -0
  29. package/skills/domains/security/threat-intel.md +50 -0
  30. package/skills/orchestration/multi-agent/SKILL.md +195 -46
  31. package/skills/run_skill.js +134 -0
  32. package/skills/tools/gen-docs/SKILL.md +6 -4
  33. package/skills/tools/gen-docs/scripts/doc_generator.js +349 -0
  34. package/skills/tools/verify-change/SKILL.md +8 -6
  35. package/skills/tools/verify-change/scripts/change_analyzer.js +270 -0
  36. package/skills/tools/verify-module/SKILL.md +6 -4
  37. package/skills/tools/verify-module/scripts/module_scanner.js +145 -0
  38. package/skills/tools/verify-quality/SKILL.md +5 -3
  39. package/skills/tools/verify-quality/scripts/quality_checker.js +276 -0
  40. package/skills/tools/verify-security/SKILL.md +7 -5
  41. package/skills/tools/verify-security/scripts/security_scanner.js +133 -0
  42. package/skills/domains/COVERAGE_PLAN.md +0 -232
  43. package/skills/domains/ai/model-evaluation.md +0 -790
  44. package/skills/domains/ai/prompt-engineering.md +0 -703
  45. package/skills/domains/architecture/compliance.md +0 -299
  46. package/skills/domains/architecture/data-security.md +0 -184
  47. package/skills/domains/data-engineering/data-pipeline.md +0 -762
  48. package/skills/domains/data-engineering/data-quality.md +0 -894
  49. package/skills/domains/data-engineering/stream-processing.md +0 -791
  50. package/skills/domains/development/dart.md +0 -963
  51. package/skills/domains/development/kotlin.md +0 -834
  52. package/skills/domains/development/php.md +0 -659
  53. package/skills/domains/development/swift.md +0 -755
  54. package/skills/domains/devops/e2e-testing.md +0 -914
  55. package/skills/domains/devops/performance-testing.md +0 -734
  56. package/skills/domains/devops/testing-strategy.md +0 -667
  57. package/skills/domains/frontend-design/build-tools.md +0 -743
  58. package/skills/domains/frontend-design/performance.md +0 -734
  59. package/skills/domains/frontend-design/testing.md +0 -699
  60. package/skills/domains/infrastructure/gitops.md +0 -735
  61. package/skills/domains/infrastructure/iac.md +0 -855
  62. package/skills/domains/infrastructure/kubernetes.md +0 -1018
  63. package/skills/domains/mobile/android-dev.md +0 -979
  64. package/skills/domains/mobile/cross-platform.md +0 -795
  65. package/skills/domains/mobile/ios-dev.md +0 -931
  66. package/skills/domains/security/secrets-management.md +0 -834
  67. package/skills/domains/security/supply-chain.md +0 -931
  68. package/skills/domains/security/threat-modeling.md +0 -828
  69. package/skills/run_skill.py +0 -88
  70. package/skills/tests/README.md +0 -225
  71. package/skills/tests/SUMMARY.md +0 -362
  72. package/skills/tests/__init__.py +0 -3
  73. package/skills/tests/test_change_analyzer.py +0 -558
  74. package/skills/tests/test_doc_generator.py +0 -538
  75. package/skills/tests/test_module_scanner.py +0 -376
  76. package/skills/tests/test_quality_checker.py +0 -516
  77. package/skills/tests/test_security_scanner.py +0 -426
  78. package/skills/tools/gen-docs/scripts/doc_generator.py +0 -491
  79. package/skills/tools/verify-change/scripts/change_analyzer.py +0 -529
  80. package/skills/tools/verify-module/scripts/module_scanner.py +0 -321
  81. package/skills/tools/verify-quality/scripts/quality_checker.py +0 -481
  82. package/skills/tools/verify-security/scripts/security_scanner.py +0 -368
@@ -1,734 +0,0 @@
1
- ---
2
- name: performance
3
- description: 前端性能优化技术。懒加载、代码分割、虚拟滚动、Web Vitals、性能监控。当用户提到性能优化、懒加载、代码分割、虚拟滚动、LCP、FID、CLS、性能指标时使用。
4
- ---
5
-
6
- # 🎨 ⚡ 性能优化 · Performance Optimization
7
-
8
- ## 性能指标 (Core Web Vitals)
9
-
10
- | 指标 | 含义 | 目标值 | 测量内容 |
11
- |------|------|--------|----------|
12
- | LCP | Largest Contentful Paint | < 2.5s | 最大内容绘制时间 |
13
- | FID | First Input Delay | < 100ms | 首次输入延迟 |
14
- | CLS | Cumulative Layout Shift | < 0.1 | 累积布局偏移 |
15
- | FCP | First Contentful Paint | < 1.8s | 首次内容绘制 |
16
- | TTI | Time to Interactive | < 3.8s | 可交互时间 |
17
- | TBT | Total Blocking Time | < 200ms | 总阻塞时间 |
18
-
19
- ## 性能优化决策树
20
-
21
- ```
22
- 性能问题?
23
-
24
- ├─ 加载慢
25
- │ ├─ Bundle 大 → 代码分割 + Tree Shaking
26
- │ ├─ 资源多 → 懒加载 + 预加载
27
- │ └─ 网络慢 → CDN + 压缩 + HTTP/2
28
-
29
- ├─ 渲染慢
30
- │ ├─ 列表长 → 虚拟滚动
31
- │ ├─ 重渲染 → React.memo + useMemo
32
- │ └─ 布局抖动 → 固定尺寸 + CSS优化
33
-
34
- └─ 交互慢
35
- ├─ JS 阻塞 → Web Worker + 时间切片
36
- ├─ 动画卡顿 → CSS动画 + requestAnimationFrame
37
- └─ 事件处理 → 防抖节流 + 事件委托
38
- ```
39
-
40
- ## 代码分割 (Code Splitting)
41
-
42
- ### React.lazy + Suspense
43
-
44
- ```typescript
45
- import { lazy, Suspense } from 'react'
46
-
47
- // 路由级别分割
48
- const Dashboard = lazy(() => import('./pages/Dashboard'))
49
- const Profile = lazy(() => import('./pages/Profile'))
50
- const Settings = lazy(() => import('./pages/Settings'))
51
-
52
- function App() {
53
- return (
54
- <Suspense fallback={<LoadingSpinner />}>
55
- <Routes>
56
- <Route path="/dashboard" element={<Dashboard />} />
57
- <Route path="/profile" element={<Profile />} />
58
- <Route path="/settings" element={<Settings />} />
59
- </Routes>
60
- </Suspense>
61
- )
62
- }
63
- ```
64
-
65
- ### 组件级别分割
66
-
67
- ```typescript
68
- import { lazy, Suspense } from 'react'
69
-
70
- // 重量级组件懒加载
71
- const HeavyChart = lazy(() => import('./components/HeavyChart'))
72
- const RichTextEditor = lazy(() => import('./components/RichTextEditor'))
73
-
74
- function Dashboard() {
75
- const [showChart, setShowChart] = useState(false)
76
-
77
- return (
78
- <div>
79
- <button onClick={() => setShowChart(true)}>Show Chart</button>
80
- {showChart && (
81
- <Suspense fallback={<div>Loading chart...</div>}>
82
- <HeavyChart />
83
- </Suspense>
84
- )}
85
- </div>
86
- )
87
- }
88
- ```
89
-
90
- ### 动态导入
91
-
92
- ```typescript
93
- // 条件加载
94
- async function loadFeature(featureName: string) {
95
- if (featureName === 'analytics') {
96
- const { Analytics } = await import('./features/Analytics')
97
- return Analytics
98
- } else if (featureName === 'reporting') {
99
- const { Reporting } = await import('./features/Reporting')
100
- return Reporting
101
- }
102
- }
103
-
104
- // 按需加载工具库
105
- async function processData(data: any[]) {
106
- const { default: _ } = await import('lodash-es')
107
- return _.groupBy(data, 'category')
108
- }
109
- ```
110
-
111
- ### Webpack 配置
112
-
113
- ```javascript
114
- // webpack.config.js
115
- module.exports = {
116
- optimization: {
117
- splitChunks: {
118
- chunks: 'all',
119
- cacheGroups: {
120
- // 第三方库单独打包
121
- vendor: {
122
- test: /[\\/]node_modules[\\/]/,
123
- name: 'vendors',
124
- priority: 10,
125
- },
126
- // 公共代码提取
127
- common: {
128
- minChunks: 2,
129
- priority: 5,
130
- reuseExistingChunk: true,
131
- },
132
- // React 相关单独打包
133
- react: {
134
- test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
135
- name: 'react',
136
- priority: 20,
137
- },
138
- },
139
- },
140
- },
141
- }
142
- ```
143
-
144
- ### Vite 配置
145
-
146
- ```typescript
147
- // vite.config.ts
148
- import { defineConfig } from 'vite'
149
-
150
- export default defineConfig({
151
- build: {
152
- rollupOptions: {
153
- output: {
154
- manualChunks: {
155
- 'react-vendor': ['react', 'react-dom'],
156
- 'router': ['react-router-dom'],
157
- 'ui': ['@mui/material', '@emotion/react'],
158
- },
159
- },
160
- },
161
- chunkSizeWarningLimit: 1000,
162
- },
163
- })
164
- ```
165
-
166
- ## 懒加载 (Lazy Loading)
167
-
168
- ### 图片懒加载
169
-
170
- ```typescript
171
- // 原生 loading 属性
172
- function ImageGallery({ images }: { images: string[] }) {
173
- return (
174
- <div>
175
- {images.map((src, i) => (
176
- <img
177
- key={i}
178
- src={src}
179
- loading="lazy"
180
- alt={`Image ${i}`}
181
- width="400"
182
- height="300"
183
- />
184
- ))}
185
- </div>
186
- )
187
- }
188
-
189
- // Intersection Observer
190
- import { useEffect, useRef, useState } from 'react'
191
-
192
- function LazyImage({ src, alt }: { src: string; alt: string }) {
193
- const [isLoaded, setIsLoaded] = useState(false)
194
- const imgRef = useRef<HTMLImageElement>(null)
195
-
196
- useEffect(() => {
197
- const observer = new IntersectionObserver(
198
- ([entry]) => {
199
- if (entry.isIntersecting) {
200
- setIsLoaded(true)
201
- observer.disconnect()
202
- }
203
- },
204
- { rootMargin: '50px' }
205
- )
206
-
207
- if (imgRef.current) {
208
- observer.observe(imgRef.current)
209
- }
210
-
211
- return () => observer.disconnect()
212
- }, [])
213
-
214
- return (
215
- <img
216
- ref={imgRef}
217
- src={isLoaded ? src : '/placeholder.jpg'}
218
- alt={alt}
219
- className={isLoaded ? 'loaded' : 'loading'}
220
- />
221
- )
222
- }
223
- ```
224
-
225
- ### 路由预加载
226
-
227
- ```typescript
228
- import { useEffect } from 'react'
229
- import { useLocation } from 'react-router-dom'
230
-
231
- // 预加载下一个可能的路由
232
- function useRoutePreload() {
233
- const location = useLocation()
234
-
235
- useEffect(() => {
236
- if (location.pathname === '/dashboard') {
237
- // 预加载 Profile 页面
238
- import('./pages/Profile')
239
- } else if (location.pathname === '/profile') {
240
- // 预加载 Settings 页面
241
- import('./pages/Settings')
242
- }
243
- }, [location])
244
- }
245
-
246
- // Link 悬停预加载
247
- function PreloadLink({ to, children }: { to: string; children: ReactNode }) {
248
- const handleMouseEnter = () => {
249
- if (to === '/dashboard') {
250
- import('./pages/Dashboard')
251
- }
252
- }
253
-
254
- return (
255
- <Link to={to} onMouseEnter={handleMouseEnter}>
256
- {children}
257
- </Link>
258
- )
259
- }
260
- ```
261
-
262
- ## 虚拟滚动 (Virtual Scrolling)
263
-
264
- ### react-window
265
-
266
- ```typescript
267
- import { FixedSizeList } from 'react-window'
268
-
269
- interface Item {
270
- id: string
271
- name: string
272
- }
273
-
274
- function VirtualList({ items }: { items: Item[] }) {
275
- const Row = ({ index, style }: { index: number; style: CSSProperties }) => (
276
- <div style={style}>
277
- {items[index].name}
278
- </div>
279
- )
280
-
281
- return (
282
- <FixedSizeList
283
- height={600}
284
- itemCount={items.length}
285
- itemSize={50}
286
- width="100%"
287
- >
288
- {Row}
289
- </FixedSizeList>
290
- )
291
- }
292
- ```
293
-
294
- ### 动态高度列表
295
-
296
- ```typescript
297
- import { VariableSizeList } from 'react-window'
298
-
299
- function DynamicList({ items }: { items: Item[] }) {
300
- const listRef = useRef<VariableSizeList>(null)
301
-
302
- // 计算每项高度
303
- const getItemSize = (index: number) => {
304
- return items[index].content.length > 100 ? 120 : 60
305
- }
306
-
307
- const Row = ({ index, style }: { index: number; style: CSSProperties }) => (
308
- <div style={style}>
309
- <h3>{items[index].title}</h3>
310
- <p>{items[index].content}</p>
311
- </div>
312
- )
313
-
314
- return (
315
- <VariableSizeList
316
- ref={listRef}
317
- height={600}
318
- itemCount={items.length}
319
- itemSize={getItemSize}
320
- width="100%"
321
- >
322
- {Row}
323
- </VariableSizeList>
324
- )
325
- }
326
- ```
327
-
328
- ### 虚拟网格
329
-
330
- ```typescript
331
- import { FixedSizeGrid } from 'react-window'
332
-
333
- function VirtualGrid({ items }: { items: Item[] }) {
334
- const COLUMN_COUNT = 4
335
- const ROW_COUNT = Math.ceil(items.length / COLUMN_COUNT)
336
-
337
- const Cell = ({ columnIndex, rowIndex, style }: any) => {
338
- const index = rowIndex * COLUMN_COUNT + columnIndex
339
- if (index >= items.length) return null
340
-
341
- return (
342
- <div style={style}>
343
- <img src={items[index].thumbnail} alt={items[index].name} />
344
- <p>{items[index].name}</p>
345
- </div>
346
- )
347
- }
348
-
349
- return (
350
- <FixedSizeGrid
351
- columnCount={COLUMN_COUNT}
352
- columnWidth={200}
353
- height={600}
354
- rowCount={ROW_COUNT}
355
- rowHeight={200}
356
- width={800}
357
- >
358
- {Cell}
359
- </FixedSizeGrid>
360
- )
361
- }
362
- ```
363
-
364
- ### 自定义虚拟滚动
365
-
366
- ```typescript
367
- import { useState, useEffect, useRef } from 'react'
368
-
369
- function useVirtualScroll<T>(
370
- items: T[],
371
- itemHeight: number,
372
- containerHeight: number
373
- ) {
374
- const [scrollTop, setScrollTop] = useState(0)
375
-
376
- const startIndex = Math.floor(scrollTop / itemHeight)
377
- const endIndex = Math.ceil((scrollTop + containerHeight) / itemHeight)
378
- const visibleItems = items.slice(startIndex, endIndex + 1)
379
-
380
- const totalHeight = items.length * itemHeight
381
- const offsetY = startIndex * itemHeight
382
-
383
- return {
384
- visibleItems,
385
- totalHeight,
386
- offsetY,
387
- onScroll: (e: React.UIEvent<HTMLDivElement>) => {
388
- setScrollTop(e.currentTarget.scrollTop)
389
- },
390
- }
391
- }
392
-
393
- function CustomVirtualList({ items }: { items: Item[] }) {
394
- const { visibleItems, totalHeight, offsetY, onScroll } = useVirtualScroll(
395
- items,
396
- 50,
397
- 600
398
- )
399
-
400
- return (
401
- <div style={{ height: 600, overflow: 'auto' }} onScroll={onScroll}>
402
- <div style={{ height: totalHeight, position: 'relative' }}>
403
- <div style={{ transform: `translateY(${offsetY}px)` }}>
404
- {visibleItems.map((item) => (
405
- <div key={item.id} style={{ height: 50 }}>
406
- {item.name}
407
- </div>
408
- ))}
409
- </div>
410
- </div>
411
- </div>
412
- )
413
- }
414
- ```
415
-
416
- ## React 性能优化
417
-
418
- ### React.memo
419
-
420
- ```typescript
421
- import { memo } from 'react'
422
-
423
- // 避免不必要的重渲染
424
- const ExpensiveComponent = memo(function ExpensiveComponent({
425
- data,
426
- onUpdate,
427
- }: {
428
- data: Data
429
- onUpdate: (id: string) => void
430
- }) {
431
- return <div>{/* 复杂渲染逻辑 */}</div>
432
- })
433
-
434
- // 自定义比较函数
435
- const CustomMemo = memo(
436
- function Component({ user }: { user: User }) {
437
- return <div>{user.name}</div>
438
- },
439
- (prevProps, nextProps) => {
440
- // 只在 user.id 变化时重渲染
441
- return prevProps.user.id === nextProps.user.id
442
- }
443
- )
444
- ```
445
-
446
- ### useMemo + useCallback
447
-
448
- ```typescript
449
- import { useMemo, useCallback } from 'react'
450
-
451
- function DataTable({ data, filter }: { data: Item[]; filter: string }) {
452
- // 缓存计算结果
453
- const filteredData = useMemo(() => {
454
- return data.filter((item) => item.name.includes(filter))
455
- }, [data, filter])
456
-
457
- const sortedData = useMemo(() => {
458
- return [...filteredData].sort((a, b) => a.name.localeCompare(b.name))
459
- }, [filteredData])
460
-
461
- // 缓存回调函数
462
- const handleClick = useCallback(
463
- (id: string) => {
464
- console.log('Clicked:', id)
465
- },
466
- []
467
- )
468
-
469
- return (
470
- <div>
471
- {sortedData.map((item) => (
472
- <Row key={item.id} item={item} onClick={handleClick} />
473
- ))}
474
- </div>
475
- )
476
- }
477
- ```
478
-
479
- ### 状态批量更新
480
-
481
- ```typescript
482
- import { unstable_batchedUpdates } from 'react-dom'
483
-
484
- // React 18 自动批处理
485
- function Component() {
486
- const [count, setCount] = useState(0)
487
- const [flag, setFlag] = useState(false)
488
-
489
- const handleClick = () => {
490
- // React 18 中自动批处理,只触发一次渲染
491
- setCount((c) => c + 1)
492
- setFlag((f) => !f)
493
- }
494
-
495
- // React 17 需要手动批处理
496
- const handleClickLegacy = () => {
497
- unstable_batchedUpdates(() => {
498
- setCount((c) => c + 1)
499
- setFlag((f) => !f)
500
- })
501
- }
502
- }
503
- ```
504
-
505
- ### 时间切片
506
-
507
- ```typescript
508
- // 使用 startTransition 标记低优先级更新
509
- import { startTransition, useState } from 'react'
510
-
511
- function SearchResults() {
512
- const [query, setQuery] = useState('')
513
- const [results, setResults] = useState<string[]>([])
514
-
515
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
516
- // 高优先级:立即更新输入框
517
- setQuery(e.target.value)
518
-
519
- // 低优先级:延迟更新搜索结果
520
- startTransition(() => {
521
- const filtered = heavySearch(e.target.value)
522
- setResults(filtered)
523
- })
524
- }
525
-
526
- return (
527
- <div>
528
- <input value={query} onChange={handleChange} />
529
- <ul>
530
- {results.map((r) => (
531
- <li key={r}>{r}</li>
532
- ))}
533
- </ul>
534
- </div>
535
- )
536
- }
537
- ```
538
-
539
- ## 资源优化
540
-
541
- ### 图片优化
542
-
543
- ```typescript
544
- // Next.js Image 组件
545
- import Image from 'next/image'
546
-
547
- function OptimizedImage() {
548
- return (
549
- <Image
550
- src="/hero.jpg"
551
- alt="Hero"
552
- width={1200}
553
- height={600}
554
- priority // LCP 图片优先加载
555
- placeholder="blur"
556
- blurDataURL="data:image/jpeg;base64,..."
557
- />
558
- )
559
- }
560
-
561
- // 响应式图片
562
- function ResponsiveImage() {
563
- return (
564
- <picture>
565
- <source
566
- srcSet="/hero.webp"
567
- type="image/webp"
568
- media="(min-width: 768px)"
569
- />
570
- <source srcSet="/hero-mobile.jpg" media="(max-width: 767px)" />
571
- <img src="/hero.jpg" alt="Hero" loading="lazy" />
572
- </picture>
573
- )
574
- }
575
- ```
576
-
577
- ### 字体优化
578
-
579
- ```css
580
- /* 字体预加载 */
581
- <link
582
- rel="preload"
583
- href="/fonts/inter.woff2"
584
- as="font"
585
- type="font/woff2"
586
- crossorigin
587
- />
588
-
589
- /* font-display 策略 */
590
- @font-face {
591
- font-family: 'Inter';
592
- src: url('/fonts/inter.woff2') format('woff2');
593
- font-display: swap; /* 立即显示备用字体 */
594
- font-weight: 400;
595
- }
596
-
597
- /* 可变字体 */
598
- @font-face {
599
- font-family: 'Inter Variable';
600
- src: url('/fonts/inter-variable.woff2') format('woff2-variations');
601
- font-weight: 100 900;
602
- }
603
- ```
604
-
605
- ### 预加载策略
606
-
607
- ```html
608
- <!-- DNS 预解析 -->
609
- <link rel="dns-prefetch" href="https://api.example.com" />
610
-
611
- <!-- 预连接 -->
612
- <link rel="preconnect" href="https://cdn.example.com" />
613
-
614
- <!-- 预加载关键资源 -->
615
- <link rel="preload" href="/critical.css" as="style" />
616
- <link rel="preload" href="/hero.jpg" as="image" />
617
-
618
- <!-- 预获取下一页资源 -->
619
- <link rel="prefetch" href="/next-page.js" />
620
-
621
- <!-- 预渲染下一页 -->
622
- <link rel="prerender" href="/next-page" />
623
- ```
624
-
625
- ## 性能监控
626
-
627
- ### Web Vitals 测量
628
-
629
- ```typescript
630
- import { onCLS, onFID, onLCP, onFCP, onTTFB } from 'web-vitals'
631
-
632
- function sendToAnalytics(metric: Metric) {
633
- const body = JSON.stringify(metric)
634
- const url = '/api/analytics'
635
-
636
- // 使用 sendBeacon 确保数据发送
637
- if (navigator.sendBeacon) {
638
- navigator.sendBeacon(url, body)
639
- } else {
640
- fetch(url, { body, method: 'POST', keepalive: true })
641
- }
642
- }
643
-
644
- // 监控所有指标
645
- onCLS(sendToAnalytics)
646
- onFID(sendToAnalytics)
647
- onLCP(sendToAnalytics)
648
- onFCP(sendToAnalytics)
649
- onTTFB(sendToAnalytics)
650
- ```
651
-
652
- ### Performance API
653
-
654
- ```typescript
655
- // 测量自定义指标
656
- function measureCustomMetric() {
657
- performance.mark('feature-start')
658
-
659
- // 执行操作
660
- doSomething()
661
-
662
- performance.mark('feature-end')
663
- performance.measure('feature-duration', 'feature-start', 'feature-end')
664
-
665
- const measure = performance.getEntriesByName('feature-duration')[0]
666
- console.log('Duration:', measure.duration)
667
- }
668
-
669
- // 监控资源加载
670
- const observer = new PerformanceObserver((list) => {
671
- for (const entry of list.getEntries()) {
672
- if (entry.entryType === 'resource') {
673
- console.log(`${entry.name}: ${entry.duration}ms`)
674
- }
675
- }
676
- })
677
-
678
- observer.observe({ entryTypes: ['resource', 'navigation'] })
679
- ```
680
-
681
- ### React DevTools Profiler
682
-
683
- ```typescript
684
- import { Profiler } from 'react'
685
-
686
- function onRenderCallback(
687
- id: string,
688
- phase: 'mount' | 'update',
689
- actualDuration: number,
690
- baseDuration: number,
691
- startTime: number,
692
- commitTime: number
693
- ) {
694
- console.log(`${id} (${phase}) took ${actualDuration}ms`)
695
- }
696
-
697
- function App() {
698
- return (
699
- <Profiler id="App" onRender={onRenderCallback}>
700
- <Dashboard />
701
- </Profiler>
702
- )
703
- }
704
- ```
705
-
706
- ## 最佳实践清单
707
-
708
- - ✅ 使用代码分割减小初始 bundle 大小
709
- - ✅ 懒加载非关键资源和路由
710
- - ✅ 虚拟滚动处理长列表
711
- - ✅ 使用 React.memo 避免不必要的重渲染
712
- - ✅ useMemo/useCallback 缓存计算和回调
713
- - ✅ 图片使用 WebP 格式 + 懒加载
714
- - ✅ 字体使用 font-display: swap
715
- - ✅ 预加载关键资源
716
- - ✅ 监控 Core Web Vitals
717
- - ✅ 使用 CDN 加速静态资源
718
- - ✅ 启用 Gzip/Brotli 压缩
719
- - ✅ 实施 HTTP/2 或 HTTP/3
720
-
721
- ## 工具清单
722
-
723
- | 工具 | 用途 |
724
- |------|------|
725
- | Lighthouse | 性能审计 |
726
- | WebPageTest | 详细性能分析 |
727
- | Chrome DevTools | 性能分析和调试 |
728
- | React DevTools Profiler | React 性能分析 |
729
- | webpack-bundle-analyzer | Bundle 分析 |
730
- | web-vitals | Core Web Vitals 监控 |
731
- | react-window | 虚拟滚动 |
732
- | Sentry | 性能监控 |
733
-
734
- ---