Written by
Fuma Nama
At
Wed May 15 2024
Fumadocs 的工作原理
构建文档的框架
一年前,我正在使用 Next.js 应用路由玩得很开心。 在我尝试在我的小项目 No Deploy 上实验它时,我计划建立文档。 然而,Nextra 不支持应用路由。
为了解决这个问题,我用纯 Contentlayer 和 App Router 的新功能实现了一个小型文档网站。它运行得非常好,看起来极快且简洁。我借鉴了 No Deploy 的逻辑,搭建了这个文档框架。经过几个月的开发,它很快变得强大且稳定。
🌐 To handle this, I implemented a small documentation site with solely Contentlayer and the new features from App Router. It was working great, looked blazing-fast and minimal. I cloned the logic from No Deploy and built this documentation framework. With a few months of development, it soon became powerful and stable.
它最初被命名为 next-docs,我将其重命名为 Fumadocs,因为它与 Next.js 文档冲突。
🌐 It was originally named next-docs, I renamed it to Fumadocs as it conflicts with Next.js Docs.
感谢 Next.js 社区的支持,我在这个过程中收到了许多建议。 Fumadocs 现在是我的库以及其他一些很棒项目所使用的框架。
🌐 Thanks to the support from Next.js community, I've received a lot of suggestions along the way. Fumadocs is now a framework used by my libraries and some other amazing projects.
我的意见
🌐 My Opinion
在网页开发中,大多数“强大”的框架/库非常庞大而出色,但确实让我们的开发者体验变得很华丽。
🌐 In Web development, most "robust" frameworks/libraries are incredibly heavy and fabulous, but it has indeed made our developer experience fancy.
在 Javascript 之上,人们构建了打包工具、转译器,甚至还有 Typescript。令人惊讶的是,Javascript 这种高级脚本语言,在现代 Web 开发中反而更像汇编代码。我们很少在没有像 Webpack 这样的工具的情况下使用它们。CSS 也同样如此,至少根据我的经验,我很少在没有 PostCSS 的情况下使用 CSS。
🌐 On top of Javascript, people built bundlers, transpilers, and even Typescript. It feels very surprising that Javascript, a high-level scripting language, is more similar to assembly code in modern Web development. We rarely use them without things like Webpack. This also applies to CSS, at least in my experience, I seldom use CSS without PostCSS.
虽然它们可能对兼容性和开发者体验是必要的,但 React Server Component 和 Next.js App Router 的引入让体验更加令人震撼。感觉就像魔法一样。这些狡猾的魔法框架,以及 web 开发的奇迹。这种设计简直疯狂,但它也让我们无意识地忘记了界限。
🌐 While they might be necessary for compatibility and DX, the landing of React Server Component and Next.js App Router made the experience even more mindblowing. It feels like magic. These cunning magical frameworks, and web development miracles. This kind of design is insane, but it also makes us mindlessly forget the boundaries.
初学者使用元数据 API,但他们根本不了解元标签的工作原理。\n他们在服务器组件中放置服务器端逻辑,但完全不知道计算的代价有多高。\n即使我们查看了代码,没有在生产模式下运行,也无法预测结果。\n我见过太多这样的误解。
🌐 Beginners use Metadata API, while they have no idea how meta tag works. They put server-side logic in a server component, while they have no idea how expensive the calculation is. Even when we looked at the code, we can't predict the result without running it in production mode. I saw too many of these misconceptions.
这种情况在许多框架中都会发生,它们过于神奇。 我想让它少一些魔法效果,至少对大多数 Next.js 开发者来说更直观一些。
🌐 This happens on many frameworks, they are overly magical. I wanted to make it less-magic, and straightforward at least for most Next.js developers.
Fumadocs MDX
作为推荐的内容来源,它实际上是一个 webpack 的技巧。由于 Next.js 只能优化静态导入,它首先将你的 .map.ts 转换为一个大致生成如下内容的文件:
🌐 As the recommended content source, It is actually a webpack hack.
Since Next.js could only optimize static imports, it first transforms your .map.ts to a file that roughly yields:
export default [import("./my/file.mdx"), ...];然后使用自定义加载器转换 MDX 文件。它使一切魔法成为可能,但没有懒加载 MDX 文件的能力。与 Nextra 相比,这可能是一个次优的做法。
🌐 And then transforms MDX files with a custom loader. It makes all magic possible, but it doesn't have the ability of lazy-loading MDX files. Comparing to Nextra, it might be a suboptimal approach.
Nextra 做起来更简单,它直接将 MDX 文件转换为页面。因为 Pages Router 会将 JavaScript 文件作为单个页面进行适配,所以这是可行的。在 App Router 中,这种做法不再可能。因此,我没有采用这种方法。
🌐 Nextra does it even easier, it directly transforms MDX files into pages. Because the Pages Router adapts Javascript files as a single page, it is possible. In App Router, this is not possible anymore. Therefore, I didn't take this approach.
Fumadocs 核心
🌐 Fumadocs Core
Fumadocs 的核心是一堆实用工具和 MDX 插件。
🌐 The core of Fumadocs is a bunch of utilities and MDX plugins.
- Source API 从内容源构建页面树,并与其他内容提供者集成。
- 无头组件加速 Fumadocs UI 和其他自定义 UI 实现。
- MDX 插件 为所有集成带来完美的开发者体验。
- 搜索工具 可以让实现文档搜索变得更加容易。
此外,它还建立了页面树和页面约定的定义。总体而言,没有 Fumadocs UI,它还不是一个完整的框架。
🌐 In addition, it has also established the definitions of Page Tree and Page Conventions. Overall, it is not yet a framework without Fumadocs UI.
在我看来,代码库中最有价值的部分是 MDX 插件。我在处理它们的过程中学到了更多关于 AST 以及 remark/rehype 生态系统的知识。这绝对是一次非常棒的体验。
🌐 In my opinion, the most valuable part of the codebase is MDX plugins. I learned a lot more about ASTs and the eco-system of remark/rehype while working on them. Absolutely an amazing experience.
Fumadocs UI
Fumadocs 使用 Tailwind CSS 和 Radix UI 的界面实现。它的设计系统受到 Shadcn UI 的启发,使用 CSS 变量来实现颜色工具。
🌐 The UI implementation of Fumadocs using Tailwind CSS and Radix UI. Its design system was inspired by Shadcn UI, using CSS variables for color utilities.
虽然 Fumadocs 的 UI 结构比核心结构还要简单,但我使用了一些巧妙的技巧来解决 "use client" 指令的问题。我使用的打包工具 TSX 无法处理例如从服务端组件导入的客户端组件这样的嵌套结构。因此,我做了一个小技巧,将服务端组件和客户端组件作为单独的入口进行构建,然后在处理完成后注入 import 语句。
🌐 Although the structure of Fumadocs UI is even simpler than core, I've used some subtle hacks to solve the problem of "use client" directive.
The bundler I am using, TSX, can't handle nested structures like client components imported from a server component.
Therefore, I made a little hack to build server components and client components as an individual entry, then inject import statements after the process.
此外,我也花了一些时间才想出将 Fumadocs UI 与 Tailwind CSS 项目集成的预设方法。
🌐 Also it took me some time to come up with the preset approach for integrating Fumadocs UI with Tailwind CSS projects.
文档生成器
🌐 Docs Generators
我们有一些内置的集成,例如 fumadocs-openapi,它可以使用 OpenAPI 架构并输出 MDX 文件。
🌐 We have a few built-in integrations, like fumadocs-openapi which takes an OpenAPI schema and output MDX files.
对于 OpenAPI 的那个,它只是解析 schema 并通过字符串模板将其转换为 MDX 文件。
🌐 For the OpenAPI one, it simply parses the schema and convert it to MDX file through string templates.
TypeScript 集成做得更多一些,它通过 TypeScript Compiler API 获取类型信息。根据这些信息,它生成 MDX 文件。你可以在服务器组件中使用它,这也是 <AutoTypeTable /> 的工作方式。
🌐 The Typescript integration does a bit more, it obtains type information with Typescript Compiler API. Based on the information, it yields MDX files.
You can use it inside a server component, which is how <AutoTypeTable /> works.
持续集成/持续交付
🌐 CI/CD
作为一个贡献者很少的项目,我尽量将 CI/CD 流程构建得尽可能方便,以提高效率。整个发布流程由 Changesets 处理,我还编写了脚本来自动更新 模板仓库。到目前为止,这一做法运行得非常顺利。
🌐 As a project with very few contributors, I built the CI/CD process as convenient as possible for a better efficiency. The entire release process is handled by Changesets, and I wrote the scripts to update the template repository automatically. It worked great so far.
谢谢
🌐 Thanks
Fumadocs 的 GitHub 仓库 在 2024 年 3 月达到了 300 颗星,这是我取得的新成就。欢迎给它点个星,支持我的工作!