Swizzling
在本节中,我们将介绍在 Docusaurus 中如何进行布局定制。
似曾相识?
本节与 样式和布局 类似,但这一次,我们将自定义 React 组件本身,而不是它们的样式。我们将讨论 Docusaurus 中的一个核心概念:swizzling,它允许进行更深层次的站点定制。
实际上,swizzling 允许用你自己的实现替换主题组件,它有两种模式。
为什么叫 swizzling?
这个名字来自 Objective-C 和 Swift-UI:方法 swizzling 是指更改现有选择器(方法)实现的过程。
对于 Docusaurus 来说,组件 swizzling 意味着提供一个替代组件,该组件优先于主题提供的组件。
你可以将其视为针对 React 组件的 Monkey Patching,使你能够覆盖默认实现。Gatsby 有一个类似的概念,称为 主题覆盖。
要更深入地理解这一点,你需要了解 主题组件的解析方式。
Swizzling 过程
概述
Docusaurus 提供了一个方便的交互式 CLI 来 swizzle 组件。你通常只需要记住以下命令。
- npm
- Yarn
- pnpm
npm run swizzle
yarn swizzle
pnpm run swizzle
它将在你的 src/theme
目录中生成一个新的组件,它应该类似于以下示例。
- Ejecting
- Wrapping
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>
);
}
import React from 'react';
import SomeComponent from '@theme-original/SomeComponent';
export default function SomeComponentWrapper(props) {
// You can enhance the original component,
// including adding extra props or JSX elements around it
return (
<>
<SomeComponent {...props} />
</>
);
}
要查看所有可供 swizzle 的主题和组件的概述,请运行
- npm
- Yarn
- pnpm
npm run swizzle -- --list
yarn swizzle --list
pnpm run swizzle --list
使用 --help
查看所有可用的 CLI 选项,或参考参考 swizzle CLI 文档。
swizzle 组件后,重新启动你的开发服务器,以便 Docusaurus 了解新的组件。
确保你了解 哪些组件是可以安全 swizzle 的。某些组件是主题的内部实现细节。
docusaurus swizzle
只是一个帮助你 swizzle 组件的自动化方式。你也可以手动创建 src/theme/SomeComponent.js
文件,Docusaurus 会 解析它。这个命令背后没有内部的魔力!
Ejecting
Ejecting 主题组件是指创建原始主题组件的副本,你可以对其进行完全自定义和覆盖。
要 eject 主题组件,请使用 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 升级后保持 ejected 组件的最新状态,请重新运行 eject 命令并使用 git diff
比较更改。建议你在文件顶部添加一个简短的注释,说明你做了哪些更改,这样你就可以在重新 eject 后更容易地重新应用你的更改。
Wrapping
Wrapping 主题组件是指在原始主题组件周围创建一个包装器,你可以对其进行增强。
要 wrap 主题组件,请使用 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
允许导入包装器所覆盖的原始组件,而不会创建无限导入循环,其中包装器会导入自身。
Wrapping 主题是在现有组件周围添加额外组件的好方法,而无需 Ejecting 它。例如,你可以在每篇博文下轻松添加自定义评论系统。
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 />
</>
);
}
什么是可以安全 swizzle 的?
能力越大,责任越大
某些主题组件是主题的内部实现细节。Docusaurus 允许你 swizzle 它们,但这可能存在风险。
为什么有风险?
主题作者(包括我们)可能需要随着时间的推移更新他们的主题:更改组件 props、名称、文件系统位置、类型……例如,考虑一个接收两个 props name
和 age
的组件,但在重构后,它现在接收一个具有上述两个属性的 person
prop。你的组件仍然期望这两个 props,将渲染 undefined
,而不是预期的内容。
此外,内部组件可能会直接消失。如果一个组件名为 Sidebar
,后来改名为 DocSidebar
,你 swizzled 的组件将被完全忽略。
标记为不安全的主题组件可能会在主题次要版本之间以不兼容的方式发生更改。升级主题(或 Docusaurus)时,你的自定义项可能会出现意外行为,甚至可能导致你的站点崩溃。
对于每个主题组件,swizzle CLI 将显示由主题作者声明的3 种不同的安全级别。
- 安全:此组件可以安全地 swizzle,它的公共 API 被认为是稳定的,主题主版本内不应该出现重大更改。
- 不安全:此组件是主题实现细节,不安全 swizzle,主题次要版本内可能会发生重大更改。
- 禁止:swizzle CLI 将阻止你 swizzle 此组件,因为它根本不是为 swizzle 而设计的。
某些组件可能可以安全地 wrap,但不能安全地 eject。
不要太害怕混用不安全的组件:只要记住,重大变更可能会发生,您可能需要在次要版本升级时手动升级您的自定义项。
如果您有使用不安全组件的强烈用例,请在此报告,我们将共同努力找到安全的解决方案。
我应该混用哪个组件?
要获得所需的结果,确切地应该混用哪个组件并不总是很清楚。@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
您可以按照以下步骤找到要混用的合适组件
- 组件描述。一些组件提供简短的描述,这是找到正确组件的好方法。
- 组件名称。官方主题组件的命名具有语义性,因此您应该能够从名称中推断出其功能。混用 CLI 允许您输入组件名称的一部分来缩小可用选项的范围。例如,如果您运行
yarn swizzle @docusaurus/theme-classic
并输入Doc
,则只会列出与文档相关的组件。 - 从更高级别的组件开始。组件形成一个树,其中一些组件导入其他组件。每个路由都将与一个顶级组件相关联,该组件将呈现该路由(其中大多数列在内容插件中的路由中)。例如,所有博客文章页面都以
@theme/BlogPostPage
作为最顶层的组件。您可以从混用此组件开始,然后沿着组件树向下找到只呈现您目标内容的组件。不要忘记删除文件后取消混用其余组件,这样您就不会维护太多组件。 - 阅读主题源代码 并明智地使用搜索。
我需要混用吗?
混用最终意味着您必须维护一些额外的 React 代码,这些代码与 Docusaurus 内部 API 交互。如果可以,在自定义网站时考虑以下替代方案
- 使用 CSS。CSS 规则和选择器通常可以帮助您实现一定程度的自定义。有关更多详细信息,请参阅样式和布局。
- 使用翻译。这可能听起来很奇怪,但翻译本质上只是自定义文本标签的一种方式。例如,如果您的网站的默认语言是
en
,您仍然可以运行yarn write-translations -l en
并编辑生成的code.json
。有关更多详细信息,请参阅i18n 教程。
使用 <Root>
包装您的网站
<Root>
组件渲染在 React 树的最顶部,位于主题 <Layout>
之上,并且永远不会卸载。这是添加有状态逻辑的理想位置,该逻辑不应跨导航重新初始化(用户身份验证状态、购物车状态……)。
通过在 src/theme/Root.js
中创建文件,手动混用它
import React from 'react';
// Default implementation, that you can customize
export default function Root({children}) {
return <>{children}</>;
}
使用此组件渲染 React 上下文提供者。