第42期 | 项目1续:部署与优化——让你的AI知识库上线全网

🎯 今天你将学会

  • 用 Vercel 一键部署全栈应用,零配置上线
  • 掌握前端性能优化的6个关键维度
  • SEO 基础:让搜索引擎收录你的项目
  • 用 AI 辅助分析性能瓶颈和生成优化方案

📖 核心知识

上期我们完成了 AI 知识库产品 TechAssist 的核心功能开发。代码在本地跑得好好的,但如果没有部署上线,它就永远只是你电脑上的一个文件夹。

部署上线,是教程项目和生产项目的分水岭。

今天我们把 TechAssist 部署到 Vercel,然后做一轮性能优化和 SEO 配置。最终效果:任何人通过一个 URL 就能访问你的 AI 知识库。


一、Vercel 部署:从本地到全网

1.1 为什么选 Vercel?
平台 适合场景 免费额度 前端友好度
Vercel Next.js / React SPA 100GB带宽/月 ⭐⭐⭐⭐⭐
Netlify 静态站 / JAMstack 100GB带宽/月 ⭐⭐⭐⭐
GitHub Pages 纯静态 无限 ⭐⭐⭐
Cloudflare Pages 静态 + Edge Functions 无限带宽 ⭐⭐⭐⭐

Vercel 的核心优势:

  • 零配置:检测到框架自动配置构建
  • Edge Network:全球 CDN,访问速度快
  • Serverless Functions:前端项目也能写后端 API
  • 预览部署:每个 PR 自动生成预览链接
1.2 部署前的准备
// package.json — 确保构建脚本正确
{
  "name": "tech-assist",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  // ...其他配置
}
// vite.config.ts — 添加生产优化
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  build: {
    // 代码分包
    rollupOptions: {
      output: {
        manualChunks: {
          'react-vendor': ['react', 'react-dom', 'react-router-dom'],
          'ai-vendor': ['openai', 'ai'],
          'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
        },
      },
    },
    // chunk 大小警告阈值
    chunkSizeWarningLimit: 1000,
    // 启用 sourcemap(生产环境用于错误追踪)
    sourcemap: false,
  },
})
// src/config/env.ts — 环境变量管理
const env = {
  // 前端可见的变量(VITE_ 前缀)
  SUPABASE_URL: import.meta.env.VITE_SUPABASE_URL,
  SUPABASE_ANON_KEY: import.meta.env.VITE_SUPABASE_ANON_KEY,
  API_BASE_URL: import.meta.env.VITE_API_BASE_URL || '/api',
  IS_PROD: import.meta.env.PROD,
}

// 启动时校验必要变量
function validateEnv() {
  const required = ['SUPABASE_URL', 'SUPABASE_ANON_KEY']
  const missing = required.filter(key => !env[key])
  if (missing.length > 0) {
    console.error(`Missing env vars: ${missing.join(', ')}`)
    if (env.IS_PROD) {
      throw new Error(`Environment configuration error: ${missing.join(', ')}`)
    }
  }
}

validateEnv()

export default env
1.3 Vercel 部署流程

方式一:CLI 部署

# 安装 Vercel CLI
npm i -g vercel

# 在项目根目录执行
vercel

# 交互式配置:
# ? Set up and deploy "tech-assist"? → Y
# ? Which scope do you want to deploy to? → 选择你的账号
# ? Link to existing project? → N
# ? What's your project's name? → tech-assist
# ? In which directory is your code located? → ./
# ? Want to modify these settings? → N

# 部署到生产环境
vercel --prod

方式二:GitHub 关联自动部署(推荐)

1. 推送代码到 GitHub
2. 登录 vercel.com → New Project → Import Git Repository
3. 选择你的仓库 → 自动检测 Vite 框架
4. 配置环境变量:
   VITE_SUPABASE_URL = https://xxx.supabase.co
   VITE_SUPABASE_ANON_KEY = eyJxxx...
5. 点击 Deploy → 等 1-2 分钟
6. 得到 https://tech-assist.vercel.app
1.4 Serverless API Routes

Vercel 支持在 api/ 目录下创建 Serverless Functions,这样前端项目也能有后端能力——而且 OpenAI API Key 可以安全地存在服务端

// api/chat.ts — 后端代理 OpenAI 调用
import { OpenAI } from 'openai'
import type { VercelRequest, VercelResponse } from '@vercel/node'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY, // 服务端环境变量,前端看不到
})

export default async function handler(req: VercelRequest, res: VercelResponse) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' })
  }

  const { messages, model = 'gpt-4o-mini' } = req.body

  try {
    const stream = await openai.chat.completions.create({
      model,
      messages,
      stream: true,
    })

    // 设置 SSE 响应头
    res.setHeader('Content-Type', 'text/event-stream')
    res.setHeader('Cache-Control', 'no-cache')
    res.setHeader('Connection', 'keep-alive')

    // 转发流式响应
    for await (const chunk of stream) {
      const content = chunk.choices[0]?.delta?.content
      if (content) {
        res.write(`data: ${JSON.stringify({ content })}\n\n`)
      }
    }

    res.write('data: [DONE]\n\n')
    res.end()
  } catch (error) {
    console.error('Chat API error:', error)
    res.status(500).json({ error: 'Internal server error' })
  }
}
// api/embed.ts — 文档向量化 API
import { OpenAI } from 'openai'
import type { VercelRequest, VercelResponse } from '@vercel/node'

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })

export default async function handler(req: VercelRequest, res: VercelResponse) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' })
  }

  const { text } = req.body as { text: string }

  if (!text || text.length > 8000) {
    return res.status(400).json({ error: 'Invalid text input' })
  }

  try {
    const response = await openai.embeddings.create({
      model: 'text-embedding-3-small',
      input: text,
    })

    return res.status(200).json({
      embedding: response.data[0].embedding,
      tokens: response.usage.total_tokens,
    })
  } catch (error) {
    console.error('Embed API error:', error)
    return res.status(500).json({ error: 'Embedding failed' })
  }
}

为什么用 API Route 而不是前端直连?

  1. API Key 安全:Key 存在服务端环境变量,前端代码里看不到
  2. 请求控制:可以在服务端做限流、内容审核
  3. CORS 无忧:同域请求,没有跨域问题
  4. 灵活降级:可以加缓存、切换模型,前端无感知
1.5 自定义域名
Vercel Dashboard → Settings → Domains → Add Domain

# 如果你有域名 tech-assist.com:
1. 添加域名
2. 在域名服务商处配置 DNS:
   - A 记录: @ → 76.76.21.21
   - CNAME: www → cname.vercel-dns.com
3. 等 DNS 生效(几分钟到几小时)
4. Vercel 自动签发 SSL 证书

# 最终访问地址:
# https://tech-assist.com (主域名)
# https://www.tech-assist.com (www 跳转到主域名)

二、性能优化:6个关键维度

部署上线只是第一步。如果用户打开页面要等 5 秒,他们会直接关掉。

2.1 性能指标:Lighthouse 评分体系
# 安装 Lighthouse CLI
npm i -g lighthouse

# 运行审计
lighthouse https://tech-assist.vercel.app --view

# 五个维度:
# Performance(性能) — 目标 90+
# Accessibility(可访问性) — 目标 95+
# Best Practices(最佳实践) — 目标 100
# SEO — 目标 95+
# PWA(渐进式 Web 应用)— 可选

核心性能指标(Core Web Vitals):

指标 含义 目标值
LCP (Largest Contentful Paint) 最大内容渲染时间 < 2.5s
FID/INP (Interaction to Next Paint) 首次交互延迟 < 200ms
CLS (Cumulative Layout Shift) 累积布局偏移 < 0.1
FCP (First Contentful Paint) 首次内容渲染 < 1.8s
TTFB (Time to First Byte) 首字节时间 < 800ms
2.2 维度一:资源加载优化
// 1. 图片懒加载 + WebP 格式
import { useState, useEffect, useRef } from 'react'

function LazyImage({ src, alt, width, height }: ImageProps) {
  const [isLoaded, setIsLoaded] = useState(false)
  const [currentSrc, setCurrentSrc] = useState('')
  const ref = useRef<HTMLImageElement>(null)

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          // 根据设备像素比选择合适尺寸
          const dpr = window.devicePixelRatio || 1
          const optimized = `${src}?w=${width * dpr}&format=webp`
          setCurrentSrc(optimized)
          observer.disconnect()
        }
      },
      { rootMargin: '50px' }
    )

    if (ref.current) observer.observe(ref.current)
    return () => observer.disconnect()
  }, [src, width])

  return (
    <div
      style={{ width, height, background: '#f0f0f0' }}
      className="image-placeholder"
    >
      {currentSrc && (
        <img
          ref={ref}
          src={currentSrc}
          alt={alt}
          width={width}
          height={height}
          loading="lazy"
          onLoad={() => setIsLoaded(true)}
          style={{ opacity: isLoaded ? 1 : 0, transition: 'opacity 0.3s' }}
        />
      )}
    </div>
  )
}
// 2. 字体优化:使用 font-display: swap
// index.css
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter.woff2') format('woff2');
  font-weight: 400 700;
  font-display: swap; /* 用系统字体先渲染,字体加载后替换 */
}

/* 或者用 Google Fonts 的预连接 */
// index.html
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"
  rel="stylesheet"
/>
// 3. 第三方脚本异步加载
// 不阻塞渲染
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_ID"></script>

// 如果依赖关系重要,用 defer
<script defer src="/js/analytics.js"></script>
2.3 维度二:代码分包与懒加载
// React.lazy 懒加载路由组件
import { lazy, Suspense } from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import Layout from './components/Layout'
import LoadingSpinner from './components/LoadingSpinner'

// 首页直接加载
import Home from './pages/Home'

// 其他页面懒加载
const Chat = lazy(() => import('./pages/Chat'))
const KnowledgeBase = lazy(() => import('./pages/KnowledgeBase'))
const Settings = lazy(() => import('./pages/Settings'))

export default function App() {
  return (
    <BrowserRouter>
      <Layout>
        <Suspense fallback={<LoadingSpinner />}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/chat" element={<Chat />} />
            <Route path="/kb" element={<KnowledgeBase />} />
            <Route path="/settings" element={<Settings />} />
          </Routes>
        </Suspense>
      </Layout>
    </BrowserRouter>
  )
}
// 动态导入大依赖
async function exportData() {
  // 只在用户点击导出时才加载 jszip
  const JSZip = (await import('jszip')).default
  const zip = new JSZip()

  // ...打包逻辑

  const blob = await zip.generateAsync({ type: 'blob' })
  return blob
}
2.4 维度三:渲染性能优化
// 1. 长列表虚拟化:react-window
import { FixedSizeList as List } from 'react-window'

interface Message {
  id: string
  role: 'user' | 'assistant'
  content: string
}

function MessageList({ messages }: { messages: Message[] }) {
  const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
    <div style={style}>
      <MessageBubble message={messages[index]} />
    </div>
  )

  return (
    <List
      height={600}
      itemCount={messages.length}
      itemSize={120}
      width="100%"
    >
      {Row}
    </List>
  )
}

// 10000 条消息也丝滑——只渲染可视区域的 ~7 条
// 2. useMemo / useCallback 避免不必要的渲染
import { useMemo, useCallback, memo } from 'react'

// ✅ 用 memo 包裹子组件
const MessageBubble = memo(({ message }: { message: Message }) => {
  return <div className="bubble">{message.content}</div>
})

function ChatRoom({ messages, onSend }: ChatRoomProps) {
  // ✅ 只在 messages 变化时重新计算统计
  const stats = useMemo(() => {
    const userCount = messages.filter(m => m.role === 'user').length
    const assistantCount = messages.filter(m => m.role === 'assistant').length
    return { userCount, assistantCount, total: messages.length }
  }, [messages])

  // ✅ onSend 引用稳定,子组件不会因父组件重渲染而重渲染
  const handleSend = useCallback(
    (text: string) => {
      onSend(text)
    },
    [onSend]
  )

  return (
    <div>
      <StatsBar stats={stats} />
      <MessageList messages={messages} />
      <InputBox onSend={handleSend} />
    </div>
  )
}
2.5 维度四:网络请求优化
// 1. 请求缓存:SWR / TanStack Query
import useSWR from 'swr'

const fetcher = (url: string) => fetch(url).then(res => res.json())

function useDocuments() {
  const { data, error, isLoading, mutate } = useSWR('/api/documents', fetcher, {
    revalidateOnFocus: false,  // 切回标签页不重新请求
    dedupingInterval: 60000,  // 60秒内相同请求去重
  })

  return { documents: data, isLoading, error, refresh: mutate }
}

// 2. 请求合并:多个请求并行
async function loadDashboardData(userId: string) {
  // ❌ 串行:慢
  // const profile = await fetchProfile(userId)
  // const docs = await fetchDocuments(userId)
  // const usage = await fetchUsage(userId)

  // ✅ 并行:快
  const [profile, docs, usage] = await Promise.all([
    fetchProfile(userId),
    fetchDocuments(userId),
    fetchUsage(userId),
  ])

  return { profile, docs, usage }
}

// 3. 防抖搜索
function useDebouncedSearch(query: string, delay = 300) {
  const [results, setResults] = useState([])

  useEffect(() => {
    if (!query.trim()) {
      setResults([])
      return
    }

    const timer = setTimeout(async () => {
      const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`)
      const data = await res.json()
      setResults(data)
    }, delay)

    return () => clearTimeout(timer)
  }, [query, delay])

  return results
}
2.6 维度五:构建产物分析
# 安装包分析工具
npm i -D rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    react(),
    visualizer({
      open: true,          // 构建后自动打开
      filename: 'stats.html',
      gzipSize: true,
      brotliSize: true,
    }),
  ],
})
# 分析结果示例
dist/assets/
├── react-vendor.js      142KB  (32%)  ← 框架,无法减小
├── ai-vendor.js          89KB  (20%)  ← openai SDK
├── ui-vendor.js          45KB  (10%)  ← Radix UI
├── index.js              67KB  (15%)  ← 业务代码
├── Chat.js               34KB   (8%)  ← 懒加载 chunk
├── KnowledgeBase.js      28KB   (6%)
├── index.css             22KB   (5%)
└── 其他                   18KB   (4%)

# 优化决策:
# - openai SDK 太大?→ 改用直接 fetch 调用 API,去掉 SDK
# - Radix UI 只用了 3 个组件?→ 按需导入
# - 业务代码 67KB?→ 检查是否有死代码
2.7 维度六:缓存策略
// Vercel 静态资源缓存配置
// vercel.json
{
  "headers": [
    {
      "source": "/assets/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    },
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=0, must-revalidate"
        }
      ]
    }
  ]
}

// Vite 构建的文件名带 hash(如 index.abc123.js)
// 所以静态资源可以永久缓存,文件变了 hash 就变,浏览器会重新请求
// HTML 文件不缓存,确保用户总是拿到最新的入口

三、SEO 基础

SEO 对 AI 应用同样重要——如果你的 AI 知识库被搜索引擎收录,会带来自然流量。

3.1 基础 meta 标签
<!-- index.html -->
<head>
  <title>TechAssist - AI 知识库助手 | 智能问答与文档管理</title>
  <meta
    name="description"
    content="TechAssist 是一个基于 RAG 的 AI 知识库助手,支持文档上传、智能问答和知识管理。"
  />
  <meta name="keywords" content="AI知识库,RAG,智能问答,文档管理,AI助手" />
  <meta name="author" content="你的名字" />

  <!-- Open Graph(社交分享卡片) -->
  <meta property="og:title" content="TechAssist - AI 知识库助手" />
  <meta property="og:description" content="基于 RAG 的智能问答与文档管理平台" />
  <meta property="og:image" content="https://tech-assist.vercel.app/og-image.png" />
  <meta property="og:url" content="https://tech-assist.vercel.app" />
  <meta property="og:type" content="website" />

  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="TechAssist - AI 知识库助手" />
  <meta name="twitter:image" content="https://tech-assist.vercel.app/og-image.png" />

  <!-- canonical URL(防止重复内容) -->
  <link rel="canonical" href="https://tech-assist.vercel.app" />
</head>
3.2 动态 meta 标签(React)
// hooks/useSEO.ts — 动态 SEO Hook
import { useEffect } from 'react'

interface SEOProps {
  title: string
  description?: string
  image?: string
  url?: string
}

function useSEO({ title, description, image, url }: SEOProps) {
  useEffect(() => {
    document.title = title

    const setMeta = (name: string, content: string, attr: 'name' | 'property' = 'name') => {
      let tag = document.querySelector(`meta[${attr}="${name}"]`)
      if (!tag) {
        tag = document.createElement('meta')
        tag.setAttribute(attr, name)
        document.head.appendChild(tag)
      }
      tag.setAttribute('content', content)
    }

    if (description) setMeta('description', description)
    if (image) {
      setMeta('og:image', image, 'property')
      setMeta('twitter:image', image)
    }
    if (url) {
      setMeta('og:url', url, 'property')
    }
    setMeta('og:title', title, 'property')
    setMeta('twitter:title', title)
  }, [title, description, image, url])
}

// 使用
function ChatPage() {
  useSEO({
    title: 'AI 对话 | TechAssist',
    description: '与你的 AI 知识库助手对话,获取基于文档的精准回答。',
    url: 'https://tech-assist.vercel.app/chat',
  })

  return <ChatInterface />
}
3.3 sitemap.xml 和 robots.txt
<!-- public/sitemap.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://tech-assist.vercel.app</loc>
    <changefreq>weekly</changefreq>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://tech-assist.vercel.app/chat</loc>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://tech-assist.vercel.app/kb</loc>
    <changefreq>daily</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>
# public/robots.txt
User-agent: *
Allow: /
Disallow: /settings
Disallow: /api/

Sitemap: https://tech-assist.vercel.app/sitemap.xml
3.4 SPA 的 SEO 痛点

SPA(单页应用)的内容是 JS 渲染的,搜索引擎爬虫可能看不到内容。解决方案:

方案 难度 效果 适合场景
SSR(服务端渲染) ⭐⭐⭐⭐⭐ 用 Next.js 重构
预渲染(prerender) ⭐⭐⭐⭐ 内容相对静态
动态渲染 ⭐⭐⭐⭐ 爬虫来时返回 HTML
基础 meta 优化 ⭐⭐⭐ 大多数 SPA

对于 TechAssist 这种交互密集的应用,先做基础 meta 优化就够了。如果后续要做落地页(Landing Page),可以用 Next.js 或 Astro 做预渲染。


🤖 AI协作实战

场景:用 AI 做 Lighthouse 性能分析

我的 Prompt:

我有一个 React + Vite 项目部署在 Vercel 上,Lighthouse 性能评分 62 分。
主要问题:
- LCP: 4.2s(目标 < 2.5s)
- CLS: 0.25(目标 < 0.1)
- TBT: 380ms(目标 < 200ms)

项目信息:
- 框架:React 18 + Vite
- 主要依赖:react-router-dom, openai, @radix-ui, react-markdown
- 首页内容:导航栏 + Hero区域 + 功能卡片 + Footer
- 图片:3张 png(未压缩),1张 gif(2.5MB)

请按优先级列出优化方案,每项说明:1)做什么 2)预期改善 3)实现代码

AI 的输出(节选):

按优先级排序的优化方案:

### P0:图片优化(预计 LCP -1.5s)

问题分析:
- 2.5MB 的 gif 是 LCP 的元凶
- 3张未压缩 png 总计 1.8MB

方案:
1. gif → WebM 视频(体积减 80%+)
2. png → WebP(体积减 50-70%)
3. 添加 width/height 属性防止 CLS

### P1:减少 JS 阻塞(预计 TBT -150ms)

问题分析:
- openai SDK 在首页并不需要,但被打进了主 chunk
- @radix-ui 全量导入

方案:
1. openai SDK 动态导入
2. Radix 按需导入
3. 首页非关键组件懒加载

### P2:CLS 修复(预计 CLS → 0.05)

问题分析:
- 图片无尺寸属性
- 字体加载导致文字跳动

方案:
1. 所有图片添加 width/height
2. 字体使用 font-display: swap + size-adjust
3. 骨架屏占位

我的审查和修改:

AI 给的方案方向正确,但有几处需要调整:

  1. gif → WebM:AI 建议转 WebM,但考虑到兼容性,我改用 <picture> 标签同时提供 WebM 和 fallback
  2. openai SDK:实际上我在第41期已经把 OpenAI 调用移到了后端 API Route,前端不需要这个 SDK——确认 package.json 中已移除
  3. size-adjust:AI 给了一个固定的 size-adjust: 95%,但这个值需要根据实际字体调试,不是通用的
/* 我的最终字体配置 */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter.woff2') format('woff2');
  font-weight: 400 700;
  font-display: swap;
  /* 逐字调试后的 size-adjust */
  size-adjust: 97.5%;
  ascent-override: 92%;
}

学到了什么:

  • AI 给的性能优化方案 方向通常正确,但具体数值需要实测
  • size-adjust 这种细调值,AI 只能给个起始值,需要用 Font Style Matcher 工具精确调试
  • 让 AI 分析时,给它越多的数据(Lighthouse 报告、包分析结果),方案越精准
  • AI 不会知道你的项目已经做了什么优化——你需要在 prompt 中说明当前状态

AI 辅助 SEO 优化

Prompt 模板:

我有一个 AI 知识库产品 TechAssist,请帮我生成以下 SEO 内容:

1. 5 个目标关键词(长尾词优先)
2. 首页 title 标签(60字符以内)
3. 首页 description(155字符以内)
4. 3 篇引流文章的标题和摘要(发布在技术社区)
5. Open Graph 卡片的文案

产品信息:
- 核心功能:RAG知识库 + AI对话 + 文档管理
- 目标用户:开发者、技术团队
- 差异化:本地部署友好的轻量级 AI 知识库

AI 能帮你快速生成 SEO 文案矩阵,但要注意:

  • 关键词密度不要堆砌——搜索引擎会惩罚
  • title 和 description 要吸引人点击——排名高但没人点击也没用
  • 引流文章要真有干货——别只是标题党

💻 动手练习

练习1:部署你的项目(简单)

把第41期的 TechAssist 项目部署到 Vercel:

  1. 推送代码到 GitHub
  2. 在 Vercel 导入项目
  3. 配置环境变量
  4. 部署成功后,用手机打开链接——感受移动端体验

练习2:跑一次 Lighthouse(中等)

部署后运行 Lighthouse 审计:

  1. 用 Chrome DevTools → Lighthouse 面板
  2. 记录五项评分
  3. 根据建议优化至少 3 项
  4. 重新部署后对比评分变化

练习3:性能优化挑战(挑战)

目标:Lighthouse Performance 评分从初始值提升 20 分以上。

必做项:

  • 图片懒加载 + WebP 转换
  • 至少 2 个路由懒加载
  • 第三方脚本 async/defer
  • CLS 降至 0.1 以下

选做项:

  • react-window 虚拟列表
  • SWR 请求缓存
  • 构建产物包分析,去掉无用依赖

📌 本期要点

  1. Vercel 部署:GitHub 关联 + 环境变量配置 = 零配置上线。API Route 让前端项目也能安全调用 OpenAI
  2. 性能指标:LCP < 2.5s / CLS < 0.1 / INP < 200ms 是核心 Web Vitals 及格线
  3. 六维优化:资源加载、代码分包、渲染性能、网络请求、构建分析、缓存策略——系统化提升
  4. SEO 三件套:meta 标签 + sitemap.xml + robots.txt 是基础三件套,动态页面用 Hook 管理
  5. AI 辅助优化:给 AI 提供 Lighthouse 报告和包分析数据,它能给出精准的优先级排序

🔗 下期预告

第43期:项目2——AI 工作流编排工具。我们来实现一个可视化的流程编辑器,让用户拖拽节点来编排 AI 工作流。涉及画布交互、React Flow、复杂状态管理,是前端交互能力的集中展示。
如果你没有苹果电脑,需要上传ios到APPStore可以访问以下网站
iPA上传工具 - IPA解析与AppStore提交

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐