# 选技术栈并搭骨架：第一次提交

> 为书单项目选定 Astro、Tailwind 和 Cloudflare Workers，让 Claude Code 初始化项目、写好 CLAUDE.md、完成第一次 git 提交并推到 GitHub。

- 网址：https://coding.chengyu.dev/learn/ship/scaffold/
- 学习路径：实战：从想法到上线一个网站
- 预计用时：30 分钟；核实日期：2026-10-25
- 官方来源：[Deploy your Astro site to Cloudflare Workers（Astro 官方文档）](https://docs.astro.build/en/guides/deploy/cloudflare/)，[@astrojs/cloudflare 适配器（Astro 官方文档）](https://docs.astro.build/en/guides/integrations-guide/cloudflare/)，[Install and set up Astro（Astro 官方文档）](https://docs.astro.build/en/install-and-setup/)，[Styling and CSS：添加 Tailwind（Astro 官方文档）](https://docs.astro.build/en/guides/styling/)，[Astro on Cloudflare Workers（Cloudflare 官方文档）](https://developers.cloudflare.com/workers/framework-guides/web-apps/astro/)，[Wrangler configuration（Cloudflare 官方文档）](https://developers.cloudflare.com/workers/wrangler/configuration/)，[Manage Claude's memory（Claude Code 官方文档）](https://code.claude.com/docs/en/memory)，[gh repo create（GitHub CLI 官方手册）](https://cli.github.com/manual/gh_repo_create)

## 学习目标

- 能说清这套技术栈每一层为什么这样选
- 能让 Claude Code 初始化 Astro + Tailwind + Cloudflare 项目并验证能构建
- 能写出精简有效的项目 CLAUDE.md，把技术栈和红线固定下来
- 能在提交前检查没有密钥进入仓库，并把代码推到 GitHub

有了规格，下一步是让项目「立起来」：能启动、能构建、有版本管理、代码在远端有备份。这一步看起来平淡，却决定了后面五课的质量。骨架搭歪了，后面每次让智能体改东西，都会在歪的地基上继续歪。

这一课有两个目标：**选对栈并把选择固定下来**，**完成第一次提交**。

## 为什么选这套栈

选技术栈的标准不是「最先进」，而是**对一个人维护的小项目最省心**。书单项目的需求很朴素：几个页面、一张表、一个调用 AI 的接口。

| 层 | 选择 | 理由 |
|---|---|---|
| 页面 | Astro | 默认输出很少的 JavaScript，内容型页面很快；组件用 `.astro` 文件，智能体写起来不容易出错 |
| 样式 | Tailwind CSS | 样式写在标签旁边，智能体改一个组件不会牵连全局；设计令牌用 CSS 变量，下一课细讲 |
| 运行环境 | Cloudflare Workers（含静态资源） | 页面和接口放在同一个 Worker 里，一次部署，没有单独的服务器要管 |
| 数据库 | D1 | Cloudflare 托管的 SQLite，免费额度对个人项目足够，用法就是 SQL |
| AI | Workers AI | 在同一个平台里直接调用模型，不需要另外申请密钥 |
| 代码托管与部署 | GitHub + GitHub Actions | 推送即部署，第 6 课配置 |

一个值得单独说的变化是 **Workers 与 Pages**。早几年部署前端框架到 Cloudflare，大家默认用 Pages。现在 Cloudflare 的框架指南和 Astro 官方部署文档都以 Workers 为准：Workers 可以同时托管静态资源和服务端代码，配置统一写在 `wrangler` 配置文件里。Astro 的 Cloudflare 适配器在 v13 起已经移除了对 Pages 的支持。所以如果你在网上看到「创建 Pages 项目、连接 Git 仓库」的老教程，不要照着做，以官方当前文档为准。

另一个变化是配置文件格式。Cloudflare 现在推荐新项目使用 `wrangler.jsonc`（带注释的 JSON），而不是早年的 `wrangler.toml`。部分较新的功能只在 JSON 格式的配置里提供。

> **版本信息**
>
> 本课写于 2026 年 10 月：Astro 为 7.x，Wrangler 为 4.x。Astro 官方的 Cloudflare 适配器页面目前写的是 Astro 6 与适配器 v13/v14 的说明，没有专门提到 Astro 7。所以**不要凭记忆写配置**：用 `npx astro add cloudflare` 让工具生成，再对照官方页面检查。

## 让 Claude Code 搭骨架

骨架搭建是典型的「步骤明确、结果可检查」的任务，很适合交给智能体。但要注意两点：

- **把栈和版本说死**。否则它可能顺手选 Next.js，或者用旧的 Pages 配置。
- **让它自己验证**。构建通过才算完成，别接受「已创建」这种自述。

整个过程的命令，其实只有这几条（智能体会替你执行，你要能看懂）：

```bash
# 1. 创建 Astro 项目（交互式，选「空项目/minimal」模板，不要选带示例的）
npm create astro@latest my-shelf

# 2. 添加 Cloudflare 适配器：安装依赖并更新 astro.config.mjs，同时生成 wrangler.jsonc
npx astro add cloudflare

# 3. 添加 Tailwind：安装官方 Vite 插件，并创建 src/styles/global.css
npx astro add tailwind

# 4. 把 wrangler 装成开发依赖，保证团队和 CI 用同一个版本
npm install -D wrangler@latest
```

如果你是在已有 `SPEC.md` 的目录里操作，`npm create astro@latest` 可以用 `.` 作为目标目录。执行前让智能体先确认目录里只有 `SPEC.md`。

适配器装好后，`wrangler.jsonc` 大致是这个形状（以官方文档为参照，实际字段以生成的为准）：

```jsonc
{
  "name": "my-shelf",
  "main": "@astrojs/cloudflare/entrypoints/server",
  "compatibility_date": "2026-10-25",
  "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
  "assets": {
    "binding": "ASSETS",
    "directory": "./dist"
  },
  "observability": { "enabled": true }
}
```

几个字段的含义：

- `main` 是 Worker 的入口，由适配器提供；
- `assets` 告诉 Wrangler 把构建产物 `dist` 目录当静态资源上传；
- `compatibility_date` 决定运行时行为的版本，**填你创建项目当天的日期**，之后不要随手改；
- `observability` 打开 Workers 日志，第 6 课查日志时会用到。

模板自带的 `.gitignore` 内容以实际生成的为准，不一定覆盖 Cloudflare 相关的本地文件。创建后让智能体确认它至少包含这些：

```text
node_modules/
dist/
.astro/
.wrangler/
.dev.vars*
.env*
```

`.dev.vars` 是 Wrangler 约定的本地密钥文件，后面存放管理令牌。**它必须在第一次提交之前就被忽略**。

## 写项目的 CLAUDE.md

`CLAUDE.md` 是 Claude Code 每个会话开头都会读的项目说明。官方的建议是：只写「删掉就会让它犯错」的内容，越短越好。在项目里运行 `/init` 可以生成一份草稿，再手工收紧。（写法细节见 [CLAUDE.md 与记忆](/learn/claude-code/claude-md/)。）

书单项目的 `CLAUDE.md` 可以这样写：

```markdown
# 个人书单（my-shelf）

规格见 @SPEC.md。需求变化时先改 SPEC.md，再改代码。

## 技术栈（不要替换）
Astro 7（on-demand 渲染）+ Tailwind CSS 4 + Cloudflare Workers（含静态资源）
+ D1（绑定名 DB）+ Workers AI（绑定名 AI）。配置文件用 wrangler.jsonc，不用 Pages。

## 常用命令
- `npm run dev`：本地开发
- `npm run build`：构建，改完代码必须跑通
- `npx wrangler types`：改完 wrangler.jsonc 后重新生成类型
- 数据库迁移：`npx wrangler d1 migrations apply my-shelf-db --local`

## 红线
- 绝不读取、打印或提交 `.dev.vars`、`.env*`；密钥只通过 `wrangler secret` 或环境变量
- 绝不对 `--remote` 的数据库执行写操作，也不执行 `wrangler deploy`，除非我明确要求
- SQL 一律使用 `bind()` 参数绑定，禁止字符串拼接
- 不要用 `set:html` 渲染用户输入
- 不新增 SPEC.md 里「不做什么」列出的功能
```

这份文件只有二十来行，但每一行都在防一种具体的失误。你会发现它和 `.gitignore` 是配套的：一个管智能体的行为，一个管仓库里有什么。

CLAUDE.md 只是「提示」，不是强制。如果某条红线你一刻也不想它越过，比如读取 `.dev.vars`，还要在 `.claude/settings.json` 里加一条 `Read` 拒绝规则，让权限系统来拦（见 [权限与安全](/learn/claude-code/permissions/)）：

```json
{
  "permissions": {
    "deny": ["Read(./.dev.vars)", "Read(./.env)"]
  }
}
```

## 第一次提交：先看再提交

提交之前，让智能体做两件事：`npm run build` 验证骨架能构建，`git status` 和 `git diff --stat` 列出将要提交的文件。**你要亲眼看一遍这份清单**，重点找三类东西：密钥文件、`node_modules`、`dist`。这一步在第 6 课也会有，只是那时会交给 CI。

```text
$ git init && git add -A && git status --short
  A  .gitignore
  A  CLAUDE.md
  A  SPEC.md
  A  astro.config.mjs
  A  package.json
  A  package-lock.json
  A  src/pages/index.astro
  A  src/styles/global.css
  A  wrangler.jsonc
# 清单里没有 .dev.vars、.env、node_modules、dist，可以提交
$ git commit -m "chore: 初始化 Astro + Tailwind + Cloudflare 骨架"
$ gh repo create my-shelf --private --source=. --push
# 私有仓库创建成功并推送；公开前先想清楚仓库里有没有不该公开的内容
```

`gh repo create` 的三个参数分别是：`--private` 选择私有仓库，`--source=.` 以当前目录为来源，`--push` 把已有提交推上去。这条命令会在你的 GitHub 账号下创建仓库，属于外部写入，**建议你自己确认后在终端执行**，或者在智能体询问时明确点头。

之后的约定也提前说好：**每课在一个分支上做，合并前看 diff**。直接往 `main` 推没有硬性禁止，但从现在起养成习惯，第 6 课配置自动部署后，推到 `main` 就意味着上线。

## 常见坑

- **智能体换了栈**。症状是出现 `next.config.js` 或 `functions/` 目录。原因通常是 CLAUDE.md 没写栈，或者写得太含糊。
- **照着旧教程配了 Pages**。症状是出现 `_routes.json` 之类的文件，或者适配器版本过旧。让智能体对照上面列的官方页面重做。
- **`compatibility_date` 被改来改去**。它影响运行时行为，应该是一次性设定，除非你有意升级。
- **构建能过，运行时才报错**。骨架阶段只能证明「编译通过」，数据库和 AI 要到第 4 课才会真正被用到。

**搭建书单项目骨架**

```text
读取 SPEC.md，在当前目录搭建项目骨架。技术栈固定为：Astro（最新稳定版）+ Tailwind CSS（用 `npx astro add tailwind`）+ Cloudflare Workers（用 `npx astro add cloudflare`），配置文件使用 wrangler.jsonc，不要使用 Cloudflare Pages。

步骤：
1. 目录里只有 SPEC.md 时，用 `npm create astro@latest .` 的最小模板初始化；创建过程中不要选择示例内容；
2. 添加 Cloudflare 适配器和 Tailwind，并安装 wrangler 作为开发依赖；
3. 打开 wrangler.jsonc，把 name 改为 my-shelf，确认 main、assets、observability 与 Astro 官方 Cloudflare 部署文档一致，compatibility_date 使用今天的日期；
4. 确保 .gitignore 包含 node_modules、dist、.astro、.wrangler、.dev.vars*、.env*；
5. 在 package.json 里加一个 `"types": "wrangler types"` 脚本；
6. 生成 CLAUDE.md：技术栈、常用命令、红线（不碰密钥文件、不对远程数据库写入、不自行部署、SQL 只用参数绑定），控制在 30 行内；
7. 运行 npm run build，把结果贴给我。

完成后不要提交。运行 git status，列出所有将被提交的文件，并逐项说明为什么需要它，等我确认后再提交。
```

> 小测：下面哪个说法最准确地描述了 Cloudflare 上部署 Astro 的当前推荐做法？

> 小测：为什么 `.dev.vars` 要在第一次 git 提交之前就加入 .gitignore？

## 动手任务

- [ ] 用提示词让 Claude Code 搭好骨架，`npm run build` 通过
- [ ] 打开 `wrangler.jsonc` 和 `.gitignore`，逐项对照本课内容检查
- [ ] 运行 `npm run dev`，在浏览器里确认首页能打开
- [ ] 读懂并收紧 `CLAUDE.md`，用 `/context` 或让 Claude 复述一遍，确认它被加载
- [ ] 提交前检查 `git status`，确认没有密钥文件，然后完成第一次提交并推送到 GitHub 私有仓库

下一课：[做界面](/learn/ship/build-ui/)。骨架已经有了，我们先用假数据把页面做出来，后端留到第 4 课。
