用 CloudBase 静态网站托管部署 React SPA
一句话定义:用 Vite + React 出
dist,通过tcb hosting deploy dist -e <env-id>一键上传到 CloudBase 静态托管,自动拿到 CDN 加速 + HTTPS + 自定义域名能力;React Router 用BrowserRouter时,在控制台把错误页面配为index.html解决刷新 404。预计耗时:15 分钟 | 难度:入门
适用场景
这一篇覆盖的是最常见的 React 前端部署场景:纯 SPA、不要 SSR、不写后端逻辑,构建产物是一坨静态文件。CloudBase 静 态托管底层是 COS + CDN,按存储和流量计费,没有容器实例费。
- 适用:React 18+ + Vite 或 Create React App 出的 SPA
- 适用:要部署在境内、域名要走 ICP 备案 + HTTPS
- 适用:成本敏感、不需要服务端进程
- 不适用:要 SSR / SSG / Next.js 服务端渲染——走云托管,参考 deploy-nextjs-to-cloudbase-run
- 不适用:要 BFF 或后端 API——也走云托管或云函数
静态托管 vs 云托管
一句话区分:静态托管 = 纯前端文件 + CDN 分发;云托管 = 容器化的服务端进程。
| 维度 | 静态托管 | 云托管 |
|---|---|---|
| 部署对象 | 一坨静态文件(HTML/CSS/JS/图片) | 一个容器镜像(Dockerfile) |
| 运行时 | 无(CDN 直接回源 COS) | Node.js / Python / 自定义运行时进程 |
| 计费 | 存储 + 流量 | 实例时长 + 流量 |
| 适合 | SPA / 文档站 / 落地页 | SSR / API / WebSocket / 长连接 |
React SPA 就两种产物:HTML 一份 + JS bundle 一份 + 各种静态资源,全部走静态托管最便宜。
环境要求
| 依赖 | 版本 |
|---|---|
| Node.js | ≥ 18(Vite 5/6 的最低要求) |
| React | 18+ |
| Vite 或 CRA | Vite 5+ / Create React App 5+ |
react-router-dom | 6+(如用路由) |
@cloudbase/cli | latest(写文章时是 v3.x) |
| 一个已开通的 CloudBase 环境 | 含静态托管能力 |
CLI 全局安装并登录:
npm i -g @cloudbase/cli
tcb login
第一步:配置 React 项目
SPA 部署到静态托管,有两个配置必须先确认,否则到了第四步会出现刷新 404、资源 404 这类典型坑。
1.1 Vite 的 base 配置
vite.config.ts(或 vite.config.js):
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
base: '/', // 部署到根域名时填 '/'
// base: '/my-app/', // 如果要部署到子路径(例如默认域名/my-app/)就填 '/my-app/'
build: {
outDir: 'dist',
},
});
base 决定 build 出来的 HTML 里资源引用的前缀。如果你部署到默认域名根路径(https://<env-id>.tcloudbaseapp.com/),就用 /;如果通过 tcb hosting deploy dist /my-app -e <env-id> 部署到子路径,就要改成 /my-app/,前后斜杠都不能少。
Create React App 项目对应的字段是 package.json 里的 homepage,含义类似(CRA build 产物在 build/ 目录而不是 dist/,下文以 Vite 为例)。
1.2 React Router 的 basename
React Router 6+ 有两种写法,basename 都必须和 Vite 的 base 完全一致。
老写法(BrowserRouter):
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
function App() {
return (
<BrowserRouter basename="/">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
export default App;
新写法(createBrowserRouter,推荐):
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
const router = createBrowserRouter(
[
{ path: '/', element: <Home /> },
{ path: '/about', element: <About /> },
],
{ basename: '/' },
);
function App() {
return <RouterProvider router={router} />;
}
export default App;
两种写法等价,但 createBrowserRouter 支持 data API(loader / action),React Router 官方推荐新项目用它。
basename 跟 Vite 的 base 不一致会出现「首页能打开但跳转 /about 之后所有资源 404」这种诡异现象,这是 hosting-vue 一样的陷阱。
如果你只想图省事不踩 history 模式的坑,可以改用 createHashRouter(或 HashRouter),URL 会变成 /#/about 这种带 # 的形式,不需要任何服务端 fallback 配置。但 SEO 不友好,生产项目一般不推荐。