组件替换
本节将介绍如何在 Docusaurus 中进行布局定制。
似曾相识?...
本节类似于 样式和布局 ,但这次我们将定制 React 组件本身,而不是它们的外观。我们将讨论 Docusaurus 中的一个核心概念: 组件替换 ,它允许进行 更深入的站点定制 。
实际上,组件替换允许你 用你自己的实现替换主题组件 ,它有两种模式:
- 弹出(Ejecting) :创建原始主题组件的 副本 ,你可以完全对其进行定制
- 包裹(Wrapping) :在原始主题组件周围创建一个 包装器 ,你可以对其进行增强
为什么叫组件替换(swizzling)?
这个名称来自 Objective-C 和 Swift-UI: 方法替换 是更改现有选择器(方法)实现的过程。
对于 Docusaurus,组件替换意味着提供一个替代组件,该组件优先于主题提供的组件。
你可以将其视为 React 组件的 猴子补丁 ,它使你能够覆盖默认实现。Gatsby 有一个类似的概念,称为 主题覆盖 。
要更深入地了解这一点,你必须了解 主题组件是如何解析的 。
组件替换流程
概述
Docusaurus 提供了一个方便的 交互式 CLI 来替换组件。你通常只需要记住以下命令:
- npm
- Yarn
- pnpm
npm run swizzle
yarn swizzle
pnpm run swizzle
它将在你的 src/theme
目录中生成一个新的组件,它应该类似于以下示例:
- Ejecting
- Wrapping
import React from 'react';
export default function SomeComponent(props) {
// 你可以完全定制这个实现
// 包括更改 JSX、CSS 和 React hook
return (
<div className="some-class">
<h1>Some Component</h1>
<p>Some component implementation details</p>
</div>
);
}
import React from 'react';
import SomeComponent from '@theme-original/SomeComponent';
export default function SomeComponentWrapper(props) {
// 你可以增强原始组件,
// 包括添加额外的 props 或 JSX 元素
return (
<>
<SomeComponent {...props} />
</>
);
}
要获取所有可替换主题和组件的概述,请运行:
- npm
- Yarn
- pnpm
npm run swizzle -- --list
yarn swizzle --list
pnpm run swizzle --list
使用 --help
查看所有可用的 CLI 选项,或参考 组件替换 CLI 文档 。
如果你决定不再需要以前替换的组件,可以简单地将其文件从 src/theme
目录中删除。删除组件后,请确保重新启动开发服务器以确保更改正确反映。
替换组件后, 重新启动你的开发服务器 ,以便 Docusaurus 能够识别新的组件。
确保了解 哪些组件是 可以安全替换的 。有些组件是主题的 内部实现细节 。
docusaurus swizzle
只是一种帮助你替换组件的自动化方法。你也可以手动创建 src/theme/SomeComponent.js
文件,Docusaurus 将 解析它 。此命令背后没有任何内部魔术!
弹出 (Ejecting)
弹出主题组件是 创建原始主题组件副本 的过程,你可以 完全自定义和覆盖 它。
要弹出主题组件,请交互式地使用 swizzle CLI,或使用 --eject
选项:
- npm
- Yarn
- pnpm
npm run swizzle [theme name] [component name] -- --eject
yarn swizzle [theme name] [component name] --eject
pnpm run swizzle [theme name] [component name] --eject
一个例子:
- npm
- Yarn
- pnpm
npm run swizzle @docusaurus/theme-classic Footer -- --eject
yarn swizzle @docusaurus/theme-classic Footer --eject
pnpm run swizzle @docusaurus/theme-classic Footer --eject
这会将当前 <Footer />
组件的实现复制到你的站点 src/theme
目录。Docusaurus 现在将使用此 <Footer>
组件副本而不是原始组件。你现在可以自由地完全重新实现 <Footer>
组件。
import React from 'react';
export default function Footer(props) {
return (
<footer>
<h1>This is my custom site footer</h1>
<p>And it is very different from the original</p>
</footer>
);
}
为了在 Docusaurus 升级后保持弹出的组件最新,请重新运行弹出命令并将更改与 git diff
进行比较。建议你在文件的顶部写一个简短的注释,解释你所做的更改,以便你可以在重新弹出后更容易地重新应用你的更改。
包裹 (Wrapping)
包裹主题组件是在原始主题组件周围 创建一个包装器 的过程,你可以对其进行 增强 。
要包裹主题组件,请交互式地使用 swizzle CLI,或使用 --wrap
选项:
- npm
- Yarn
- pnpm
npm run swizzle [theme name] [component name] -- --wrap
yarn swizzle [theme name] [component name] --wrap
pnpm run swizzle [theme name] [component name] --wrap
一个例子:
- npm
- Yarn
- pnpm
npm run swizzle @docusaurus/theme-classic Footer -- --wrap
yarn swizzle @docusaurus/theme-classic Footer --wrap
pnpm run swizzle @docusaurus/theme-classic Footer --wrap
这将在你的站点 src/theme
目录中创建一个包装器。Docusaurus 现在将使用 <FooterWrapper>
组件而不是原始组件。你现在可以向原始组件添加自定义项。
import React from 'react';
import Footer from '@theme-original/Footer';
export default function FooterWrapper(props) {
return (
<>
<section>
<h2>Extra section</h2>
<p>This is an extra section that appears above the original footer</p>
</section>
<Footer {...props} />
</>
);
}
这是什么@theme-original
?
Docusaurus 使用 主题别名 来解析要使用的主题组件。新创建的包装器采用 @theme/SomeComponent
别名。@theme-original/SomeComponent
允许导入包装器隐藏的原始组件,而不会创建无限导入循环,其中包装器导入自身。
包裹主题是在不 弹出 的情况下 在现有组件周围添加额外组件 的好方法。例如,你可以轻松地在每篇博文下添加自定义评论系统:
import React from 'react';
import BlogPostItem from '@theme-original/BlogPostItem';
import MyCustomCommentSystem from '@site/src/MyCustomCommentSystem';
export default function BlogPostItemWrapper(props) {
return (
<>
<BlogPostItem {...props} />
<MyCustomCommentSystem />
</>
);
}
哪些组件可以安全替换?
能力越大,责任越大
一些主题组件是主题的 内部实现细节 。Docusaurus 允许你替换它们,但这 可能很危险 。
为什么有风险?
主题作者(包括我们)可能需要随着时间的推移更新他们的主题:更改组件 props、名称、文件系统位置、类型……例如,考虑一个接收两个 props name
和 age
的组件,但在重构后,它现在接收一个包含上述两个属性的 person
prop。你的组件仍然期望这两个 props,将呈现 undefined
。
此外,内部组件可能会消失。如果一个组件名为 Sidebar
,后来改名为 DocSidebar
,你的替换组件将被完全忽略。
标记为不安全的主题组件可能会在主题次要版本之间以向后不兼容的方式发生更改。 升级主题(或 Docusaurus)时,你的自定义项可能会 行为异常 ,甚至可能 破坏你的网站。
对于每个主题组件,swizzle CLI 将指示主题作者声明的 三个不同的安全级别 :
- 安全 (Safe) :此组件可以安全地替换,其公共 API 被认为是稳定的,并且在主题 主版本 内不应发生重大更改
- 不安全 (Unsafe) :此组件是主题的实现细节,不安全替换,并且在主题 次要版本 内可能会发生重大更改
- 禁止 (Forbidden) :swizzle CLI 将阻止你替换此组件,因为它根本没有设计为要替换
一些组件可以安全地包裹,但不能安全地弹出。
不要太 害怕替换不安全的组件 :记住 重大更改 可能会发生,你可能需要在次要版本升级时手动升级你的自定义项。
如果你有 替换不安全组件的强烈用例 ,请 在此处报告 ,我们将一起努力寻找解决方案以使其安全。
我应该替换哪个组件?
要实现预期的结果,并不总是清楚你应该替换哪个组件。@docusaurus/theme-classic
提供了大多数主题组件,大约有 100 个组件 !
要打印所有 @docusaurus/theme-classic
组件的概述:
- npm
- Yarn
- pnpm
npm run swizzle @docusaurus/theme-classic -- --list
yarn swizzle @docusaurus/theme-classic --list
pnpm run swizzle @docusaurus/theme-classic --list
你可以按照以下步骤找到合适的组件进行替换:
- 组件描述。 一些组件提供了简短的描述,这是一种查找正确组件的好方法。
- 组件名称。 官方主题组件的命名具有语义意义,因此你应该能够从名称中推断出它的功能。swizzle CLI 允许你输入组件名称的一部分以缩小可用选项的范围。例如,如果你运行
yarn swizzle @docusaurus/theme-classic
并输入Doc
,则只列出与文档相关的组件。 - 从更高级别的组件开始。 组件形成一棵树,一些组件导入其他组件。每个路由都将与一个顶级组件关联,该路由将呈现该组件(大多数列在 内容插件中的路由 中)。例如,所有博客文章页面都将
@theme/BlogPostPage
作为顶级组件。你可以从替换此组件开始,然后向下遍历组件树以找到只呈现你目标内容的组件。别忘了在你找到正确的组件后通过删除文件来取消替换其余组件,这样你就不会维护太多组件。 - 阅读 主题源代码 并明智地使用搜索。
如果你仍然不知道应该替换哪个组件才能达到预期的效果,你可以在我们的 支持渠道 中寻求帮助。
我们也希望更好地了解你最酷炫的自定义用例,所以请 报告它们 。
:::
我需要组件替换吗?
组件替换最终意味着你必须维护一些与 Docusaurus 内部 API 交互的额外 React 代码。如果可以,在自定义站点时考虑以下替代方案:
- 使用 CSS。 CSS 规则和选择器通常可以帮助你实现相当程度的自定义。有关更多详细信息,请参阅 样式和布局 。
- 使用翻译。 这听起来可能令人惊讶,但翻译最终只是一种自定义文本标签的方法。例如,如果你的网站默认语言是
en
,你仍然可以运行yarn write-translations -l en
并编辑生成的code.json
。有关更多详细信息,请参阅 国际化教程 。
使用 <Root>
包装你的站点
<Root>
组件渲染在 React 树的 最顶部 ,位于主题 <Layout>
之上,并且 从不卸载 。它是添加状态逻辑的理想位置,这些逻辑不应跨导航重新初始化(用户身份验证状态、购物车状态……)。
通过在 src/theme/Root.js
中创建一个文件来 手动 替换它:
import React from 'react';
// 默认实现,你可以自定义
export default function Root({children}) {
return <>{children}</>;
}
使用此组件来渲染 React Context 提供程序。