--- description: > 性能优化规范与审计流程。当用户讨论性能优化、报告页面慢、加载卡、 包体积大、Swoole 调优、数据库查询优化、缓存策略或 Core Web Vitals 时激活。 alwaysApply: false --- # Performance Standards & Audit > 本文件同时包含性能**诊断流程**和**编码规范**。 > 完整审计工作流见:Read `.cursor/skills/performance-audit/SKILL.md` ## 诊断流程(用户报告性能问题时先走这里) ### 1. 性能基线 收集当前数据(前端 `npm run build` + `du -sh dist/`,后端路由数量)。 ### 2. 四维度审计 **前端渲染** — 不必要重渲染、大列表虚拟化、图片压缩、按需导入 **网络请求** — 瀑布式请求、Redis 缓存、Axios 拦截、Nginx 缓存 **打包体积** — 大依赖、dynamic import、tree-shaking、barrel exports **数据库查询** — N+1、缺少索引、未限制字段、未分页 ### 3. 输出优化建议 按影响力排序,每个建议包含:预期收益 + 实施难度 + 具体代码。 ### 诊断验证 - [ ] 优化前后有可量化对比 - [ ] 未引入功能回归 - [ ] Core Web Vitals 达标 --- ## 性能指标目标 | 指标 | 目标 | 说明 | |------|------|------| | FCP | < 1.5s | 首次内容绘制 | | LCP | < 2.5s | 最大内容绘制 | | INP | < 200ms | 交互到下一帧 | | API P95 | < 200ms | 后端接口响应 | | API P99 | < 500ms | 后端接口响应(长尾) | | DB Query | < 50ms | 单条数据库查询 | | Redis | < 5ms | 缓存操作 | --- ## 前端性能 ### Vite 构建优化 ```typescript // vite.config.ts export default defineConfig({ build: { target: 'es2015', minify: 'terser', terserOptions: { compress: { drop_console: true, drop_debugger: true, }, }, rollupOptions: { output: { manualChunks: { 'vue-vendor': ['vue', 'vue-router', 'pinia'], // 仅管理端:'element-plus': ['element-plus'], 'echarts': ['echarts'], }, }, }, chunkSizeWarningLimit: 2000, }, }) ``` ### 加载优化 - 路由懒加载: `() => import('@/views/module/page/index.vue')` - 管理端 Element Plus 按需导入: `unplugin-vue-components` + `unplugin-auto-import`(用户端不使用 Element Plus) - 大列表虚拟化: `@tanstack/vue-virtual` - 组件懒加载: `defineAsyncComponent()` - 页面缓存: `` 配合 worktab Store ### 资源优化 - 图片: WebP 格式 + 压缩 + CDN - 图标: 管理端 `@element-plus/icons-vue` 按需导入;用户端使用 `lucide-vue-next` - 字体: `font-display: swap` 避免阻塞 - Gzip: Nginx 开启 gzip 压缩 ### 前端缓存策略 | 数据 | 方式 | TTL | |------|------|-----| | 产品列表 | Pinia + localStorage | 5 分钟 | | 字典数据 | Pinia + localStorage | 1 小时 | | 用户配置 | Pinia persist | 持久 | | 路由/菜单 | Pinia persist | 持久 | | 接口响应 | 不缓存(后端 Redis) | — | --- ## 后端性能 ### Swoole 调优 ```php // config/autoload/server.php 'settings' => [ 'worker_num' => swoole_cpu_num() * 2, 'task_worker_num' => 4, 'max_request' => 10000, 'max_coroutine' => 100000, 'enable_coroutine' => true, 'open_tcp_nodelay' => true, 'socket_buffer_size' => 3 * 1024 * 1024, 'buffer_output_size' => 3 * 1024 * 1024, 'package_max_length' => 5 * 1024 * 1024, ], ``` ### 数据库查询优化 | 规则 | 正确做法 | 反模式 | |------|---------|--------| | 避免 N+1 | `with(['customer', 'subOrders'])` | 循环中查询关联 | | 明确列名 | `select('id', 'order_no', 'status')` | `SELECT *` | | 分页 | 游标分页 `WHERE id > ? LIMIT ?` | `OFFSET` 深分页 | | 批量操作 | `insert([...])` / `chunk(500)` | 循环单条 SQL | | 索引 | 复合索引遵循最左前缀 | 过多单列索引 | | 大表 COUNT | 缓存计数 / 近似值 | `COUNT(*)` 全表 | ### 缓存优化 ```php // Cache-Aside 模式 + TTL 抖动 $data = $cache->withCache("order:{$id}", 300, fn() => Order::find($id)); // 缓存失效:写操作后立即清除 $cache->invalidate("order:{$id}"); $cache->invalidatePattern('orders:list:*'); ``` ### 协程并发 ```php // 并行无依赖 I/O 操作 $parallel = new Parallel(10); $parallel->add(fn() => $this->orderService->getStatistics($id)); $parallel->add(fn() => $this->paymentService->getPayments($id)); [$stats, $payments] = $parallel->wait(); ``` --- ## 服务器性能 ### Nginx 优化 ```nginx # Gzip 压缩 gzip on; gzip_min_length 1k; gzip_comp_level 6; gzip_types text/plain text/css application/json application/typescript image/svg+xml; # 静态资源长期缓存(Vite 产物带 hash) location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # HTTP/2 listen 443 ssl http2; ``` ### 连接池调优 ```php // MySQL 连接池 'pool' => [ 'min_connections' => 5, 'max_connections' => 50, 'connect_timeout' => 10.0, 'wait_timeout' => 3.0, 'max_idle_time' => 60, ], // Redis 连接池 'pool' => [ 'min_connections' => 5, 'max_connections' => 30, 'connect_timeout' => 10.0, 'wait_timeout' => 3.0, ], ``` --- ## 禁止事项 - 前端同步阻塞主线程的操作 - 未压缩的大图片 (> 200KB) - 无限滚动不做虚拟化 - Swoole Worker 中执行阻塞 I/O (`file_get_contents`, `sleep`) - 全表扫描无 WHERE 条件 - 事务内包含远程调用 - 单事务操作 > 1000 行