初始化
This commit is contained in:
215
.cursor/rules/022-performance.mdc
Normal file
215
.cursor/rules/022-performance.mdc
Normal file
@@ -0,0 +1,215 @@
|
||||
---
|
||||
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()`
|
||||
- 页面缓存: `<keep-alive :include="cachedViews">` 配合 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 行
|
||||
Reference in New Issue
Block a user