Skip to main content
Version: 3.5.1

调配

¥Swizzling

在本节中,我们将介绍如何在 Docusaurus 中完成布局自定义。

¥In this section, we will introduce how customization of layout is done in Docusaurus.

似曾相识……?

¥Déjà vu...?

本节与 样式和布局 类似,但是这一次,我们将自定义 React 组件本身,而不是它们的样子。我们将讨论 Docusaurus 中的一个中心概念:swizzling,允许更深入的站点定制。

¥This section is similar to Styling and Layout, but this time, we will customize React components themselves, rather than what they look like. We will talk about a central concept in Docusaurus: swizzling, which allows deeper site customizations.

在实践中,调配允许将主题组件与你自己的实现进行交换,它有两种模式:

¥In practice, swizzling permits to swap a theme component with your own implementation, and it comes in 2 patterns:

  • 弹出:创建原始主题组件的副本,你可以完全自定义该副本

    ¥Ejecting: creates a copy of the original theme component, which you can fully customize

  • 封装:围绕原始主题组件创建一个封装器,你可以增强它

    ¥Wrapping: creates a wrapper around the original theme component, which you can enhance

Why is it called swizzling?

该名称来自 Objective-C 和 Swift-UI:方法混合 是改变现有选择器(方法)的实现的过程。

¥The name comes from Objective-C and Swift-UI: method swizzling is the process of changing the implementation of an existing selector (method).

对于 Docusaurus,组件混合意味着提供一个优先于主题提供的组件的替代组件。

¥For Docusaurus, component swizzling means providing an alternative component that takes precedence over the component provided by the theme.

你可以将其视为 React 组件的 猴子补丁,使你能够覆盖默认实现。Gatsby 有一个类似的概念,称为 主题阴影

¥You can think of it as Monkey Patching for React components, enabling you to override the default implementation. Gatsby has a similar concept called theme shadowing.

为了更深入地理解这一点,你必须了解 主题组件是如何解析的

¥To gain a deeper understanding of this, you have to understand how theme components are resolved.

调配过程

¥Swizzling Process

概述

¥Overview

Docusaurus 提供了方便的交互式 CLI 来混合组件。一般只需要记住以下命令即可:

¥Docusaurus provides a convenient interactive CLI to swizzle components. You generally only need to remember the following command:

npm run swizzle

它将在你的 src/theme 目录中生成一个新组件,其应类似于以下示例:

¥It will generate a new component in your src/theme directory, which should look like this example:

src/theme/SomeComponent.js
import React from 'react';

export default function SomeComponent(props) {
// You can fully customize this implementation
// including changing the JSX, CSS and React hooks
return (
<div className="some-class">
<h1>Some Component</h1>
<p>Some component implementation details</p>
</div>
);
}

要获得可用于 swizzle 的所有主题和组件的概述,请运行:

¥To get an overview of all the themes and components available to swizzle, run:

npm run swizzle -- --list

使用 --help 查看所有可用的 CLI 选项,或参考参考 swizzle CLI 文档

¥Use --help to see all available CLI options, or refer to the reference swizzle CLI documentation.

注意

调配组件后,重新启动开发服务器,以便 Docusaurus 了解新组件。

¥After swizzling a component, restart your dev server in order for Docusaurus to know about the new component.

更喜欢保持安全

一定要理解 哪些组件可以安全混合。有些组件是主题的内部实现细节。

¥Be sure to understand which components are safe to swizzle. Some components are internal implementation details of a theme.

信息

docusaurus swizzle 只是一种帮助你调整组件的自动化方法。你还可以手动创建 src/theme/SomeComponent.js 文件,Docusaurus 将创建 解决它。这个命令背后没有任何内在的魔法!

¥docusaurus swizzle is only an automated way to help you swizzle the component. You can also create the src/theme/SomeComponent.js file manually, and Docusaurus will resolve it. There's no internal magic behind this command!

弹出

¥Ejecting

弹出主题组件是创建原始主题组件副本的过程,你可以完全自定义和覆盖该副本。

¥Ejecting a theme component is the process of creating a copy of the original theme component, which you can fully customize and override.

要弹出主题组件,请交互使用 swizzle CLI,或使用 --eject 选项:

¥To eject a theme component, use the swizzle CLI interactively, or with the --eject option:

npm run swizzle [theme name] [component name] -- --eject

一个例子:

¥An example:

npm run swizzle @docusaurus/theme-classic Footer -- --eject

这会将当前 <Footer /> 组件的实现复制到站点的 src/theme 目录。Docusaurus 现在将使用此 <Footer> 组件副本而不是原始组件。你现在可以完全重新实现 <Footer> 组件。

¥This will copy the current <Footer /> component's implementation to your site's src/theme directory. Docusaurus will now use this <Footer> component copy instead of the original one. You are now free to completely re-implement the <Footer> component.

src/theme/Footer/index.js
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>
);
}
警告

弹出 unsafe 组件有时会导致复制大量内部代码,你现在必须自己维护这些代码。它可能会使 Docusaurus 升级更加困难,因为如果收到的属性或使用的内部主题 API 发生更改,你将需要迁移自定义设置。

¥Ejecting an unsafe component can sometimes lead to copying a large amount of internal code, which you now have to maintain yourself. It can make Docusaurus upgrades more difficult, as you will need to migrate your customizations if the props received or internal theme APIs used have changed.

尽可能首选 wrapping:需要维护的代码量更少。

¥Prefer wrapping whenever possible: the amount of code to maintain is smaller.

重新调配

要在 Docusaurus 升级后使弹出的组件保持最新状态,请重新运行弹出命令并将更改与 git diff 进行比较。还建议你在文件顶部写一条简短的注释,解释你所做的更改,以便你在重新弹出后可以更轻松地重新应用更改。

¥To keep ejected components up-to-date after a Docusaurus upgrade, re-run the eject command and compare the changes with git diff. You are also recommended to write a brief comment at the top of the file explaining what changes you have made, so that you could more easily re-apply your changes after re-ejection.

封装

¥Wrapping

封装主题组件是围绕原始主题组件创建封装器的过程,你可以对其进行增强。

¥Wrapping a theme component is the process of creating a wrapper around the original theme component, which you can enhance.

要封装主题组件,请交互使用 swizzle CLI,或使用 --wrap 选项:

¥To wrap a theme component, use the swizzle CLI interactively, or with the --wrap option:

npm run swizzle [theme name] [component name] -- --wrap

一个例子:

¥An example:

npm run swizzle @docusaurus/theme-classic Footer -- --wrap

这将在你站点的 src/theme 目录中创建一个封装器。Docusaurus 现在将使用 <FooterWrapper> 组件而不是原来的组件。你现在可以在原始组件周围添加自定义项。

¥This will create a wrapper in your site's src/theme directory. Docusaurus will now use the <FooterWrapper> component instead of the original one. You can now add customizations around the original component.

src/theme/Footer/index.js
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} />
</>
);
}
What is this @theme-original thing?

Docusaurus 使用 主题别名 来解析要使用的主题组件。新创建的封装器采用 @theme/SomeComponent 别名。@theme-original/SomeComponent 允许导入封装器隐藏的原始组件,而不会创建封装器导入自身的无限导入循环。

¥Docusaurus uses theme aliases to resolve the theme components to use. The newly created wrapper takes the @theme/SomeComponent alias. @theme-original/SomeComponent permits to import original component that the wrapper shadows without creating an infinite import loop where the wrapper imports itself.

提示

封装主题是一种在现有主题周围添加额外组件而无需对其进行 ejecting 的好方法。例如,你可以轻松地在每篇博客文章下添加自定义评论系统:

¥Wrapping a theme is a great way to add extra components around existing one without ejecting it. For example, you can easily add a custom comment system under each blog post:

src/theme/BlogPostItem.js
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 />
</>
);
}

调配什么是安全的?

¥What is safe to swizzle?

拥有权利的同时也被赋予了重大的责任

¥With great power comes great responsibility

一些主题组件是主题的内部实现细节。Docusaurus 允许你混合它们,但这可能有风险。

¥Some theme components are internal implementation details of a theme. Docusaurus allows you to swizzle them, but it might be risky.

Why is it risky?

主题作者(包括我们)可能必须随着时间的推移更新他们的主题:更改组件属性、名称、文件系统位置、类型...例如,考虑一个接收两个 props nameage 的组件,但在重构之后,它现在接收一个具有上述两个属性的 person props。你的组件仍然需要这两个属性,因此将渲染 undefined

¥Theme authors (including us) might have to update their theme over time: changing the component props, name, file system location, types... For example, consider a component that receives two props name and age, but after a refactor, it now receives a person prop with the above two properties. Your component, which still expects these two props, will render undefined instead.

此外,内部组件可能会消失。如果一个组件名为 Sidebar,后来重命名为 DocSidebar,则你的混合组件将被完全忽略。

¥Moreover, internal components may simply disappear. If a component is called Sidebar and it's later renamed to DocSidebar, your swizzled component will be completely ignored.

标记为不安全的主题组件可能会在主题次要版本之间以向后不兼容的方式更改。升级主题(或 Docusaurus)时,你的自定义可能会出现意外行为,甚至可能会破坏你的网站。

¥Theme components marked as unsafe may change in a backward-incompatible way between theme minor versions. When upgrading a theme (or Docusaurus), your customizations might behave unexpectedly, and can even break your site.

对于每个主题组件,swizzle CLI 将指示主题作者声明的 3 个不同的安全级别:

¥For each theme component, the swizzle CLI will indicate 3 different levels of safety declared by theme authors:

  • 安全的:该组件可以安全地混合,其公共 API 被认为是稳定的,并且主题主要版本内不应发生重大更改

    ¥Safe: this component is safe to be swizzled, its public API is considered stable, and no breaking changes should happen within a theme major version

  • 不安全:该组件是主题实现细节,不能安全地混合,并且主题次要版本中可能会发生重大更改

    ¥Unsafe: this component is a theme implementation detail, not safe to be swizzled, and breaking changes might happen within a theme minor version

  • 禁止:swizzle CLI 将阻止你 swizzling 该组件,因为它根本不是为 swizzled 而设计的

    ¥Forbidden: the swizzle CLI will prevent you from swizzling this component, because it is not designed to be swizzled at all

注意

有些组件可能可以安全地封装,但不能安全地弹出。

¥Some components might be safe to wrap, but not safe to eject.

信息

不要太害怕混合不安全的组件:请记住,可能会发生重大更改,并且你可能需要在次要版本升级时手动升级自定义设置。

¥Don't be too afraid to swizzle unsafe components: just keep in mind that breaking changes might happen, and you might need to upgrade your customizations manually on minor version upgrades.

报告你的用例

如果你有一个强大的用例来调配不安全的组件,请 在这里报告,我们将共同努力找到使其安全的解决方案。

¥If you have a strong use-case for swizzling an unsafe component, please report it here and we will work together to find a solution to make it safe.

我应该混合哪个组件?

¥Which component should I swizzle?

并不总是清楚应该准确地混合哪个组件才能达到所需的结果。提供了大部分主题组件的 @docusaurus/theme-classic,大约有 100 个组件

¥It is not always clear which component you should swizzle exactly to achieve the desired result. @docusaurus/theme-classic, which provides most of the theme components, has about 100 components!

提示

要打印所有 @docusaurus/theme-classic 组件的概述:

¥To print an overview of all the @docusaurus/theme-classic components:

npm run swizzle @docusaurus/theme-classic -- --list

你可以按照以下步骤找到要混合的适当组件:

¥You can follow these steps to locate the appropriate component to swizzle:

  1. 组件描述。有些组件提供了简短的描述,这是找到正确组件的好方法。

    ¥Component description. Some components provide a short description, which is a good way to find the right one.

  2. 组件名称。官方主题组件是按语义命名的,因此你应该能够从名称推断出其功能。swizzle CLI 允许你输入组件名称的一部分以缩小可用选择的范围。例如,如果你运行 yarn swizzle @docusaurus/theme-classic,并输入 Doc,则只会列出与文档相关的组件。

    ¥Component name. Official theme components are semantically named, so you should be able to infer its function from the name. The swizzle CLI allows you to enter part of a component name to narrow down the available choices. For example, if you run yarn swizzle @docusaurus/theme-classic, and enter Doc, only the docs-related components will be listed.

  3. 从更高级别的组件开始。组件形成一棵树,其中一些组件导入其他组件。每条路由都将与该路由将渲染的一个顶层组件相关联(其中大部分在 内容插件中的路由 中列出)。例如,所有博客文章页面都将 @theme/BlogPostPage 作为最顶层的组件。你可以从调整此组件开始,然后沿着组件树向下查找恰好渲染你的目标内容的组件。找到正确的文件后,不要忘记通过删除文件来取消其余部分,这样你就不会维护太多组件。

    ¥Start with a higher-level component. Components form a tree with some components importing others. Every route will be associated with one top-level component that the route will render (most of them listed in Routing in content plugins). For example, all blog post pages have @theme/BlogPostPage as the topmost component. You can start with swizzling this component, and then go down the component tree to locate the component that renders just what you are targeting. Don't forget to unswizzle the rest by deleting the files after you've found the correct one, so you don't maintain too many components.

  4. 阅读 主题源代码 并明智地使用搜索。

    ¥Read the theme source code and use search wisely.

就问吧!

如果你仍然不知道要调整哪个组件才能达到所需的效果,你可以在我们的 支持渠道 中寻求帮助。

¥If you still have no idea which component to swizzle to achieve the desired effect, you can reach out for help in one of our support channels.

我们还想更好地了解你最奇特的定制用例,所以请 举报他们

¥We also want to understand better your fanciest customization use-cases, so please report them.

我需要搅动吗?

¥Do I need to swizzle?

Swizzling 最终意味着你必须维护一些与 Docusaurus 内部 API 交互的额外 React 代码。如果可以的话,在自定义网站时考虑以下替代方案:

¥Swizzling ultimately means you have to maintain some additional React code that interact with Docusaurus internal APIs. If you can, think about the following alternatives when customizing your site:

  1. 使用 CSS。CSS 规则和选择器通常可以帮助你实现相当程度的自定义。详细信息请参阅 样式和布局

    ¥Use CSS. CSS rules and selectors can often help you achieve a decent degree of customization. Refer to styling and layout for more details.

  2. 使用翻译。这听起来可能令人惊讶,但翻译最终只是定制文本标签的一种方式。例如,如果你网站的默认语言是 en,你仍然可以运行 yarn write-translations -l en 并编辑触发的 code.json。请参阅 国际化教程 了解更多详细信息。

    ¥Use translations. It may sound surprising, but translations are ultimately just a way to customize the text labels. For example, if your site's default language is en, you can still run yarn write-translations -l en and edit the code.json emitted. Refer to the i18n tutorial for more details.

提示

越小越好。如果调配是不可避免的,那么最好只调配相关部分,并自己维护尽可能少的代码。调配小组件通常意味着升级过程中破坏更改的风险较小。

¥The smaller, the better. If swizzling is inevitable, prefer to swizzle only the relevant part and maintain as little code on your own as possible. Swizzling a small component often means less risk of breaking changes during upgrade.

封装 也是比 ejecting 安全得多的替代品。

¥Wrapping is also a far safer alternative to ejecting.

<Root> 封装你的网站

¥Wrapping your site with <Root>

<Root> 组件渲染在 React 树的最顶部,位于主题 <Layout> 之上,并且永远不会卸载。它是添加不应在导航中重新初始化的状态逻辑(用户身份验证状态、购物车状态...)的完美位置。

¥The <Root> component is rendered at the very top of the React tree, above the theme <Layout>, and never unmounts. It is the perfect place to add stateful logic that should not be re-initialized across navigations (user authentication status, shopping cart state...).

通过在 src/theme/Root.js 创建文件来手动调整它:

¥Swizzle it manually by creating a file at src/theme/Root.js:

src/theme/Root.js
import React from 'react';

// Default implementation, that you can customize
export default function Root({children}) {
return <>{children}</>;
}
提示

使用此组件来渲染 React Context 提供程序。

¥Use this component to render React Context providers.