《林一的 Cloudflare 通关记》第 4 篇-给网站安个"家"——Cloudflare Pages 部署前端
《林一的 Cloudflare 通关记》第4篇
周三下午,老张给林一发了一条消息:「把星火AI的前端代码推到GitHub上,然后部署起来。」
林一精神一振——终于可以干正事了。他熟练地 git init、git push,代码推到了GitHub仓库。然后,他打开了某云服务商的控制台,开始挑选云服务器。
故事:林一的手差点按下了"购买"按钮
「2核4G……嗯,够跑一个nginx了。按量付费,一个月大概六七十块……」
就在他的鼠标即将点击"立即购买"的那一刻,一只手从背后伸出来,按住了他的手腕。
「你一个月实习工资多少?」老张的声音从身后传来。
「三千……」
「服务器一个月七十,域名一年五十,SSL证书……哦证书咱用Cloudflare的免费的。但服务器钱,你打算自己掏?」
「公司不是可以报销吗?」
老张叹了口气:「报销流程走完要两个月。小王明天就要看线上效果,你等得起?」
林一收回了手。「那张哥,怎么办?」
老张拉了把椅子坐下来:「你听说过 Cloudflare Pages 吗?」
「Pages?像GitHub Pages那样?只能放静态页面?」
「此Pages非彼Pages。」老张笑了,「Cloudflare Pages 可不是简单放几个HTML文件。它能直接连你的GitHub仓库,你push代码它自动构建、自动部署、自动分发到全球CDN——而且,免费。」
「免费?连服务器都不用买?」
「不用买服务器,不用装nginx,不用配HTTPS,不用搞CI/CD pipeline。你只要把代码推到GitHub,剩下的Cloudflare全包了。」
林一将信将疑:「真有这么好的事?那我的React项目也能部署?」
「React、Vue、Next.js、Astro、SvelteKit……主流框架全支持。来,我给你讲讲这背后的原理。」
技术讲解:从"租房"到"拎包入住"
一、静态网站 vs 动态网站:餐厅的两种模式
「在讲Pages之前,先搞清楚一个基本概念。」老张在白板上写了两个词:静态 和 动态。
「你去一家餐厅吃饭,有两种模式。」
静态网站 就像一家预制菜餐厅——所有菜品提前做好,摆在取餐台上。顾客一来,直接端走吃,不需要厨师现场操作。速度快、成本低,但菜品是固定的,不能现点现做。
HTML、CSS、JS文件提前生成好,放在服务器上,用户访问时直接返回这些文件。没有数据库查询,没有服务端渲染,没有实时计算。
动态网站 就像一家现炒餐厅——顾客点了菜,厨师才开始切菜、炒菜、调味。每道菜都是现做的,可以根据顾客要求调整(少辣、不加香菜)。但你需要请厨师、备厨房,出餐也慢一些。
PHP、Java、Node.js这些后端服务,每次收到请求都要查询数据库、拼装HTML、再返回给用户。功能强,但成本高、维护复杂。
「那我的星火AI前端是哪种?」林一问。
「你的前端是React写的,构建之后生成一堆静态文件——HTML、JS、CSS。这本质上是静态网站。但你的AI功能需要调后端API对吧?这个后端后面用Workers解决。前端本身,用Pages托管就够了。」
老张画了一张对比图:
传统动态网站: 用户 → 服务器 → 查数据库 → 拼HTML → 返回
静态网站: 用户 → CDN → 直接返回HTML/JS/CSS
JAMstack网站: 用户 → CDN → 返回静态页面 → 页面通过JS调API获取动态数据

「第三种就是我们要用的架构——JAMstack。」
二、JAMstack:现代前端的"预制菜+外卖"模式
老张在白板上写下了三个字母:J-A-M。
「JAMstack不是某个具体的技术,而是一种架构理念。它把网站拆成了三层:」
J — JavaScript(动态交互)
页面上的所有动态行为都通过JavaScript在浏览器端完成。页面跳转、表单验证、数据渲染,全靠JS。React、Vue、Svelte这些框架就是干这个的。
A — API(后端服务)
需要动态数据怎么办?不通过服务端渲染,而是通过JS在浏览器里直接调API。API可以是自己的后端,也可以是第三方服务。前端和后端完全解耦,通过HTTP接口通信。
M — Markup(静态标记)
网页的HTML内容在构建时(build time)就生成好了,而不是在用户请求时(request time)动态拼装。构建工具(如Vite、Webpack)把React/Vue组件编译成静态HTML+JS文件。
「用餐厅打比方,」老张说,「JAMstack就是:预制菜提前做好摆在取餐台上(Markup),顾客自己来取(CDN分发),如果需要热一下或者加点调料,自己用微波炉搞定(JavaScript),想加个荷包蛋?单独找厨师点(API)。」
林一恍然大悟:「这不就是我天天点的外卖模式嘛!主菜提前做好,饮料单独加。」
「没错。JAMstack的核心优势就是:静态内容走CDN,快如闪电;动态数据走API,按需获取。 两者各司其职,互不拖累。」

老张总结了JAMstack的好处:
| 优势 | 说明 |
|---|---|
| 速度 | 静态文件走CDN,毫秒级响应,不用等服务器查数据库 |
| 安全 | 没有传统服务器,攻击面大幅缩小,SQL注入都不存在了 |
| 成本 | 静态文件托管几乎零成本,Pages免费套餐就够用 |
| 可扩展 | CDN天然支持高并发,不用担心服务器扛不住 |
| 开发体验 | 前后端分离,前端独立部署,后端API独立迭代 |
「这跟国内的Serverless架构思路类似,」老张补充道,「比如腾讯云的静态网站托管、阿里云的OSS+CDN方案,本质都是把静态前端和动态后端拆开。区别是Cloudflare Pages把Git集成、自动构建、全球CDN一把梭全包了,体验更丝滑。」
三、Cloudflare Pages 工作原理:一条push,全球上线
「好了,概念讲完了。来看看Cloudflare Pages到底怎么工作的。」
老张画了一张流程图:
开发者 push 代码 → GitHub/GitLab 仓库
↓
Cloudflare Pages 监听到 push 事件
↓
拉取代码 → 执行构建命令(如 npm run build)
↓
生成静态文件(dist/ 目录)
↓
上传到 Cloudflare 全球 CDN
↓
用户访问 → 就近节点返回内容

「整个过程全自动。你只需要做一件事:push代码。」
老张解释了几个关键环节:
1. Git集成
Pages支持GitHub和GitLab两种Git提供商。你授权Cloudflare访问你的仓库后,每次push代码,Cloudflare都会收到通知并触发构建。就像你给快递公司设了一个自动取件点——包裹一放上去,快递员自动来取。
2. 自动构建
Cloudflare会在它的构建服务器上执行你指定的构建命令(比如 npm run build),把源代码编译成静态文件。构建环境预装了Node.js、Python、Go等常用运行时,也支持通过环境变量指定Node版本。
3. 全球CDN分发
构建完成后,静态文件会被推送到Cloudflare的全球CDN网络——还记得第3篇讲的那330多个城市的边缘节点吗?文件会被自动分发到这些节点上。用户访问时,直接从最近的节点获取,不用回源。
「这跟你之前用ngrok映射本地完全不是一个量级,」老张说,「ngrok是把你的电脑当服务器,电脑关了网站就没了。Pages是把文件放到全球330多个城市的CDN节点上,你的电脑关了?网站照样活蹦乱跳。」
林一有点不好意思地摸了摸头。
4. 内置HTTPS
还记得第2篇配SSL/TLS的痛苦吗?Pages自动给你配好了HTTPS。每个Pages项目都会获得一个 *.pages.dev 的域名,自带SSL证书。绑定自定义域名时,证书也自动签发。全程不用你操心想证书的事。
四、连接GitHub自动部署:手把手操作
「理论够了,上手操作。」老张拉过林一的键盘。
步骤一:创建Pages项目
-
登录 Cloudflare Dashboard → 左侧菜单 Workers & Pages
-
点击 Create application → Pages → Connect to Git
-
首次使用会弹出GitHub授权页面,授权Cloudflare访问你的仓库。你可以选择授权所有仓库,也可以只授权指定仓库。
老张提醒:「授权的时候注意,如果你选'All repositories',Cloudflare可以访问你账号下所有仓库。建议只选择需要部署的那个仓库,最小权限原则。」
步骤二:选择仓库并配置项目
-
授权完成后,会显示你的仓库列表。选择
spark-ai-frontend仓库。 -
点击 Install & Authorize → Begin setup,进入构建配置页面。
-
Project name:输入
spark-ai。这个名字会生成你的默认域名spark-ai.pages.dev。 -
Production branch:选择
main。这个分支的代码会部署到生产环境,其他分支会生成预览部署。
老张解释:「Production branch就是你正式上线用的分支。一般用
main或master。开发的时候在别的分支上改,改完提PR合并到main,Pages就会自动更新生产环境。」
步骤三:配置构建设置
- 在 Set up builds and deployments 区域配置:
Framework preset: React (Vite) ← 根据你的框架选择
Build command: npm run build ← 框架的构建命令
Build output directory: dist ← 构建产物所在的目录
「如果你用的框架在预设列表里,选了之后构建命令和输出目录会自动填好,不用自己查。」老张说。
官方文档里列出的常见框架预设:
| 框架 | 构建命令 | 构建输出目录 |
|---|---|---|
| React (Vite) | npm run build | dist |
| Vue | npm run build | dist |
| Next.js (Static Export) | npx next build | out |
| Nuxt.js | npm run build | dist |
| Astro | npm run build | dist |
| SvelteKit | npm run build | .svelte-kit/cloudflare |
| Docusaurus | npm run build | build |
| Hugo | hugo | public |
老张补充:「如果你的项目不需要构建步骤(比如纯HTML网站),构建命令留空就行。Pages会直接把仓库里的文件当成静态资源部署。」
- 如果你的项目不在仓库根目录(比如monorepo),在 Root directory (advanced) 里填上子目录路径。
步骤四:配置环境变量
- 在 Environment variables (optional) 区域添加构建时需要的环境变量:
NODE_VERSION = 18 ← 指定Node.js版本
VITE_API_URL = https://api.spark-ai.com ← 前端需要的API地址
「环境变量在构建时注入,」老张解释,「比如你的前端代码里用 import.meta.env.VITE_API_URL 读取API地址,构建时Pages会把它替换成实际值。」
Cloudflare还会自动注入一些系统环境变量,你在构建脚本里可以直接使用:
| 系统变量 | 含义 |
|---|---|
CI | 值为 true,表示在CI环境运行 |
CF_PAGES | 值为 1,表示在Cloudflare Pages环境运行 |
CF_PAGES_COMMIT_SHA | 当前提交的Git commit hash |
CF_PAGES_BRANCH | 当前构建的分支名 |
CF_PAGES_URL | 本次部署的URL地址 |
「这些变量挺有用的,」老张说,「比如你可以在构建脚本里判断 CF_PAGES_BRANCH 是不是 main,来决定用生产环境的API地址还是测试环境的。」
步骤五:部署
- 点击 Save and Deploy。
构建日志会实时输出——安装依赖、执行构建命令、上传文件。整个过程通常1-3分钟。
- 构建完成后,你会得到一个
https://spark-ai.pages.dev的地址,打开就能看到你的网站了。
「以后每次你push代码到main分支,」老张说,「Pages会自动触发构建,1分钟内新版本就上线了。不用手动部署,不用SSH登录服务器,不用重启nginx。push即上线。」
林一看着自己的网站在浏览器里打开,有点激动:「这也太爽了吧?」
五、预览部署:每个PR都是一个"试验田"
「等一下,还有个更爽的功能。」老张说。
「你在开发新功能的时候,肯定不想直接改main分支吧?你会开一个新分支,改完提PR。但问题是,怎么让团队看到你的改动效果?以前你得自己部署一个测试环境,现在Pages帮你做了。」
预览部署(Preview Deployments) 的工作方式:
- 你从main分支切出一个新分支
feature/chat-ui - push到GitHub后,Pages自动触发一次构建
- 生成一个唯一的预览URL:
https://373f31e2.spark-ai.pages.dev - 这个URL会随每次push到该分支而更新
- 在GitHub的PR页面,Cloudflare会自动评论,附上预览链接
main 分支 → 部署到生产环境 spark-ai.pages.dev
feature/chat-ui 分支 → 部署到预览环境 373f31e2.spark-ai.pages.dev
老张解释了预览部署的几个特点:
分支别名:除了hash形式的URL,Pages还会为每个分支创建一个别名URL。比如 feature/chat-ui 分支的别名是 feature-chat-ui.spark-ai.pages.dev(分支名转小写,非字母数字字符替换为连字符)。这个别名始终指向该分支的最新部署。
老张提醒:「分支别名很方便,但也意味着别名URL会随push而变化。如果你想把某个特定版本固定下来,用hash形式的URL——那个是永久的、不可变的。」
SEO友好:预览部署默认会带上 X-Robots-Tag: noindex 响应头,告诉搜索引擎不要索引这些临时URL。不用担心预览环境影响你的SEO排名。
访问控制:默认预览URL是公开的,任何人都能访问。但你可以通过Cloudflare Access给预览部署加上访问控制,只有团队成员才能查看。这在开发未发布功能时很有用。
开启方式:项目Settings → General → Enable access policy
「这个功能太实用了,」林一感叹,「以前给小王看效果,要么截图发企微,要么ngrok映射。现在一个PR链接丢过去,他自己点开看。」
「而且预览环境跟生产环境用的是同一套构建流程、同一套CDN,效果完全一致。不像ngrok,本地环境跟线上环境经常有差异。」

六、构建配置进阶:避坑指南
林一第一次构建就报错了。构建日志显示:
12:03:45 Cloning repository...
12:03:48 Found project at /opt/buildhome/repo
12:03:48 Installing dependencies...
12:03:50 npm ERR! code ENOTSUP
12:03:50 npm ERR! notsup Required: {"node":">=18.0.0"}
12:03:51 npm ERR! notsup Actual: {"npm":"8.19.2","node":"v16.20.0"}
「Node版本不对,」老张瞥了一眼,「你项目的 package.json 里要求Node 18以上,但Pages默认用的是Node 16。加个环境变量指定版本。」
在项目的 Settings → Environment variables 里添加:
NODE_VERSION = 18
重新触发构建,这次成功了。
老张又补充了几个常见的构建坑:
坑1:构建输出目录写错
「Vue + Vite的输出目录是 dist,但有些框架不是。比如Gatsby是 public,Next.js静态导出是 out,Docusaurus是 build。目录写错了,Pages会找不到文件,部署成功但页面404。」
**坑2:路径大小写问题」
「本地开发是macOS,文件系统不区分大小写。但Pages的构建环境是Linux,区分大小写。你 import Header from './components/Header.jsx',但文件实际叫 header.jsx,本地能跑,Pages上构建报错。」
「这个坑我踩过!」林一说,「找了一小时才发现。」
**坑3:.gitignore 忽略了构建配置文件」
「有些同学把 .env 文件gitignore了,但构建配置依赖它。本地能构建,Pages上找不到配置就挂了。环境变量一定要在Pages后台也配一份。」

七、自定义域名:让网站姓"自己"
「现在你的网站在 spark-ai.pages.dev 上跑着,」老张说,「但我们第1篇就注册了 spark-ai.com 这个域名,得把它绑上。」
绑定自定义域名的步骤很简单:
- 进入Pages项目 → Custom domains → Set up a domain
- 输入域名
spark-ai.com(或子域名app.spark-ai.com) - 点击 Continue
因为 spark-ai.com 已经托管在Cloudflare的DNS上了(第1篇的操作),Cloudflare会自动帮你创建CNAME记录,指向你的Pages项目。SSL证书也会自动签发——还记得第2篇讲的边缘证书吗?全自动,不用你管。
「如果你的域名不在Cloudflare上,」老张补充,「你需要手动在DNS服务商那边加一条CNAME记录,指向 <你的项目名>.pages.dev。但既然我们第1篇就把域名托管到Cloudflare了,这一步省了。」
绑定完成后,访问 https://spark-ai.com 就能打开你的网站了。spark-ai.pages.dev 也依然可用。
八、Pages Functions:前端的"后花园"
「还有一个功能得提一嘴,」老张说。「你前面不是问了'没有后端怎么行'吗?Pages其实自带了一个轻量级的后端能力——Pages Functions。」
Pages Functions让你在项目中创建一个 functions/ 目录,放一些服务端代码,Pages会自动把它们部署为Serverless函数。本质上它就是Cloudflare Workers——跑在边缘网络上的JavaScript运行时。
「比如你想做一个简单的API接口,或者处理表单提交,不用单独搭后端,在Pages项目里写几个Functions就行。」
老张在白板上写了个简单示例:
// functions/api/hello.js
export async function onRequest(context) {
return new Response(JSON.stringify({ message: "Hello from 星火AI!" }), {
headers: { "Content-Type": "application/json" },
});
}
部署后访问 https://spark-ai.com/api/hello 就能拿到JSON响应。
「不过Functions的能力有限,复杂业务逻辑还是需要独立的Workers。」老张说,「这个等下一篇讲Workers的时候详细聊。你现在只要知道Pages不只是能放静态文件,还能跑一点点后端代码就够了。」
小结
林一看着浏览器里 https://spark-ai.com 打开的星火AI首页,回味着今天学到的内容。
「总结一下,」老张说,「你今天完成了几件事:」
- 理解了JAMstack架构——静态内容走CDN,动态数据走API,前后端分离,各司其职。
- 用Pages部署了前端——连接GitHub仓库,push代码自动构建自动部署,全程不用碰服务器。
- 搞定了预览部署——每个PR自动生成预览环境,团队协作效率拉满。
- 绑定了自定义域名——
spark-ai.com正式上线,HTTPS自动配置。 - 知道了Pages Functions——轻量级后端能力,后续Workers篇会深入。
「记住,Pages的核心价值不是'免费托管静态文件'——这事儿很多平台都能干。它的核心价值是把部署流程做到了极致简单:Git集成、自动构建、预览部署、全球CDN,一条龙服务,你只管写代码。」
林一点点头,然后突然想起了什么:「张哥,那后端API呢?星火AI的智能问答功能,前端调的API从哪来?」
老张站起身,拍了拍裤子上的灰:「好问题。一个没有后端的网站,就像一家没有厨房的餐厅——门面再好看,顾客也吃不上饭。明天教你用Cloudflare Workers搭后端API,那才是真正的重头戏。」
本篇知识点回顾
| 知识点 | 要点 |
|---|---|
| 静态网站 vs 动态网站 | 静态=预制菜提前做好直接上菜;动态=现点现炒需要厨师 |
| JAMstack架构 | J(JavaScript动态交互) + A(API后端服务) + M(Markup静态标记),前后端分离 |
| Pages工作原理 | push代码→自动构建→生成静态文件→分发到全球CDN |
| Git集成 | 支持GitHub和GitLab,授权后每次push自动触发构建 |
| 框架预设 | React/Vue/Next.js/Astro等20+框架预设,自动填构建命令和输出目录 |
| 构建命令 | 框架提供的build命令(如 npm run build),无框架留空 |
| 构建输出目录 | 构建产物所在目录(如 dist/public/build),按框架而定 |
| 环境变量 | 构建时注入,系统变量含 CF_PAGES_BRANCH、CF_PAGES_COMMIT_SHA 等 |
| 预览部署 | 非生产分支自动生成预览URL(hash+分支别名),默认带noindex |
| 分支别名 | branch-name.pages.dev 形式,始终指向该分支最新部署 |
| 预览访问控制 | 通过Cloudflare Access限制预览环境访问权限 |
| 自定义域名 | 项目→Custom domains→输入域名,DNS在CF上则自动配置CNAME和证书 |
| Pages Functions | functions/目录下的代码自动部署为Serverless函数,本质是Workers |
| 构建坑:Node版本 | 通过 NODE_VERSION 环境变量指定Node.js版本 |
| 构建坑:大小写 | Linux区分文件名大小写,macOS不区分,注意import路径 |
| 构建坑:输出目录 | 目录写错会导致部署成功但页面404 |
动手挑战
-
基础题:把一个前端项目(React/Vue/纯HTML都行)部署到Cloudflare Pages,确保能通过
*.pages.dev域名访问。 -
进阶题:创建一个新分支修改页面内容,push后查看预览部署URL,在GitHub PR页面找到Cloudflare自动评论的预览链接。然后合并PR到main分支,观察生产环境自动更新。
-
思考题:你的项目同时需要开发环境和生产环境不同的API地址。如何利用Pages的环境变量和系统变量
CF_PAGES_BRANCH,让构建时自动选择正确的API地址?(提示:可以在构建脚本中根据分支名判断,也可以在Pages后台为Production和Preview分别配置不同的环境变量)
下回预告
星火AI的页面终于上线了,林一兴冲冲地发给小王看。小王回了一句:「页面挺好看的,但智能问答功能怎么用不了?点了按钮没反应啊。」
林一一看控制台,满屏的红色错误:API request failed — 404 Not Found。
「废话,」老张在旁边说,「你前端调的 /api/chat 接口根本不存在。一个没有后端的网站就像没有厨房的餐厅——门面再好看,顾客也吃不上饭。明天教你用Cloudflare Workers搭后端API,让你的星火AI真正'亮'起来。」
「Workers又是什么?」
「Serverless。边缘计算。你的代码跑在全球330个城市的节点上,离用户最近的那个响应请求。还是免费的。」
林一的眼睛又亮了。
本文是《林一的 Cloudflare 通关记》系列第4篇。 上一篇:给网站请个超级快递员——CDN加速与缓存策略 下一篇:网站要有个"厨房"——Cloudflare Workers搭建后端API
