tech-interview-handbook 网站(Docusaurus 2 + Vite+ 单体仓库)的本地开发、构建与部署实战指南
本文以 apps/website/README.md 为主线,讲清 Tech Interview Handbook 官网(Docusaurus 2 静态站点)在 Vite+ 单体仓库(monorepo)中的完整工作流:依赖安装(vp install)、本地开发(vp run dev)、生产构建(vp run --filter @tih/website build)以及 GitHub Pages 部署。读完本文,你能独立完成该网站的本地运行与构建发布,并理解 docusaurus.config.js、sidebars.js 等核心配置项以及 src/theme 下的主题定制实现,具备二次开发该站点的能力。
一、网站在仓库中的定位
Tech Interview Handbook 仓库是一个 pnpm workspace 管理的单体仓库,根目录 package.json 声明了两个 workspace 范围:
"workspaces": [
"packages/*",
"apps/*"
]
其中 apps/website 即本文的主角——由 Docusaurus 2 驱动的静态文档站点,包名为 @tih/website(见 apps/website/package.json)。仓库另一个应用 apps/portal 是 Next.js 应用,与网站相互独立;本文不涉及它。
关键依赖版本(以 apps/website/package.json 为准):
| 依赖 | 版本 | 作用 |
|---|---|---|
@docusaurus/core |
^2.4.3 |
Docusaurus 核心,提供文档/博客/搜索等基础设施 |
@docusaurus/preset-classic |
^2.4.3 |
经典预设,聚合文档、博客、Algolia 搜索、主题配置 |
@docusaurus/plugin-client-redirects |
^2.4.3 |
客户端重定向插件 |
react / react-dom |
^18.2.0 |
React 18 运行时 |
apps/website/README.md 开篇即点明该站点是仓库 "Vite+ monorepo setup" 的一部分:所有命令都通过全局 vp CLI(Vite+)执行,而不是直接调用 pnpm 或 docusaurus 二进制。根目录 package.json 的 engines 与 packageManager 字段锁定了运行环境前提:
"engines": {
"node": "25.8.1",
"pnpm": "10.32.1"
},
"packageManager": "pnpm@10.32.1"
仓库的 AGENTS.md 还给出了明确的 Vite+ 使用约束:不要直接使用 pnpm/npm/Yarn,依赖操作一律通过 vp 包装命令完成;内置命令(如 vp dev)与同名 package.json 脚本不冲突,脚本必须用 vp run <script> 显式运行。这一点对理解下面所有命令至关重要。
二、安装依赖:vp install
apps/website/README.md 给出的第一步是在仓库根目录执行:
$ vp install
Vite+ 会根据根 package.json 的 packageManager: pnpm@10.32.1 自动包装底层 pnpm 完成整个 workspace(packages/* + apps/*)的依赖安装。workspace 结构由 pnpm-workspace.yaml 声明:
packages:
- 'apps/*'
- 'packages/*'
值得注意的是该文件还定义了 pnpm catalog: 版本目录(vite、vitest、vite-plus 等统一从 catalog 取版本),并且通过 overrides 强制 vite/vitest 走 catalog 版本。这意味着 vp install 一次安装即可让 @tih/website 与 @tih/portal 的依赖同时就绪——不需要 cd apps/website 后再装一次。
三、本地开发:vp run dev
同样在仓库根目录:
$ vp run dev
这条命令的调用链可以从源码结构中完整还原:
- 根 package.json 的 scripts 定义了
"dev": "vp run --filter @tih/website... dev",即按 pnpm workspace 过滤器只作用于@tih/website及其依赖包; - apps/website/package.json 的 scripts 定义了
"dev": "docusaurus start"(另有等价的"start": "docusaurus start"); - 最终由 Docusaurus CLI 启动本地开发服务器并自动打开浏览器。
README 中说明的行为同样适用于此:对 JavaScript 和 Markdown 文件的绝大多数修改都会即时热更新,无需重启服务。由于 Docusaurus 的文档内容全部是 Markdown 文件(位于 apps/website/contents 目录),改完一篇面试指南文档后保存即可在浏览器中立刻看到效果。
一个容易踩的坑(AGENTS.md 中有专门提示):vp run dev 运行的是 package.json 脚本;如果直接输入 vp dev,启动的会是 Vite 内置 dev server 而非 Docusaurus。两者语义不同,务必使用 README 中的 vp run dev 形式。
四、生产构建:vp run --filter @tih/website build
从仓库根目录执行:
$ vp run --filter @tih/website build
该命令直接对应 apps/website/package.json 中的 "build": "docusaurus build" 脚本。docusaurus build 会:
- 以 docusaurus.config.js 为入口装配整个站点;
- 把 apps/website/contents 下的 Markdown 文档、apps/website/blog 下的博客文章编译为静态 HTML;
- 产出静态内容到
build目录(README 明确说明生成位置为build目录,且可被任意静态内容托管服务直接服务)。
根 package.json 还有一个聚合脚本 "build": "vp run --cache -r build"(-r 即 recursive,对所有 workspace 跑 build),CI 脚本 "ci": "vp check && vp test && vp run --cache -r build" 则把格式/类型检查、测试与构建串成一条流水线。本地只关心网站时,使用 README 中 --filter @tih/website 的形式更快、范围更小。
五、部署到 GitHub Pages
apps/website/README.md 给出的部署命令:
$ cd apps/website
$ GIT_USER=<Your GitHub username> USE_SSH=1 vp run deploy
要点拆解:
- 该命令对应 apps/website/package.json 的
"deploy": "docusaurus deploy"脚本,即 Docusaurus 内置的 deploy 子命令:先执行docusaurus build,再把产物推送到gh-pages分支; - 环境变量
GIT_USER指定 git 提交者身份,USE_SSH=1让推送走 SSH 协议; - README 说明这是使用 GitHub Pages 托管时的便捷方式:一条命令完成"构建 + 推送 gh-pages"。
适用前提:本地配置了该仓库的 git 远程访问凭据,且 gh-pages 分支有推送权限。除此之外,README 也保留了更通用的路径——构建产物在 build 目录,可交给任意静态托管服务,不局限于 GitHub Pages。
六、核心配置解读:docusaurus.config.js
apps/website/docusaurus.config.js 是本地开发与构建行为的核心控制文件,结合源码可以逐段理解:
基础元信息
title: 'Tech Interview Handbook',
tagline: 'Free curated interview preparation materials for busy people',
url: 'https://www.techinterviewhandbook.org',
baseUrl: '/',
trailingSlash: true,
url + baseUrl 决定生成的绝对 URL 与 sitemap 基础地址;trailingSlash: true 表示所有路由带尾部斜杠(如 /algorithms/array/),这与 apps/website/static/_redirects 等静态部署文件的配合方式一致。
docs 预设:内容目录与路由
presets: [
['@docusaurus/preset-classic', {
docs: {
path: './contents', // 文档源目录
routeBasePath: '/', // 文档挂在站点根路由
sidebarPath: require.resolve('./sidebars.js'),
showLastUpdateTime: true,
},
...
}],
]
path: './contents'指明文档 Markdown 的源目录是 apps/website/contents(而非 Docusaurus 默认的docs/),里面包含 software-engineering-interview-guide.md、coding-interview-prep.md 等指南文章,以及 algorithms 子目录下的算法专题;routeBasePath: '/'让文档直接占据站点根路径,例如/resume/、/system-design/;showLastUpdateTime: true在每篇文档底部展示最后修改时间。
侧边栏:sidebars.js
sidebarPath 指向 sidebars.js,它用手工分类(autogenerated 之外的显式声明)组织了整个学习路径:
Introduction→software-engineering-interview-guideCoding interview preparation→coding-interview-prep、coding-interview-cheatsheet、coding-interview-techniques、mock-interviews等Algorithms study cheatsheets→ 再按 Basics / Data structures / Advanced data structures / Additional 分组挂载algorithms/array、algorithms/tree、algorithms/graph等Salary and offer negotiation preparation、Beyond the interview等
新增一篇文档后,只需在 contents 添加 Markdown 并在 sidebars.js 中注册对应 id,本地 vp run dev 即可在侧边栏看到——这也是开发循环中最常用的两步。
搜索、统计与站点地图
配置中还有三块运维向设置:
themeConfig.algolia:接入 Algolia DocSearch(appId: 'Y09P1J4IPV'、indexName: 'techinterviewhandbook'),提供站点内全文搜索;blog: { blogSidebarCount: 15 }:博客列表侧栏显示 15 篇;sitemap.ignorePatterns:显式排除旧博客文章、/algorithms/oop/、/search/等路径,避免这些页面进入 sitemap;plugins中挂载了@docusaurus/plugin-google-gtag(anonymizeIP: true),themeConfig内另有gtag的trackingID——两者共同承担访问统计,与 docusaurus.config.js 中 preset 级gtag配置相互独立。
导航方面,themeConfig.navbar.items 声明了 Start reading / Coding / Algorithms / Blog / Grind 75 等入口,hideOnScroll: true 使导航栏滚动时隐藏;themeConfig.footer 为 dark 风格四栏页脚。
七、主题定制:src/theme 下的 Swizzle 组件
Docusaurus 的主题定制惯例是通过 swizzle 复制默认组件再修改(apps/website/package.json 提供了 "swizzle": "docusaurus swizzle" 脚本支持该流程)。从 apps/website/src/theme 的目录结构看,该仓库定制了三个组件:
- 文档内容区 — DocItem/Content/index.js:
- 实现了 Docusaurus 的 "synthetic title" 逻辑:当 front matter 未设置
hide_title且正文本身没有顶层 H1 时,用metadata.title渲染出合成标题,保证两种标题写法渲染一致(文件内注释引用了上游 PR #4882 的讨论背景); - 在正文上方固定注入 GitHub Star 按钮(iframe 引入
ghbtns.com)与作者介绍区块,再挂载MDXContent渲染正文。
- 实现了 Docusaurus 的 "synthetic title" 逻辑:当 front matter 未设置
- 右侧目录 — TOC/index.js:在标准
TOCItems之上插入 SidebarAd 组件(position="table_of_contents"),并用固定 class 名控制 TOC 高亮行为。 - 移动端侧边栏 — DocSidebar/Mobile/index.js:在
DocSidebarItems之后追加SidebarAd(position="mobile_sidebar"),并定制了点击行为——只有当分类带链接或为外链时才关闭移动端侧边栏。
apps/website/src/components/SidebarAd/index.js 从源码结构看是一个"按路由条件渲染、带定时刷新"的广告位组件:用 BrowserOnly 包裹以避免 SSR/客户端 hydration 不一致(源码注释明确说明了这一动机),每 20 秒(AD_REFRESH_RATE)重新抽签一次,并根据 window.location.pathname 分流:resume 页展示 FAANGTechLeads、system-design 页在 ByteByteGo 与 DesignGurus 之间各 50% 概率、默认在 FAANGTechLeads 与 GreatFrontEnd 之间随机;每次点击都会通过 window.gtag('event', ...) 上报事件——这与第六节 gtag 插件形成完整闭环。
这些定制正是 README 所说"JavaScript 文件的修改即时热更新"的直观受益对象:改动 TOC/index.js 或 SidebarAd 后,开发服务器会自动刷新页面。
八、静态资源与部署相关文件
build 产物中会原样携带 apps/website/static 目录的内容,其中与部署强相关的文件包括:
- static/CNAME:GitHub Pages 的自定义域名声明,配合
url配置让站点以正式域名访问; - static/_redirects:静态托管(如 Netlify 风格)的 URL 重定向规则;
- static/ads.txt 与 static/img(社交分享图、文章配图等,社交图按 social/algorithms 等子目录组织,与文档 slug 一一对应)。
此外,apps/website/functions 目录下的 grind75/[[catchall]].js 是平台无关的 serverless 函数,为站点的 Grind 75 页面提供动态能力——构建该目录需依赖具体的函数托管平台,README 未展开其部署方式,这里仅作结构说明。
九、常用命令速查与注意事项
汇总 apps/website/README.md 与 package.json、apps/website/package.json 中的脚本,实际可用的命令如下:
| 目的 | 命令(工作目录) | 实际映射 |
|---|---|---|
| 安装依赖 | vp install(仓库根目录) |
pnpm workspace 全量安装 |
| 本地开发 | vp run dev(仓库根目录) |
@tih/website 的 docusaurus start |
| 仅构建网站 | vp run --filter @tih/website build(仓库根目录) |
docusaurus build → build/ |
| 构建全部 workspace | vp run --cache -r build(仓库根目录) |
各包 build 脚本,带缓存 |
| 类型/格式/静态检查 | vp check(仓库根目录) |
Vite+ 内置 check |
| 网站 lint | vp lint docusaurus.config.js sidebars.js src(apps/website) |
@tih/website 的 lint 脚本 |
| 部署 | GIT_USER=<用户名> USE_SSH=1 vp run deploy(apps/website) |
docusaurus deploy → gh-pages |
注意事项(均有仓库内依据):
- 环境与包管理:Node 25.8.1 / pnpm 10.32.1(package.json 的
engines),不要绕过vp直接调用 pnpm(AGENTS.md); vp run <script>与vp <command>语义不同,同名脚本必须走vp run(AGENTS.md 的 Common Pitfalls);- 文档内容源在
contents/而非docs/,新增文章记得同步维护 sidebars.js; vp check/vp test是 AGENTS.md 中建议 Agent 在动手前后执行的验证手段,可配合ci脚本做整体验证。
十、小结
apps/website 的完整生命周期可以概括为:vp install 安装 → vp run dev 开发 → vp run --filter @tih/website build 产出 build/ → vp run deploy 推送 gh-pages。它的设计特点是"内容(Markdown)与呈现(Docusaurus 配置 + swizzle 主题组件)分离、命令入口统一收敛到 Vite+":内容作者只需编辑 contents 下的 Markdown,而工程侧的所有行为都由 docusaurus.config.js、sidebars.js 与 src/theme 三个位置精确控制。掌握这条从源码可验证的链路,就能对该站点做任何从加一篇文档到改一处主题组件的本地开发工作。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00