首页
/ tech-interview-handbook 网站(Docusaurus 2 + Vite+ 单体仓库)的本地开发、构建与部署实战指南

tech-interview-handbook 网站(Docusaurus 2 + Vite+ 单体仓库)的本地开发、构建与部署实战指南

2026-09-03 16:07:58作者:卓炯娓

本文以 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.jssidebars.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.jsonenginespackageManager 字段锁定了运行环境前提:

"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.jsonpackageManager: pnpm@10.32.1 自动包装底层 pnpm 完成整个 workspace(packages/* + apps/*)的依赖安装。workspace 结构由 pnpm-workspace.yaml 声明:

packages:
  - 'apps/*'
  - 'packages/*'

值得注意的是该文件还定义了 pnpm catalog: 版本目录(vitevitestvite-plus 等统一从 catalog 取版本),并且通过 overrides 强制 vite/vitest 走 catalog 版本。这意味着 vp install 一次安装即可让 @tih/website@tih/portal 的依赖同时就绪——不需要 cd apps/website 后再装一次。

三、本地开发:vp run dev

同样在仓库根目录:

$ vp run dev

这条命令的调用链可以从源码结构中完整还原:

  1. package.json 的 scripts 定义了 "dev": "vp run --filter @tih/website... dev",即按 pnpm workspace 过滤器只作用于 @tih/website 及其依赖包;
  2. apps/website/package.json 的 scripts 定义了 "dev": "docusaurus start"(另有等价的 "start": "docusaurus start");
  3. 最终由 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,
    },
    ...
  }],
]

侧边栏:sidebars.js

sidebarPath 指向 sidebars.js,它用手工分类(autogenerated 之外的显式声明)组织了整个学习路径:

  • Introductionsoftware-engineering-interview-guide
  • Coding interview preparationcoding-interview-prepcoding-interview-cheatsheetcoding-interview-techniquesmock-interviews
  • Algorithms study cheatsheets → 再按 Basics / Data structures / Advanced data structures / Additional 分组挂载 algorithms/arrayalgorithms/treealgorithms/graph
  • Salary and offer negotiation preparationBeyond 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-gtaganonymizeIP: true),themeConfig 内另有 gtagtrackingID——两者共同承担访问统计,与 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 的目录结构看,该仓库定制了三个组件:

  1. 文档内容区DocItem/Content/index.js
    • 实现了 Docusaurus 的 "synthetic title" 逻辑:当 front matter 未设置 hide_title 且正文本身没有顶层 H1 时,用 metadata.title 渲染出合成标题,保证两种标题写法渲染一致(文件内注释引用了上游 PR #4882 的讨论背景);
    • 在正文上方固定注入 GitHub Star 按钮(iframe 引入 ghbtns.com)与作者介绍区块,再挂载 MDXContent 渲染正文。
  2. 右侧目录TOC/index.js:在标准 TOCItems 之上插入 SidebarAd 组件(position="table_of_contents"),并用固定 class 名控制 TOC 高亮行为。
  3. 移动端侧边栏DocSidebar/Mobile/index.js:在 DocSidebarItems 之后追加 SidebarAdposition="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 目录的内容,其中与部署强相关的文件包括:

此外,apps/website/functions 目录下的 grind75/[[catchall]].js 是平台无关的 serverless 函数,为站点的 Grind 75 页面提供动态能力——构建该目录需依赖具体的函数托管平台,README 未展开其部署方式,这里仅作结构说明。

九、常用命令速查与注意事项

汇总 apps/website/README.mdpackage.jsonapps/website/package.json 中的脚本,实际可用的命令如下:

目的 命令(工作目录) 实际映射
安装依赖 vp install(仓库根目录) pnpm workspace 全量安装
本地开发 vp run dev(仓库根目录) @tih/websitedocusaurus start
仅构建网站 vp run --filter @tih/website build(仓库根目录) docusaurus buildbuild/
构建全部 workspace vp run --cache -r build(仓库根目录) 各包 build 脚本,带缓存
类型/格式/静态检查 vp check(仓库根目录) Vite+ 内置 check
网站 lint vp lint docusaurus.config.js sidebars.js src(apps/website) @tih/websitelint 脚本
部署 GIT_USER=<用户名> USE_SSH=1 vp run deploy(apps/website) docusaurus deploygh-pages

注意事项(均有仓库内依据):

  1. 环境与包管理:Node 25.8.1 / pnpm 10.32.1(package.jsonengines),不要绕过 vp 直接调用 pnpm(AGENTS.md);
  2. vp run <script>vp <command> 语义不同,同名脚本必须走 vp runAGENTS.md 的 Common Pitfalls);
  3. 文档内容源在 contents/ 而非 docs/,新增文章记得同步维护 sidebars.js
  4. vp check/vp testAGENTS.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.jssidebars.jssrc/theme 三个位置精确控制。掌握这条从源码可验证的链路,就能对该站点做任何从加一篇文档到改一处主题组件的本地开发工作。

登录后查看全文
热门项目推荐
相关项目推荐