客户端架构
Theme aliases
A theme works by exporting a set of components, e.g. Navbar
, Layout
, Footer
, to render the data passed down from plugins. Docusaurus and users use these components by importing them using the @theme
webpack alias:
import Navbar from '@theme/Navbar';
The alias @theme
can refer to a few directories, in the following priority:
- A user's
website/src/theme
directory, which is a special directory that has the higher precedence. - A Docusaurus theme package's
theme
directory. - Docusaurus core 提供的原始组件(通常用不到)。
This is called a layered architecture: a higher-priority layer providing the component would shadow a lower-priority layer, making swizzling possible. 假设有以下文件结构:
website
├── node_modules
│ └── @docusaurus/theme-classic
│ └── theme
│ └── Navbar.js
└── src
└── theme
└── Navbar.js
website/src/theme/Navbar.js
takes precedence whenever @theme/Navbar
is imported. 这被称为 swizzle。 If you are familiar with Objective C where a function's implementation can be swapped during runtime, it's the exact same concept here with changing the target @theme/Navbar
is pointing to!
We already talked about how the "userland theme" in src/theme
can re-use a theme component through the @theme-original
alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the @theme-init
import.
Here's an example of using this feature to enhance the default theme CodeBlock
component with a react-live
playground feature.
import InitialCodeBlock from '@theme-init/CodeBlock';
import React from 'react';
export default function CodeBlock(props) {
return props.live ? (
<ReactLivePlayground {...props} />
) : (
<InitialCodeBlock {...props} />
);
}
Check the code of @docusaurus/theme-live-codeblock
for details.
Unless you want to publish a re-usable "theme enhancer" (like @docusaurus/theme-live-codeblock
), you likely don't need @theme-init
.
要理解这些别名可能有点困难。 我们来想象一个超级复杂的场景:三个主题,以及网站本身,都尝试定义同一个组件。 Docusaurus 内部会把这些主题加载成一个「栈」。
+-------------------------------------------------+
| `website/src/theme/CodeBlock.js` | <-- `@theme/CodeBlock` always points to the top
+-------------------------------------------------+
| `theme-live-codeblock/theme/CodeBlock/index.js` | <-- `@theme-original/CodeBlock` points to the topmost non-swizzled component
+-------------------------------------------------+
| `plugin-awesome-codeblock/theme/CodeBlock.js` |
+-------------------------------------------------+
| `theme-classic/theme/CodeBlock/index.js` | <-- `@theme-init/CodeBlock` always points to the bottom
+-------------------------------------------------+
The components in this "stack" are pushed in the order of preset plugins > preset themes > plugins > themes > site
, so the swizzled component in website/src/theme
always comes out on top because it's loaded last.
@theme/*
always points to the topmost component—when CodeBlock
is swizzled, all other components requesting @theme/CodeBlock
receive the swizzled version.
@theme-original/*
always points to the topmost non-swizzled component. That's why you can import @theme-original/CodeBlock
in the swizzled component—it points to the next one in the "component stack", a theme-provided one. 插件作者不能使用这个别名,因为你的组件可能是最顶端的组件,从而导致自己导入自己的情况。
@theme-init/*
always points to the bottommost component—usually, this comes from the theme or plugin that first provides this component. Individual plugins / themes trying to enhance code block can safely use @theme-init/CodeBlock
to get its basic version. Site creators should generally not use this because you likely want to enhance the topmost instead of the bottommost component. It's also possible that the @theme-init/CodeBlock
alias does not exist at all—Docusaurus only creates it when it points to a different one from @theme-original/CodeBlock
, i.e. when it's provided by more than one theme. 我们不会浪费别名的!