性能优化与安全

让应用又快又安全——全栈开发者的最后两把刷子

🎯 学习目标

学完本章,你将能够:

  • 使用 Redis 缓存和数据库连接池优化后端性能
  • 理解前端性能优化策略:懒加载、代码分割和图片优化
  • 防御常见安全威胁:XSS、CSRF、SQL 注入和速率攻击
  • 配置 CORS、CSP 等安全响应头
  • 安全管理和轮换密钥与依赖审计

你有没有想过

为什么接口响应从 50ms 变成了 500ms?为什么 Google 搜索你的网站会出现"此网站已被黑客入侵"的警告?

性能和安全是生产环境的两条生命线。慢一秒,用户跑一半;漏一个洞,数据全泄露。

这一章是全课程的最后一章,也是最不能跳过的一章。我们从后端缓存、前端优化,讲到四大安全漏洞的防御。

Part 1: 后端性能

缓存策略和数据库索引,数量级的性能提升。

Redis 缓存

不重复计算,直接返回结果

缓存就是把计算结果存起来,下次同样的请求直接返回,不再重复计算。

// 通用缓存包装器
async function cacheable<T>(
  key: string,
  fetcher: () => Promise<T>,
  ttl: number = 300  // 默认5分钟
): Promise<T> {
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);

  const data = await fetcher();
  await redis.setex(key, ttl, JSON.stringify(data));
  return data;
}

// 使用
const user = await cacheable(
  `user:${id}`,
  () => prisma.user.findUnique({ where: { id } }),
  600  // 10分钟
);

这个比喻只是为了帮你建立直观感受:缓存像把常用工具放在桌面,而不是每次都去仓库找。实际上缓存涉及失效策略、穿透防护等复杂机制。

缓存失效策略

数据改了,缓存要跟着变

// 写操作后使缓存失效
app.put('/api/users/:id', async (req, res) => {
  const user = await prisma.user.update({
    where: { id: Number(req.params.id) },
    data: req.body,
  });

  // 使相关缓存失效
  await redis.del(`user:${req.params.id}`);
  await redis.del('users:list');

  res.json(user);
});
模式策略适用场景
Cache-Aside先查缓存,miss 时查 DB 回填通用
Write-Through写数据时同时更新缓存一致性要求高
Write-Behind先写缓存,异步写 DB写密集场景

数据库连接池

避免每次请求都重建连接

建立数据库连接是一个昂贵的操作——TCP 握手、TLS 协商、权限校验,单次可能要 50-200ms。连接池在应用启动时预先创建一组连接,请求到来时直接复用。

// Prisma 内置连接池,通过 URL 参数配置
// .env
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb?connection_limit=20&pool_timeout=30"
参数作用建议值
connection_limit最大连接数CPU 核心数 x 2 + 1
pool_timeout获取连接等待时间10-30s

Prisma 默认连接池大小为 num_physical_cpus * 2 + 1。在 Serverless 环境中,由于实例频繁冷启动,建议使用 Prisma Data Proxy 或 PgBouncer 等外部池化方案。

连接池监控

// 监控连接池状态(Prisma 内置指标)
const metrics = await prisma.$metrics.json();
console.log(metrics.counters);

// 告警阈值:活跃连接 > 80% 最大限制时需扩容
// 告警阈值:等待请求数 > 0 时排查慢查询
指标健康范围异常处理
active connections< 80% of limit增加 pool_size
waiting requests0优化慢查询 / 加 pool
avg acquire time< 50ms检查网络 / 连接泄漏

数据库索引

给查询加速的目录

// prisma/schema.prisma
model Post {
  id        Int      @id @default(autoincrement())
  title     String
  slug      String   @unique
  published Boolean  @default(false)
  createdAt DateTime @default(now())
  authorId  Int
  author    User     @relation(fields: [authorId], references: [id])

  @@index([authorId])                // 外键索引
  @@index([published, createdAt])    // 复合索引
}

复合索引有最左前缀原则:索引 [published, createdAt] 能优化 WHERE published = true,但不能优化 WHERE createdAt > '2024-01-01'(跳过了第一列)。

N+1 问题

最常见的数据库性能杀手

// 错误:N+1 查询
const posts = await prisma.post.findMany(); // 1次
for (const post of posts) {
  const author = await prisma.user.findUnique({  // N次!
    where: { id: post.authorId },
  });
}

// 正确:使用 include 预加载
const posts = await prisma.post.findMany({
  include: { author: true },  // 1次 JOIN 搞定
});

// 更好:只查需要的字段
const posts = await prisma.post.findMany({
  select: {
    id: true, title: true,
    author: { select: { id: true, name: true } },
  },
});

Part 2: 前端性能

代码分割、懒加载、图片优化。

组件懒加载

按需加载,减少首屏体积

首屏只需要展示核心内容,图表、地图、富文本编辑器等重组件可以等用户真正需要时再下载。

// React.lazy + Suspense
import { lazy, Suspense } from 'react';

const HeavyChart = lazy(() => import('@/components/HeavyChart'));
const RichEditor = lazy(() => import('@/components/RichEditor'));

function AnalyticsPage() {
  return (
    <div>
      <h1>数据分析</h1>
      <Suspense fallback={<div>加载中...</div>}>
        <HeavyChart />
      </Suspense>
    </div>
  );
}

// Vue 3 defineAsyncComponent
import { defineAsyncComponent } from 'vue';
const AsyncMap = defineAsyncComponent(() =>
  import('@/components/Map.vue')
);
// 路由级别代码分割(React Router v6)
import { lazy } from 'react';
import { createBrowserRouter } from 'react-router-dom';

const Settings = lazy(() => import('./pages/Settings'));
const Profile = lazy(() => import('./pages/Profile'));

const router = createBrowserRouter([
  { path: '/settings', element: <Settings /> },
  { path: '/profile',  element: <Profile /> },
]);

压缩与 HTTP 缓存

减小体积 + 避免重复传输

Gzip 或 Brotli 压缩可以将文本资源体积减少 60%-80%。HTTP 缓存让浏览器在资源未变更时直接使用本地副本。

// Express 压缩中间件
import compression from 'compression';

app.use(compression({
  level: 6,               // 压缩级别 1-9
  threshold: 1024,        // 大于 1KB 才压缩
  filter: (req, res) => {
    if (req.headers['x-no-compression']) return false;
    return compression.filter(req, res);
  },
}));
// 静态资源长期缓存(带 hash 文件名)
app.use('/static', express.static('public', {
  maxAge: '1y',            // 缓存 1 年
  immutable: true,         // 文件内容不变
}));

// API 响应短期缓存
app.get('/api/config', (req, res) => {
  res.set('Cache-Control', 'public, max-age=300'); // 5 分钟
  res.json(config);
});

// 资源变更时协商缓存
res.set({
  'ETag': '"33a64df5"',
  'Last-Modified': 'Thu, 01 Jul 2026 00:00:00 GMT',
});
策略响应头适用场景
强缓存Cache-Control: max-age=31536000带 hash 的静态资源
协商缓存ETag / If-None-MatchAPI 响应、HTML
不缓存Cache-Control: no-store敏感数据、实时信息

Brotli 压缩

Brotli 比 Gzip 再压缩 15%-25%,但压缩速度慢,适合预压缩静态资源。

import shrinkRay from 'shrink-ray-current';
app.use(shrinkRay());  // 优先使用 Brotli,降级到 Gzip

代码分割

不用的代码不加载

// React.lazy 懒加载重型组件
import { lazy, Suspense } from 'react';

const HeavyChart = lazy(() => import('@/components/HeavyChart'));

function DashboardPage() {
  return (
    <div>
      <h1>仪表盘</h1>
      <Suspense fallback={<div>加载中...</div>}>
        <HeavyChart />
      </Suspense>
    </div>
  );
}

// Next.js dynamic(支持 SSR 控制)
import dynamic from 'next/dynamic';
const Map = dynamic(() => import('@/components/Map'), {
  ssr: false,  // 禁用 SSR
  loading: () => <div>地图加载中...</div>,
});

图片优化

Next.js Image 自动处理格式和尺寸

import Image from 'next/image';

// 自动转 WebP/AVIF、适配尺寸、懒加载
<Image
  src="/hero.jpg"
  alt="首页大图"
  width={1200}
  height={600}
  sizes="(max-width: 768px) 100vw, 50vw"
  priority   // 首屏图片优先加载
/>
指标衡量内容优秀阈值
LCP最大内容渲染时间< 2.5s
FID首次交互延迟< 100ms
CLS累积布局偏移< 0.1
TTFB首字节时间< 800ms

前端性能监控

用数据驱动性能优化

优化前先测量。Web Vitals 是 Google 提出的核心指标,帮助开发者从用户视角评估页面体验。

// web-vitals 库上报核心指标
import { onCLS, onINP, onLCP } from 'web-vitals';

function report(metric) {
  navigator.sendBeacon('/analytics', JSON.stringify({
    name: metric.name,     // CLS / INP / LCP
    value: metric.value,   // 毫秒或分数
    rating: metric.rating, // good / needs-improvement / poor
  }));
}

onCLS(report);   // 累积布局偏移
onINP(report);   // 交互到下一次绘制
onLCP(report);   // 最大内容渲染
指标含义优秀需改进
LCP最大内容渲染时间< 2.5s2.5-4s
INP交互到下一次绘制< 200ms200-500ms
CLS累积布局偏移< 0.10.1-0.25

建议将性能数据接入 CI,每次部署自动对比基线,性能回退 > 10% 时触发告警。

Part 3: 安全防御

XSS、CSRF、SQL 注入、依赖审计——四大安全威胁。

Part 3: 请求安全

速率限制、跨域控制、内容安全策略——防御恶意请求。

速率限制深入

滑动窗口 vs 固定窗口,分布式计数

简单的每分钟 100 次限制可能被窗口边界突刺攻击绕过——在两个窗口的交界点各发 100 次,实际 200 次只在 1 秒内完成。

// express-rate-limit:完整配置
import rateLimit from 'express-rate-limit';

// 反向代理(Nginx / ELB / Cloudflare)后必须设置 trust proxy,
// 否则 req.ip 永远是代理回环地址(如 127.0.0.1),限流会退化成"全站共享一个桶"。
// 设为代理层数(通常是 1),不要设为 true(会有 IP 欺骗风险)。
app.set('trust proxy', 1);

const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,    // 15 分钟
  limit: 100,                   // 每个 IP 最大请求数
  standardHeaders: 'draft-7',   // 返回 RateLimit-* 头
  legacyHeaders: false,         // 禁用 X-RateLimit-* 头
  message: { error: '请求过于频繁,请 15 分钟后再试' },
  // 用默认 keyGenerator 即可,它会读取上面 trust proxy 解析后的 req.ip。
  // 不要自己读 req.headers['x-forwarded-for']——该头部可被客户端任意伪造,攻击者能借此绕过限流。
  skip: (req) => req.path === '/health',  // 健康检查不限速
});

app.use('/api/', apiLimiter);
反代场景下最容易踩的两个限流坑:① 没设 trust proxy,导致所有请求的 req.ip 都是 127.0.0.1,限流对全站失效;② 自己读 x-forwarded-for 头当 key,而该头可被任意伪造,攻击者换个 IP 字符串就能绕过限流。正确做法是设置 trust proxy 后用默认 keyGenerator,让 Express 安全地解析真实客户端 IP。
算法优点缺点
固定窗口实现简单,内存少边界突刺问题
滑动窗口平滑限流,无突刺内存稍多,实现复杂
令牌桶允许合理突发流量参数调优复杂
// 通过 RateLimit 标准化头返回配额信息
app.get('/api/data', (req, res) => {
  // 响应头:RateLimit-Limit: 100, RateLimit-Remaining: 95
  res.json({ ok: true });
});
// 分布式环境:使用 Redis 存储计数
import { RedisStore } from 'rate-limit-redis';
import { createClient } from 'redis';

const client = createClient({ url: process.env.REDIS_URL });
await client.connect();

const limiter = rateLimit({
  store: new RedisStore({
    sendCommand: (...args: string[]) => client.sendCommand(args),
  }),
  windowMs: 60 * 1000,
  limit: 100,
});

CORS 跨域资源共享

控制哪些来源可以访问你的 API

同源策略是浏览器安全的基础——默认情况下,网页只能请求同协议、同域名、同端口的资源。CORS(跨域资源共享)是允许特定来源访问的机制。

// 基础配置:允许特定来源
import cors from 'cors';

app.use(cors({
  origin: ['https://myapp.com', 'https://admin.myapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,            // 允许携带 Cookie
  maxAge: 86400,                // 预检请求缓存 24 小时
}));
注意:当 credentials: true 时,不能使用 origin: '*'。浏览器会拒绝带通配符 origin + 凭证的组合。必须显式指定允许的来源列表。
// 动态来源验证(多租户场景)
const allowedDomains = ['.myapp.com', 'localhost:3000'];

app.use(cors({
  origin: (origin, callback) => {
    // 允许无 origin 的请求(如移动端、Postman)
    if (!origin) return callback(null, true);

    const isAllowed = allowedDomains.some(domain =>
      origin === domain || origin.endsWith(domain)
    );

    if (isAllowed) {
      callback(null, true);
    } else {
      callback(new Error('Origin ' + origin + ' not allowed by CORS'));
    }
  },
  credentials: true,
}));

CSP 内容安全策略

告诉浏览器哪些资源可以加载和执行

Content-Security-Policy 响应头是一个深度防御层——即使攻击者成功注入了恶意脚本,CSP 也能阻止其执行。

// 使用 Helmet 配置 CSP
import helmet from 'helmet';

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],           // 默认只允许同源
      scriptSrc: ["'self'", "'nonce-abc123'"],  // 脚本仅同源 + nonce
      styleSrc: ["'self'", "'unsafe-inline'"],  // 样式允许内联
      imgSrc: ["'self'", "data:", "https://cdn.myapp.com"],
      connectSrc: ["'self'"],           // fetch/XHR 仅同源
      fontSrc: ["'self'", "https://fonts.googleapis.com"],
      objectSrc: ["'none'"],            // 禁止 Flash 等插件
      baseUri: ["'self'"],              // 限制 <base> 标签
      formAction: ["'self'"],           // 表单提交仅同源
      frameAncestors: ["'none'"],       // 禁止被 iframe 嵌入
    },
  },
  // 其他安全头也一并启用
  crossOriginEmbedderPolicy: true,
  crossOriginOpenerPolicy: true,
  crossOriginResourcePolicy: { policy: "same-origin" },
}));
// 对应生成的 HTTP 响应头
// Content-Security-Policy: default-src 'self';
//   script-src 'self' 'nonce-abc123';
//   style-src 'self' 'unsafe-inline';
//   img-src 'self' data: https://cdn.myapp.com;
//   connect-src 'self';
//   font-src 'self' https://fonts.googleapis.com;
//   object-src 'none';
//   base-uri 'self';
//   form-action 'self';
//   frame-ancestors 'none'

Nonce 与 Hash 白名单

如果页面需要内联脚本,可以使用 nonce 或 hash 白名单放行特定内联代码。

import { randomBytes } from 'crypto';

app.use((req, res, next) => {
  res.locals.nonce = randomBytes(16).toString('hex');
  next();
});

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      scriptSrc: ['self', (req, res) => `'nonce-${res.locals.nonce}'`],
    },
  },
}));

// <script nonce="<%= nonce %>">initApp();</script>

XSS 跨站脚本

恶意脚本注入到你的页面中

XSS 是攻击者把恶意 JavaScript 注入到网页中,在其他用户浏览时执行。

// React JSX 自动转义(安全)
const userInput = '<script>alert("xss")</script>';
return <div>{userInput}</div>;  // 自动转义

// 需要渲染 HTML 时使用 DOMPurify
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirtyHtml, {
  ALLOWED_TAGS: ['b', 'i', 'a', 'p'],
});

// 配置 CSP 头(Content Security Policy)
import helmet from 'helmet';
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
    },
  },
}));

CSRF 跨站伪造

诱导用户在不知情下执行操作

// 防御1:SameSite Cookie(最简单有效)
app.use(session({
  cookie: {
    httpOnly: true,    // JS 无法读取
    secure: true,      // 仅 HTTPS
    sameSite: 'strict', // 不允许跨站发送
  },
}));

// 防御2:验证 Origin 头
function csrfCheck(req, res, next) {
  const origin = req.headers.origin;
  const allowed = ['https://myapp.com'];
  if (req.method !== 'GET' && !allowed.some(o => origin?.startsWith(o))) {
    return res.status(403).json({ error: 'CSRF check failed' });
  }
  next();
}

SQL 注入

Prisma 默认参数化查询,天然免疫

// Prisma 自动参数化(安全)
const user = await prisma.user.findUnique({
  where: { email: userInput },  // 安全
});

// 危险:SQL 字符串拼接(绝对不要这样做!)
// const q = `SELECT * FROM users WHERE email = '${userInput}'`
// userInput = "admin' OR '1'='1" → 返回所有用户!

// 安全:Prisma tagged template 自动参数化
const result = await prisma.$queryRaw`
  SELECT * FROM "User" WHERE email = ${userInput}
`;
永远不要信任用户输入。无论前端做了什么验证,后端都必须重新验证。前端验证只是提升体验,不能替代后端安全检查。

依赖审计

你的第三方库可能有漏洞

# npm audit 检查已知漏洞
$ npm audit
$ npm audit --production    # 只检查生产依赖
$ npm audit fix             # 自动修复

# 在 CI 中集成安全审计
# .github/workflows/security.yml
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm audit --audit-level=high

Part 4: 密钥管理

密钥泄露是最常见的安全事故原因。

环境变量管理

用 Zod 验证,启动时报错好过运行时崩溃

// src/env.ts
import { z } from 'zod';

const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']),
  DATABASE_URL: z.string().url(),
  JWT_SECRET: z.string().min(32),
  PORT: z.coerce.number().default(3000),
});

export const env = envSchema.parse(process.env);
// 缺少必要变量时直接报错退出
# .gitignore 中必须排除
.env
.env.local
.env.production

密码与 JWT

bcrypt 哈希 + 短期 Token

// 密码哈希
const hashed = await bcrypt.hash(password, 12);
const isValid = await bcrypt.compare(input, hashed);

// 短期 Access Token
const accessToken = sign(
  { userId: user.id },
  env.JWT_SECRET,
  { expiresIn: '15m' }
);

// Refresh Token 存 HttpOnly Cookie
res.cookie('refreshToken', refreshToken, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 7 * 24 * 60 * 60 * 1000,
});

Part 4: 依赖与环境安全

依赖审计、密钥管理——让供应链和环境配置不再成为短板。

依赖审计工具链

从手动检查到自动化门禁

npm audit 对比依赖版本与已知漏洞数据库(Advisory Database)。但仅靠 npm audit 不够——还需要持续监控和自动修复机制。

工具定位特点
npm audit命令行检查内置,零配置
DependabotGitHub 原生自动提 PR 升级
Renovate跨平台高度可配置,支持分组
Snyk深度安全平台容器、IaC 扫描
# .github/workflows/security.yml
name: Security Audit
on:
  push:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # 每周一早上 6 点

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm audit --audit-level=high
        continue-on-error: false

Dependabot 配置(.github/dependabot.yml)可以指定哪些依赖需要自动更新、review 人等。对于关键安全依赖,建议设置自动合并低风险升级。

密钥管理最佳实践

环境隔离 + 最小权限 + 定期轮换

密钥管理不是"存到 .env 就完了"。生产环境需要集中式管理,审批流程,以及泄漏后的快速响应能力。

// 1. 环境分离 —— 开发 / 测试 / 生产各自独立密钥
// .env.development
DATABASE_URL=postgresql://localhost/devdb
JWT_SECRET=dev-only-not-secret

// .env.production(由部署平台注入,不入库)
DATABASE_URL=postgresql://prod-db.internal:5432/app
JWT_SECRET=(32+ 字节强随机值)
存储方案适用场景安全等级
.env + .gitignore本地开发基础
GitHub SecretsCI/CD 流水线良好
云 KMS (AWS/GCP)中大型应用优秀
HashiCorp Vault企业级最高
绝对禁止:
• 在任何 Git 仓库中提交 .env 文件
• 在代码中硬编码 API Key 或密码
• 前端代码中存放后端密钥
• 多人共享同一个生产密钥
• 密钥从不轮换(超过 90 天)

密钥轮换流程(双密钥过渡法)

# 1. 生成新密钥
NEW_SECRET=$(openssl rand -hex 32)

# 2. 服务端双密钥并行验证(过渡期 7 天)
# 3. 7 天后下线旧密钥

# 4. 客户端迁移到新密钥
echo "密钥轮换完成"
阶段操作验证
准备生成新密钥,通知团队本地测试
过渡期双密钥并行验证监控错误日志
收尾下线旧密钥全量回归测试

排错:密钥泄露

症状:GitHub 收到安全告警,或发现 .env 文件被提交到了仓库。
原因:忘记把 .env 加入 .gitignore,或代码中硬编码了密钥。
修复:(1)立即轮换泄露的密钥;(2)用 BFG Repo-Cleaner 从 Git 历史中删除;(3)用 git-secrets 或 gitleaks 防止再次泄露。
# 用 gitleaks 扫描代码中的密钥
$ gitleaks detect --source . --verbose

安全检查清单

上线前逐一检查

类别措施工具
XSS输入转义、CSP 头DOMPurify、Helmet
CSRFSameSite Cookie + Origin 校验自实现 / csrf-csrf
SQL 注入参数化查询Prisma
认证密码哈希、短 Tokenbcrypt、JWT
HTTPS全站 HTTPSLet's Encrypt
依赖定期审计npm audit
速率限制防暴力破解express-rate-limit
输入验证验证所有输入Zod

试一试

练习一

任务:为你的 Express 应用添加速率限制,每个 IP 每分钟最多 100 次请求。

参考答案

import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 60 * 1000,
  max: 100,
  message: '请求太频繁,请稍后再试',
});
app.use('/api/', limiter);

练习二

任务:运行 npm audit 检查你的项目依赖,修复所有高危漏洞。

$ npm audit
$ npm audit fix

练习三:CORS 修复

解决本地前后端分离的跨域障碍

场景:前端运行在 http://localhost:5173,后端 API 在 http://localhost:3000。前端调用 fetch('http://localhost:3000/api/data') 被浏览器拦截,控制台报 Access-Control-Allow-Origin 错误。

任务:在后端 Express 中添加 CORS 中间件,使前端可以跨域请求,同时允许携带 Cookie。

参考答案

import cors from 'cors';

app.use(cors({
  origin: 'http://localhost:5173',  // 明确指定前端来源
  credentials: true,                // 允许 Cookie
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
}));

练习四:Helmet 安全头配置

CSP / HSTS / X-Content-Type-Options

任务:使用 Helmet 中间件一次性启用多个安全头。要求:
① CSP 仅允许同源脚本和样式
② HSTS 强制 HTTPS(max-age 至少 30 天)
③ 禁用 MIME 类型嗅探(X-Content-Type-Options: nosniff)

参考答案

import helmet from 'helmet';

app.use(helmet());  // 自动启用 X-Content-Type-Options: nosniff 等

// 手动追加 HSTS(Helmet 默认不开启严格 HSTS)
app.use(helmet.hsts({
  maxAge: 2592000,        // 30 天(单位:秒)
  includeSubDomains: true,
  preload: true,
}));

// CSP 配置(需手动启用)
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", "data:"],
  },
}));

📝 要点总结

本章核心知识点

缓存策略

Redis 缓存热点数据,连接池复用数据库连接,索引加速查询

前端性能

懒加载减少首屏体积,代码分割按需加载,图片格式优化

安全防御

输入验证 + 参数化查询 + CSP = 防御 XSS/SQL 注入/CSRF

密钥管理

环境变量 + 定期轮换 + 依赖审计 = 安全的密钥生命周期

恭喜你完成了全部课程

从 JavaScript 入门到全栈部署,你已经掌握了 Node.js 全栈开发的核心技能。继续实践,持续学习,写出更好的代码。

1 / X