使用 grunt 一键发布到 Vercel、Netlify、Cloudflare
解读
面试官抛出“一键发布”话题,表面看是问“能不能”,实质在考察候选人是否理解国内网络环境、CI/CD 流程与 Grunt 插件生态的边界。
国内团队普遍把 Grunt 当“本地构建机”,而 Vercel、Netlify、Cloudflare Pages 的入口都是 Git 推送触发或 CLI 调用;如何把 Grunt 的构建产物准确、快速、可回滚地送到三家境外托管,并兼顾备案、域名解析、环境变量、缓存策略,才是得分点。
回答时切忌只说“装个 grunt-shell 调 vercel deploy”,而要给出“本地构建 → 产物校验 → 多目标上传 → 回滚钩子”的完整闭环,并指出 Grunt 在 2025 年国内项目中的定位:负责构建层,发布层交给平台 CLI 或 GitHub Action,但用 Grunt 做门禁与编排。
知识点
- Grunt 运行模型:任务链、并发控制、文件映射、时间戳指纹。
- 三家平台国内可用性:
- Vercel:边缘节点被墙概率高,需自定义 CDN 回源;CLI 支持
--prod与--token。 - Netlify:备案域名可接入,但自动 HTTPS 证书签发偶发超时;CLI 支持
--dir指定目录。 - Cloudflare Pages:绑定 CNAME 需先完成工信部备案,且 Pages 函数(Functions)与大陆访问延迟大。
- Vercel:边缘节点被墙概率高,需自定义 CDN 回源;CLI 支持
- 产物合规:HTML 中不能出现“NEXT_DATA 含敏感词”、JS 文件名不能带 ad 等关键字,否则触发平台自动拦截。
- 鉴权方式:
- 环境变量
VERCEL_TOKEN、NETLIFY_AUTH_TOKEN、CLOUDFLARE_API_TOKEN必须走.env.grunt加载,不能硬编码进 Gruntfile。
- 环境变量
- 缓存与回滚:
- 利用 Grunt 的
grunt-contrib-rename给产物目录加{gitHash}前缀,上传后通过平台 API 设置alias,实现秒级回滚。
- 利用 Grunt 的
- 国内加速:
- 构建结束后把产物同时推到「阿里云 OSS + 镜像回源」作为备案主站,Vercel 仅作海外流量入口,Grunt 任务里用 grunt-aliyun-oss 插件并行上传。
答案
“我会把发布拆成三段,全部收进一个 grunt publish 主任务,确保任何同事在仓库根目录执行 npx grunt publish 就能一键上线。
第一段,本地构建与产物校验:
- 用
grunt-contrib-clean清掉 dist。 webpack:client把 TSX 打到 dist/static,输出带 hash 的 JS/CSS。grunt-contrib-htmlmin压缩 HTML,随后grunt-inline-assets把小图片转 base64,减少请求数。- 自定义子任务
grunt lint-dist,遍历 dist 下所有.js文件,走国内云函数做敏感词扫描,失败直接中断,防止境外平台因合规下架整个站点。
第二段,多目标上传:
- 通过
grunt-env把.env.grunt加载到process.env,确保 token 不落源码。 - 注册三个子任务:
vercel-deploy:用grunt-shell执行npx vercel deploy dist --prod --token=$VERCEL_TOKEN --meta commit=$GIT_HASH,返回的 URL 写入vercel.json。netlify-deploy:先npx netlify deploy --dir=dist --prod --auth=$NETLIFY_AUTH_TOKEN,拿到 deploy_id 后调netlify api updateSite把production_branch指向本次 commit,保证国内备案域名立刻生效。cloudflare-deploy:调用npx wrangler pages publish dist --project-name=xxx,成功后把CF_PAGES_URL回写grunt.config('cf.url'),供后续钉钉通知。
- 三个子任务用
grunt-concurrent限流 2 并发,防止本地出口带宽被打满导致 CI 超时。
第三段,门禁与回滚:
- 上传完成后触发
grunt http任务,分别请求三家的 API 获取status=READY并断言 HTTP 200,任何一家失败即调用对应平台的 rollback API 并退出码 1,让 GitHub Action 显示红叉。 - 最后
grunt-contrib-rename把 dist 重命名为dist-${GIT_HASH}归档到releases/目录,本地留痕,方便秒级回滚。
整个 Gruntfile 约 180 行,无敏感信息,全部参数走环境变量,已在公司 GitLab CI 与开发者本机 macOS/Windows 双验证,平均构建+发布 90 秒,符合国内团队“下班前一键上线”的节奏。”
拓展思考
- 如果公司强制要求“源码不出内网”,可把 Grunt 构建产物推到内网 Nexus 的 raw 仓库,再通过「云效+自定义节点」把产物同步到境外平台,Grunt 侧只负责生成 checksum 与签名文件,实现“构建在内、发布在外”。
- 当项目转向 monorepo,Grunt 的
grunt-multi-dest插件可以按 package 维度并行发布到三家平台的不同子目录,利用 Vercel 的“monorepo detector”自动识别根目录下 vercel.json,无需每个子包写配置。 - 国内备案域名与海外托管的 SEO 冲突,可在 Grunt 任务里插入
grunt-sitemap生成两套 sitemap:一套指向备案主域,一套指向 Vercel 提供的边缘域名,通过 Search Console 的国际化定位把权重收拢到主域,既享受边缘加速又避免重复收录。