Swizzle
在本节中,我们将介绍如何在 Docusaurus 中完成自定义布局。
似曾相识?
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?
The name comes from Objective-C and Swift-UI: method swizzling is the process of changing the implementation of an existing selector (method).
For Docusaurus, component swizzling means providing an alternative component that takes precedence over the component provided by the theme.
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.
Swizzle 的过程
概览
Docusaurus provides a convenient interactive CLI to swizzle components. 你一般只需要记住以下指令:
- npm
- Yarn
- pnpm
npm run swizzle
yarn swizzle
pnpm run swizzle
It will generate a new component in your src/theme
directory, which should look like this example:
- 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} />
</>
);
}
要总览所有可用的主题和组件,请运行:
- npm
- Yarn
- pnpm
npm run swizzle -- --list
yarn swizzle --list
pnpm run swizzle --list
Use --help
to see all available CLI options, or refer to the reference swizzle CLI documentation.
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
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. 这个命令背后没有什么内部的魔法!
Ejecting
Ejecting a theme component is the process of creating a copy of the original theme component, which you can fully customize and override.
To eject a theme component, use the swizzle CLI interactively, or with the --eject
option:
- npm
- Yarn
- pnpm
npm run swizzle [主题名] [组件名] -- --eject
yarn swizzle [主题名] [组件名] --eject
pnpm run swizzle [主题名] [组件名] --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
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.
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>
);
}
To keep ejected components up-to-date after a Docusaurus upgrade, re-run the eject command and compare the changes with git diff
. 也建议你在文件顶部写一个简短的注释,说明你做了哪些修改。这样你可以在重弹出后更容易地重新做更改。
Wrapping
Wrapping a theme component is the process of creating a wrapper around the original theme component, which you can enhance.
To wrap a theme component, use the swizzle CLI interactively, or with the --wrap
option:
- npm
- Yarn
- pnpm
npm run swizzle [主题名] [组件名] -- --wrap
yarn swizzle [主题名] [组件名] --wrap
pnpm run swizzle [主题名] [组件名] --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
This will create a wrapper in your site's src/theme
directory. Docusaurus will now use the <FooterWrapper>
component instead of the original one. 你现在可以在原始组件周围添加自定义内容。
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 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.
Wrapping a theme is a great way to add extra components around existing one without ejecting it. 比如,你可以轻松在每篇博文下面添加一个自定义的评论系统:
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?
能力越大,责任越大
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?
主题作者(包括我们自己)可能会在未来更新主题:改变组件的属性、名字、文件系统位置、类型,等等。 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.
此外,一些内部组件可能会直接消失。 If a component is called Sidebar
and it's later renamed to DocSidebar
, your swizzled component will be completely ignored.
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.
For each theme component, the swizzle CLI will indicate 3 different levels of safety declared by theme authors:
- 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
- Forbidden: the swizzle CLI will prevent you from swizzling this component, because it is not designed to be swizzled at all
有些组件可能能被安全包装,但不能被安全弹出。
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.
我该 swizzle 哪个组件?
要 swizzle 哪个组件才能达成预期效果,答案并不是总是很清晰。 @docusaurus/theme-classic
, which provides most of the theme components, has about 100 components!
To print an overview of all the @docusaurus/theme-classic
components:
- npm
- Yarn
- pnpm
npm run swizzle @docusaurus/theme-classic -- --list
yarn swizzle @docusaurus/theme-classic --list
pnpm run swizzle @docusaurus/theme-classic --list
你可以按照这些步骤来定位合适的 swizzle 组件:
- Component description. Some components provide a short description, which is a good way to find the right one.
- Component name. Official theme components are semantically named, so you should be able to infer its function from the name. Swizzle CLI 允许你输入组件名称的一部分,以缩小选择范围。 For example, if you run
yarn swizzle @docusaurus/theme-classic
, and enterDoc
, only the docs-related components will be listed. - 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. 你可以先 swizzle 这个组件,然后沿着组件树一步步往下走,最后定位到正好渲染你的目标区域的组件。 最后,在你找到正确的组件后,别忘了把其他文件全部删掉,这样你就不需要维护太多组件了。 - 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.
我真的需要 swizzle 吗?
Swizzle 最终还是意味着你必须维护一些额外的 React 代码,这些代码会与 Docusaurus 的内部 API 交互。 如果可行的话,在个性化你的站点时,也考虑一下这些替代方案:
- Use CSS. CSS rules and selectors can often help you achieve a decent degree of customization. Refer to styling and layout for more details.
- 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 runyarn write-translations -l en
and edit thecode.json
emitted. Refer to the i18n tutorial for more details.
Wrapping your site with <Root>
The <Root>
component is rendered at the very top of the React tree, above the theme <Layout>
, and never unmounts. 如果你有一个不需要在跳转时重新初始化的状态(比如用户身份验证状态、购物车状态……),它是添加这段逻辑的最佳位置。
Swizzle it manually by creating a file at src/theme/Root.js
:
import React from 'react';
// Default implementation, that you can customize
export default function Root({children}) {
return <>{children}</>;
}
用这个组件来渲染 React context provider。