216 lines
5.4 KiB
Plaintext
216 lines
5.4 KiB
Plaintext
---
|
||
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 行
|