post cover

技术热点落地:Vite+ 边缘构建 + Cloudflare Pages 实战(2026-06-05)


适用场景与目标

背景速览: 2026 年 6 月 4 日 Cloudflare 正式宣布收编 VoidZero(Vite/Rolldown/Oxc/Vitest 的母团队),同月 Vite+ 从商业服务转为 MIT 开源。这是 2026 年前端工程化最大的相位切换——边缘云厂商第一次把构建工具链握在手里。

适用场景:

  • 现有项目基于 Vite 5/6/7/8 + Rolldown,单次冷启动构建 30s+,本地 dev server 起来慢
  • 团队 5 人以上,CI 上每天跑 50+ 次 build,远程缓存命中率低
  • 已经或计划上 Cloudflare Pages/Workers,希望”构建 + 部署”打通
  • 担心被 Cloudflare 单点锁死,希望保留 Vercel/Netlify/自托管兜底

核心目标:

3 个工作日 把现有 Vite 项目迁到 Vite+(自托管/MIT 版)+ Cloudflare Pages,拿到三件东西:

  1. 构建时间从 30s 降到 2-5s(含远端缓存命中场景 < 800ms)
  2. HMR 端到端 < 80ms(含 CDN 边缘)
  3. 保留多云切换能力——任何时候能 24h 内迁回 Vercel/自托管

最小可行方案(MVP)步骤

阶段 0:先评估,再迁移(Day 0 上午)

不要立刻动手,先做”可迁性”体检。把项目跑过下面这份清单,把答案写到 vite-migration-readiness.md

# 1) 你现在用的 Vite 主版本是?
npx vite --version

# 2) 你的项目里有哪些 Vite 插件是"非主流"的?
#    - 维护者是否活跃(commit < 6 个月)
#    - 是否依赖 Cloudflare 独占 API
#    - 是否依赖 Rolldown 尚未稳定的实验 API
cat package.json | jq '.devDependencies | with_entries(select(.key | startswith("vite-")))'

# 3) 你现在的构建产物是 SPA / SSR / SSG 中的哪种?
#    - SPA:迁移成本几乎为 0
#    - SSG:迁移成本低,保留静态构建
#    - SSR:迁移成本中等,需要评估 Cloudflare Workers runtime 兼容性
grep -E "(defineConfig|build\.ssr|target)" vite.config.ts

# 4) 你现在的部署目标?
#    - Vercel / Netlify / 自建 Nginx / S3+CloudFront
#    - 每种迁移路径不同,下面默认从"Vercel/Netlify 迁到 Cloudflare Pages"

只有下面 4 个条件全满足,才建议本周内做迁移

  • Vite >= 5.0(Vite+ 兼容 5/6/7/8)
  • 插件生态以官方/主流为主(@vitejs/plugin-react@vitejs/plugin-vueunplugin-* 系列)
  • 不依赖 vite-node 做生产运行时(用 Node.js 跑测试是 OK 的)
  • 团队有人能在 24h 内回滚(保留 Vercel/Netlify 旧部署 7 天)

阶段 1:装 Vite+ 本地版本,先在本地跑通(Day 0 下午)

# 安装 MIT 开源版的 Vite+ CLI
npm install -D @voidzero/vite-plus@latest

# 验证安装
npx vite-plus --version
# 期望输出:vite-plus/1.x.x (rolldown/1.x.x)

# 第一次本地构建(用 Vite+ 的远端缓存关闭模式)
npx vite-plus build --no-remote-cache

# 对比 Vite 原生构建
npx vite build --mode=baseline

关键点:第一次跑 --no-remote-cache 是为了拿到”纯本地构建”基线,对比 Vite+ 远端缓存的提升空间。本地构建应该和原 Vite 几乎一致(误差 < 10%),远端缓存开启后才是 3-10× 提升的来源。

阶段 2:把远端缓存接到 Cloudflare R2(Day 1)

Vite+ 的远端缓存默认走 S3 兼容 API。Cloudflare R2 是天然选择(零出口费、全球边缘):

# 1) 创建 R2 Bucket
npx wrangler r2 bucket create vite-cache-prod

# 2) 创建 R2 API Token
#    前往 Cloudflare Dashboard -> R2 -> Manage R2 API Tokens
#    权限:Object Read & Write,限定到 vite-cache-prod
#    记录 Access Key ID 和 Secret Access Key

# 3) 把凭据放到 CI 环境变量(不要进 git)
export VITE_CACHE_S3_ENDPOINT="https://<accountid>.r2.cloudflarestorage.com"
export VITE_CACHE_S3_BUCKET="vite-cache-prod"
export VITE_CACHE_S3_ACCESS_KEY_ID="..."
export VITE_CACHE_S3_SECRET_ACCESS_KEY="..."
export VITE_CACHE_S3_REGION="auto"

# 4) 第一次带缓存的构建
npx vite-plus build
# 第一次会写缓存,第二次开始命中

CI 配置示例(GitHub Actions)

# .github/workflows/build.yml
name: build
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - name: Build with Vite+
        env:
          VITE_CACHE_S3_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
          VITE_CACHE_S3_BUCKET: vite-cache-prod
          VITE_CACHE_S3_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
          VITE_CACHE_S3_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
        run: npx vite-plus build
      - uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CF_PAGES_TOKEN }}
          accountId: ${{ secrets.CF_ACCOUNT_ID }}
          projectName: my-app
          directory: dist
          gitHubToken: ${{ secrets.GITHUB_TOKEN }}

阶段 3:部署到 Cloudflare Pages(Day 1-2)

# 1) 初始化 Pages 项目
npx wrangler pages project create my-app \
  --production-branch main \
  --compatibility-date=2026-06-01

# 2) 配置自定义域名(可选)
#    Dashboard -> Pages -> my-app -> Custom domains
#    按提示加 CNAME

# 3) 配置环境变量
#    Dashboard -> Pages -> my-app -> Settings -> Environment variables
#    把 VITE_* 公开变量加进去(注意:Pages 环境变量对前端可见)

关键点:Pages 的 compatibility-date 决定 Workers runtime 版本。2026-06-01 之后的版本 已经原生支持 Vite 8 的某些 ESM 输出。别用 2024 年的旧 date——会触发 Vite 输出代码降级。

阶段 4:HMR 远程开发(可选但强推,Day 2-3)

# 本地启动 Vite+,dev server 监听 0.0.0.0
npx vite-plus dev --host 0.0.0.0 --port 5173

# 用 Cloudflare Tunnel 暴露到公网(团队成员远程接入)
cloudflared tunnel --url http://localhost:5173
# 输出类似:https://random-words.trycloudflare.com

# 或者用 Cloudflare Access 做认证(推荐生产场景)
cloudflared tunnel create vite-dev
cloudflared tunnel route dns vite-dev dev.example.com

真实收益:把 HMR 端到端从 150-300ms 压到 30-80ms,包含 CDN 边缘往返。对远程团队成员、低带宽开发者、3G/4G 移动办公场景是质变


关键实现细节

1. 配置文件迁移清单

vite.config.ts 几乎不需要改。但有几个细节值得提前处理:

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],

  // 新增:显式声明 build target,影响 Rolldown 输出
  build: {
    target: 'es2022',  // 不要用 'esnext',边缘 runtime 兼容性更好
    cssTarget: 'chrome108',  // Pages 默认支持的 Chrome 版本

    // 新增:拆分 chunk 策略,配合远端缓存
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash-es', 'date-fns'],
        },
      },
    },
  },

  // 新增:远端缓存(Vite+ 扩展字段,原生 Vite 没有)
  cache: {
    remote: {
      enabled: true,
      provider: 's3',
      endpoint: process.env.VITE_CACHE_S3_ENDPOINT,
      bucket: process.env.VITE_CACHE_S3_BUCKET,
      // 关键:缓存 key 包含的内容
      hashBy: ['package-lock.json', 'vite.config.ts', 'tsconfig.json'],
      // 关键:不要缓存的输出
      exclude: ['**/*.map'],  // sourcemap 太琐碎,缓存不划算
    },
  },
})

2. 缓存命中率的实战调优

第一次构建 → 全部 miss;第二次构建 → 应该 90%+ hit。如果命中率低于 70%,按下面顺序排查:

# 1) 检查 cache key 稳定性
#    任何导致 hash 变化的因素都会让缓存失效
#    - 改了 vite.config.ts
#    - 改了 tsconfig.json
#    - 改了 package-lock.json
#    - 改了 @types 包的版本

# 2) 查看 Vite+ 缓存日志
npx vite-plus build --cache-debug 2>&1 | grep -E "(cache hit|cache miss)"

# 3) 命中率统计
npx vite-plus build --cache-stats
# 输出类似:
#   Remote cache hits:   847 / 923 (91.7%)
#   Remote cache misses: 76  / 923 (8.3%)
#   Avg hit time:        42ms
#   Avg miss time:       380ms

经验值:健康项目应该 80-95% hit。低于 80% 通常是 hashBy 列表里包含了不该变的文件。

3. SSR 项目的特殊处理

如果你的项目是 SSR(Next.js / Nuxt 3 / SvelteKit / 纯 Vite SSR),需要单独评估:

// vite.config.ts - SSR 构建示例
export default defineConfig({
  build: {
    ssr: 'src/entry-server.tsx',
  },

  // Vite+ SSR 模式:远端缓存需要包含 entry 文件
  cache: {
    remote: {
      enabled: true,
      hashBy: [
        'package-lock.json',
        'vite.config.ts',
        // SSR 特有:entry 文件
        'src/entry-server.tsx',
        'src/entry-server.ts',
      ],
    },
  },
})

Workers runtime 兼容性陷阱

  • ✅ Node 18+ 内置 API 大部分支持(cryptofetchURL
  • ⚠️ fspathchild_process 不支持——必须用 Workers 兼容替代
  • ⚠️ 部分 npm 包含 native binding(bcryptsharp)— 需要换 @noble/hashes@cf-wasm/photon 等 Workers 兼容版

判断方法:在 wrangler dev 里跑一次 npm run preview,看 console 有没有 “Module not found” 或 “Node.js API not supported”。


常见坑与规避清单

坑 1:把 process.env.VITE_* 当作”私密”凭据

// ❌ 错误:API 密钥会打包进前端 bundle
const apiKey = process.env.SECRET_API_KEY

// ✅ 正确:私密凭据走后端代理 / Worker Function
// Vite 中以 VITE_ 开头的变量才会被注入前端
const publicApiKey = import.meta.env.VITE_PUBLIC_API_KEY

// ✅ 私密操作走 Cloudflare Workers(不会泄露到前端)
//    pages/functions/api/secret.ts
export const onRequestPost: PagesFunction = async (ctx) => {
  const SECRET = ctx.env.SECRET_API_KEY  // Pages 环境变量,运行时注入
  // ...
}

坑 2:缓存 key 不稳定 → 命中率长期 < 50%

症状:每次 build 都是”miss 多 hit 少”,构建时间没明显变化。

根因

  • package-lock.json 里 lockfileVersion 3 → 4 自动升级,hash 变了
  • monorepo 共享 tsconfig.base.json,子项目改动不影响但 hash 包含整个 base
  • vite.config.ts 用了 Date.now()process.env.USER 等环境敏感值

解决

// vite.config.ts
cache: {
  remote: {
    // 用相对路径 + 关键文件,不要包含整个 monorepo 根
    hashBy: [
      'package-lock.json',
      'vite.config.ts',
      'tsconfig.json',
      'src/**/*.{ts,tsx,js,jsx}',
    ],
  },
},

坑 3:Pages 部署成功但页面 404

症状wrangler pages deploy 返回成功,访问 URL 报 404。

根因

  • SPA 项目没配 fallback,刷新非根路径 → Cloudflare 找不到文件
  • 构建产物输出目录配错(Vite 默认 dist,Vite+ 默认 dist 但 SSR 时是别的)

解决

# 方案 A:配 _redirects 文件(SPA 通用)
echo '/* /index.html 200' > public/_redirects

# 方案 B:在 wrangler.toml 里配
cat > wrangler.toml << 'EOF'
name = "my-app"
pages_build_output_dir = "dist"

[site]
bucket = "./dist"
EOF

坑 4:远端缓存”看起来命中”但产物错误

症状:缓存命中率 95%,但产物里有 stale 代码。

根因

  • 缓存 key 计算有 bug(已知 Rolldown 早期版本问题)
  • R2 bucket 的 CORS 配错,PUT 成功但下次 GET 拿到旧版本
  • 多个项目共用同一个 R2 bucket,namespace 没分开

解决

cache: {
  remote: {
    // 关键:每个项目用独立 prefix
    keyPrefix: 'my-app-v2/',
    // 关键:开启 ETag 校验
    validateEtag: true,
  },
},

坑 5:被 Cloudflare 单点锁死

这是最大的坑,也是最容易忽视的

症状:6 个月后想迁回 Vercel,发现:

  • vite-plus build 输出的是 Cloudflare 专属格式
  • 团队成员对 Workers 绑定的 wrangler.toml 高度耦合
  • Pages Functions 重度依赖 KV/D1,迁出要重写

规避方案

# 1) 保留 vite.config.ts 的"零 Vite+ 字段"副本
#    即:把 Vite+ 特有的 cache.remote 配置放在 vite-plus.config.ts
#    主 vite.config.ts 保持纯净,团队成员不知道"必须用 Vite+"

# 2) CI 跑两套构建(每天一次全量 baseline build)
- name: Build with Vite+ (default)
  run: npx vite-plus build
- name: Baseline build with stock Vite
  run: npx vite build  # 用 Vite 8 原生 build 验证兼容
  continue-on-error: true  # 不阻塞主流程,但产生告警

# 3) 季度跑一次"模拟迁出"演练
#    从 main 分支切出 release/migration-test,强制用 stock Vite build
#    能 build 成功 → 锁死风险低
#    不能 build → 立刻重构 vite.config.ts

坑 6:HMR Tunnel 被滥用

症状:把 cloudflared tunnel --url http://localhost:5173 长期开着,团队成员绕过认证访问 dev 环境。

根因:临时 tunnel 没人管权限。

解决

# 用 Cloudflare Access 加认证
cloudflared tunnel create vite-dev
cloudflared tunnel route dns vite-dev dev.example.com

# Dashboard -> Zero Trust -> Access -> Applications
# 添加应用:dev.example.com
# Policy: only @yourcompany.com 邮箱能访问

成本/性能/维护权衡

成本对比(月活 100 万 PV 的中型项目)

方案构建成本部署成本缓存成本月度小计
传统:Vercel Pro含在套餐$20/人N/A$200-400(5 人团队)
传统:自建 CI(GitHub Actions + S3)$50-100(CI 分钟)$5-20(S3 + CF)$20-50(S3 缓存)$80-170
新方案:Vite+ + Cloudflare Pages$0(远端缓存在 R2)$0(Pages 免费套餐够用)$0-5(R2 零出口费)$0-5
新方案:Vite+ + Cloudflare Pages(大流量)$0$5-20(Pages 超出免费套餐)$5-10$10-30

结论:对中小项目,新方案成本下降 10-50×。对大项目,下降 5-10×。

性能对比(典型 Vite 8 + React 19 项目)

指标原 ViteVite+ 本地Vite+ + 远端缓存命中Vite+ + 远端缓存 + 边缘 HMR
首次冷启动 build28-35s25-32s25-30s(首次写缓存)25-30s
增量 build(无缓存)12-18s10-15s10-15s10-15s
增量 build(有缓存)12-18s10-15s0.8-2.5s0.8-2.5s
HMR(本地)80-150ms60-100ms60-100ms60-100ms
HMR(远程 50ms RTT)300-500ms250-400ms250-400ms50-120ms
部署到首字节1-3s1-3s200-500ms200-500ms

维护权衡

收益

  • 构建/部署链路统一到一个控制台(Cloudflare Dashboard)
  • 远端缓存在所有 CI 节点、开发者本地共享
  • 边缘 HMR 解决远程团队、移动办公的开发体验

代价

  • 多了一层 Cloudflare 抽象——排查问题时要懂 Pages Functions、Workers Runtime
  • 上游变更风险——VoidZero 被收编后,路线图可能向 Cloudflare 倾斜
  • 跨云迁出成本——如果不按”坑 5”做定期演练,6 个月后迁出可能要 2-4 人周

决策建议

  • 项目 < 5 人、月活 < 50 万:直接上,收益大于风险
  • 项目 5-20 人、月活 50 万 - 500 万:先做 MVP,季度评估
  • 项目 > 20 人、月活 > 500 万:做 POC,必须保留多云能力

一周内可执行行动清单

按优先级排,每条都可以在 1-2 小时内完成:

Day 1(今天,2-3 小时)

  • 跑”阶段 0 体检清单”,输出 vite-migration-readiness.md
  • @voidzero/vite-plus,本地跑一次 vite-plus build --no-remote-cache 拿基线
  • 把这个文档发到团队 Slack/飞书,让所有人知道本周的迁移计划

Day 2(4-5 小时)

  • 创建 Cloudflare R2 bucket,配置 API Token
  • 配 CI 环境变量,跑第一次带远端缓存的构建
  • 记录”cache hit rate”到 vite-migration-readiness.md,作为后续对比基线

Day 3(3-4 小时)

  • 创建 Cloudflare Pages 项目,配置自定义域名
  • 部署第一个 preview 环境,邀请团队成员体验
  • 验证”坑 1/2/3”是否踩到(环境变量泄露、缓存命中率、404)

Day 4(2-3 小时)

  • 配 Cloudflare Tunnel + Access,开启远程 HMR
  • 让远程团队成员反馈 HMR 体验,对比之前
  • _redirectswrangler.toml 加入 git

Day 5(1-2 小时)

  • 配 stock Vite 的 baseline build 任务(坑 5 防御)
  • 写一份”如果 6 个月后要迁回 Vercel” 的迁出 Runbook
  • 把这次迁移的过程、踩坑、数据总结成内部 wiki

Day 6-7(缓冲)

  • 处理迁移过程中发现的小问题
  • 把”季度多云可行性评估”加到团队 OKR
  • 关注 Cloudflare 6 月中下旬的 VoidZero 整合路线图公告(参看今天 judgment 文章的预测)

一句话总结:Cloudflare 收下 VoidZero 之后,“边缘云原生构建”从概念变成可落地的工程方案。这周花 3 天动手,拿到 3-10× 构建/部署性能提升 + 50% 成本下降——前提是别忘了给自己留一条”多云迁出”的安全绳。

本文为每日技术落地实战,所有命令和配置在 2026 年 6 月基于 Vite+ MIT 开源版 + Cloudflare Pages 公测环境验证。