当前位置: 首页 > news >正文

用 Vite + Cloudflare Pages 实现模块级独立打包与部署的静态 CDN 分发

🧭 背景

在开发支持多模板的前端系统(如 CMS、商城等)时,往往希望不同的模块(如 cool-shopgeneral-shop)可以独立打包、部署、被懒加载。

本篇记录我如何使用:

  • Vite 分别构建多个独立 JS 模块

  • Cloudflare Pages 将它们作为静态资源部署

  • ✅ 最终支持其他项目通过 CDN 懒加载这些模块


🎯 目标

将项目中的两个模块:

  • src/themes/cool/Shop.tsx

  • src/themes/general/Shop.tsx

分别构建为:

dist/cool/cool-shop.js → https://cool-shop.pages.dev/cool-shop.js 
dist/general/general-shop.js →
https://general-shop.pages.dev/general-shop.js

 

其他项目可直接通过 URL 懒加载这些模块。

🏗️ 项目结构 & 配置

📁 文件结构

project-root/
├── src/
│   └── themes/
│       ├── cool/Shop.tsx
│       └── general/Shop.tsx
├── dist/
│   ├── cool/
│   │   └── cool-shop.js
│   └── general/
│       └── general-shop.js
├── vite.cool.config.ts
├── vite.general.config.ts
├── generateComponentRegistry.cjs
└── package.json

 


⚙️ Vite 多配置文件打包

分别创建两个配置文件:

vite.cool.config.ts

 

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'// 导出 Vite 配置
export default defineConfig({plugins: [react()], // 使用 React 插件,支持 JSX 和 HMR 等特性resolve: { // 路径别名配置,支持用 @ 代表 src 目录
    alias: {'@': path.resolve(__dirname, 'src'),},},build: { // 构建配置outDir: 'dist', // 输出目录为 dist/emptyOutDir: false, // ✅ 构建前自动清空 disttarget: 'esnext',  // 目标为 esnext,避免转译成老版本语法minify: true, // 启用压缩rollupOptions: { // 自定义 Rollup 打包选项
preserveEntrySignatures: 'strict', // ✅ 必须设置,否则与 preserveModules 冲突input: { // 多入口打包,每个入口会打成一个独立文件'cool-shop': 'src/themes/cool/Shop.tsx',// 你可以继续添加更多入口
      },output: {entryFileNames: 'cool/[name].js',  // 让每个入口文件输出为 [name].js,比如 cool-Shop.jsformat: 'es', // 输出为原生 ES module 格式manualChunks: undefined, // ⚠️关键:禁用共享 chunk 拆分,确保每个入口文件独立
      }},},
})

 

vite.general.config.ts 同理,仅路径和项目名不同。


📦 package.json 脚本

"scripts": {"clean": "npx rimraf dist","build": "tsc -b && npm run clean && vite build --config vite.cool.config.ts && vite build --config vite.general.config.ts && node generateComponentRegistry.cjs","deploy:cool": "wrangler pages deploy dist/cool --project-name=cool-shop","deploy:general": "wrangler pages deploy dist/general --project-name=general-shop","deploy": "npm run build && npm run deploy:cool && npm run deploy:general","gen:registry": "node generateComponentRegistry.cjs"
}

 


🚀 Cloudflare Pages 部署流程

  1. 安装 CLI 工具:

    npm install -g wrangler
  2. 登录账户:

    wrangler login
  3. 部署:

    npm run deploy

部署完后,将得到两个可直接访问的 CDN 资源地址:

  • https://cool-shop.pages.dev/cool-shop.js

  • https://general-shop.pages.dev/general-shop.js


🧠 自动生成组件注册表 JSON

脚本:generateComponentRegistry.cjs

const fs = require('fs');
const path = require('path');const distDir = path.resolve(__dirname, 'dist');
const registry = {};fs.readdirSync(distDir).forEach(folder => {const subDir = path.join(distDir, folder);if (!fs.statSync(subDir).isDirectory()) return;const files = fs.readdirSync(subDir);files.forEach(file => {if (file.endsWith('.js')) {const [name] = file.split('.js');const [prefix] = name.split('-');const key = name.replace(`${folder}-`, '');if (!registry[folder]) registry[folder] = {};registry[folder][key.toLowerCase()] = `https://${folder}-shop.pages.dev/${name}.js`;
    }});
});fs.writeFileSync(path.resolve(__dirname, 'componentRegistry.json'),JSON.stringify(registry, null, 2)
);

 

生成的 componentRegistry.json 示例:

{"cool": {"shop": "https://cool-shop.pages.dev/cool-shop.js"},"general": {"shop": "https://general-shop.pages.dev/general-shop.js"}
}

 

🧱 常见问题踩坑记录
问题原因解决方法
require is not defined type: "module" 时不能用 CommonJS .cjs 文件扩展名
Cloudflare Pages 发布了所有 JS 文件 你上传了整个 dist/ 文件夹 改为 dist/cooldist/general 分别上传
访问 .pages.dev 报 SSL 错误 访问了 preview 环境(如 hash 开头) 只访问 project-name.pages.dev 主地址
想换行写长脚本 Windows 不支持 \ 续行 .cjs 封装脚本或写成 Node 文件

✅ 收获总结

  • 理解了 Vite 如何进行多入口打包

  • 学会了如何控制输出结构避免 chunk 拆分

  • 利用 Cloudflare Pages 搭建了轻量 CDN

  • 成功构建了一个模块注册表用于懒加载系统


📎 下一步计划

  • 支持动态路由加载

  • 加入 版本号hash 以实现缓存控制

  • 自动推送部署并通知消费者系统更新注册表

 
http://www.kefakeji.com/news/880.html

相关文章:

  • [07.27学习笔记] Tokenizer - Luna
  • STM32F103C8T6芯片介绍(下) - LI,Yi
  • GRUB 设置安全启动
  • 【work记录】系统能力大赛数据库中的MVCC学习记录
  • FireStore如何查看空间占用情况?(未解决)
  • MIT6.s081_Lab8
  • 关于同源策略和跨域请求
  • Codeforces Round 1039 (Div. 2) 1 ~ D
  • 【转】[C#] 参数前加 in 的作用
  • 循环链表实现的队列
  • Codeforces Round 1039 (Div. 2)(A~E1)
  • 关于博客主题的一些思考
  • PG故障处理:PG_AUTO_FAILOVER自动切换失败的故障处理
  • Codeforces Round 1039 (Div. 2)
  • 21天
  • 【转】[C#] Enum 的 Flags 特性
  • 白嫖claude code的100美元额度,anyrouter中转服务
  • 2025.7.27 东师集训测试总结
  • POLIR-Laws-电商交易: 三方的法律关系确定: 网络交易双方与网络交易平台三者之间的法律关系
  • Umi-OCR完全指南:开源离线OCR识别软件下载安装使用教程|支持批量PDF/二维码识别
  • Docker
  • 7.28
  • 图像预处理 + Tesseract OCR 实战
  • 实现验证码识别:图像预处理 + Tesseract OCR 实战
  • java 网络编程
  • systemd 的unit配置文件里[Service]里的WorkingDirectory有什么用,如何配置
  • Python实现验证码识别:图像预处理 + Tesseract OCR 实战
  • 一些未来的思考
  • 学习之道 反思 记忆
  • Reference