生命周期 API
在构建过程中,插件会并行加载以获取各自的数据内容,并渲染为路由。插件还可以配置 webpack 或对生成的文件进行后处理。
async loadContent()
插件应在此生命周期中从数据源(如文件系统、远程 API、Headless CMS 等)获取数据,或进行一些服务端处理。返回值即为插件所需的内容。
例如,下面这个插件会返回 1 到 10 之间的随机整数作为内容:
export default function (context, options) {
return {
name: 'docusaurus-plugin',
async loadContent() {
return 1 + Math.floor(Math.random() * 10);
},
};
}
async contentLoaded({content, actions})
在 loadContent
加载的数据会在 contentLoaded
生命周期中被消费。你可以将其渲染为路由、注册为全局数据等。
content
contentLoaded
会在 loadContent
完成后调用。loadContent()
的返回值会作为 content
传递给 contentLoaded
。
actions
actions
包含三个函数:
addRoute(config: RouteConfig): void
创建一个要添加到网站的路由。
export type RouteConfig = {
/**
* 必须以斜杠开头。结尾斜杠会根据配置自动规范化。
*/
path: string;
/**
* 用于渲染该路由的组件,需为 bundler 可 require 的路径。
*/
component: string;
/**
* 组件 props。每一项应为 `[propName]: pathToPropModule`(由 `createData` 创建)
*/
modules?: RouteModules;
/**
* 路由上下文会包裹 `component`。可通过 `useRouteContext` 获取此处声明的内容。所有自定义路由上下文会被命名空间化到 {@link RouteContext.data} 下。
*/
context?: RouteModules;
/**
* 嵌套路由配置,适用于有子路由的"布局路由"。
*/
routes?: RouteConfig[];
/**
* React router 配置项:`exact` 路由不会匹配子路由。
*/
exact?: boolean;
/**
* React router 配置项:`strict` 路由对结尾斜杠敏感。
*/
strict?: boolean;
/**
* 用于路由排序。
* 优先级高的路由会优先匹配。
*/
priority?: number;
/**
* 可选路由元数据
*/
metadata?: RouteMetadata;
/**
* 额外 props,会在客户端可用。
*/
[propName: string]: unknown;
};
/**
* 插件作者可为创建的路由分配额外元数据
* 仅在 Node.js 侧可用,不会发送到浏览器
* 可选:鼓励插件作者提供,但不是强制要求
*
* 某些插件可能会用到这些数据以提供额外功能。
* 例如 sitemap 插件用于支持"lastmod"。
* 参考:https://github.com/facebook/docusaurus/pull/9954
*/
export type RouteMetadata = {
/**
* 当前路由对应的源代码文件路径
* 官方内容插件通常为 Markdown 或 React 文件
* 路径应为相对于站点目录的相对路径
*/
sourceFilePath?: string;
/**
* 路由的最后更新时间
* 通常通过 sourceFilePath 的 Git 历史获取
* 也可通过其他方式(如 front matter)提供
*
* 该字段主要用于 sitemap 插件的"lastmod"支持
* 参考:https://github.com/facebook/docusaurus/pull/9954
*/
lastUpdatedAt?: number;
};
type RouteModules = {
[module: string]: Module | RouteModules | RouteModules[];
};
type Module =
| {
path: string;
__import?: boolean;
query?: ParsedUrlQueryInput;
}
| string;
createData(name: string, data: any): Promise<string>
声明式回调,用于创建静态数据(通常为 JSON 或字符串),可作为 props 提供给你的路由。接收文件名和要存储的数据,返回实际数据文件的路径。
例如,下面这个插件会创建一个 /friends
页面,显示 Your friends are: Yangshun, Sebastien
:
import React from 'react';
export default function FriendsComponent({friends}) {
return <div>Your friends are {friends.join(',')}</div>;
}
export default function friendsPlugin(context, options) {
return {
name: 'docusaurus-friends-plugin',
async contentLoaded({content, actions}) {
const {createData, addRoute} = actions;
// 创建 friends.json
const friends = ['Yangshun', 'Sebastien'];
const friendsJsonPath = await createData(
'friends.json',
JSON.stringify(friends),
);
// 添加 '/friends' 路由,并确保其接收到 friends props
addRoute({
path: '/friends',
component: '@site/src/components/Friends.js',
modules: {
// propName -> JSON 文件路径
friends: friendsJsonPath,
},
exact: true,
});
},
};
}
setGlobalData(data: any): void
该函数允许你创建全局插件数据,可在任意页面(包括其他插件创建的页面和主题布局)读取。
这些数据可通过 useGlobalData
和 usePluginData
hook 在客户端/主题代码中访问。
全局数据是全局的:其体积会影响所有页面的加载速度,请尽量保持精简。优先使用 createData
和页面专属数据。
例如,下面这个插件会创建一个 /friends
页面,显示 Your friends are: Yangshun, Sebastien
:
import React from 'react';
import {usePluginData} from '@docusaurus/useGlobalData';
export default function FriendsComponent() {
const {friends} = usePluginData('docusaurus-friends-plugin');
return <div>Your friends are {friends.join(',')}</div>;
}
export default function friendsPlugin(context, options) {
return {
name: 'docusaurus-friends-plugin',
async contentLoaded({content, actions}) {
const {setGlobalData, addRoute} = actions;
// 创建 friends 全局数据
setGlobalData({friends: ['Yangshun', 'Sebastien']});
// 添加 '/friends' 路由
addRoute({
path: '/friends',
component: '@site/src/components/Friends.js',
exact: true,
});
},
};
}
configureWebpack(config, isServer, utils, content)
修改内部 webpack 配置。如果返回值为 JavaScript 对象,则会通过 webpack-merge
合并进最终配置;如果 为函数,则会接收 config 作为第一个参数,isServer 作为第二个参数。
configureWebpack
的 API 未来会调整为接收对象参数(configureWebpack({config, isServer, utils, content})
)
config
configureWebpack
会接收根据客户端/服务端构建生成的 config。你可以将其视为待合并的基础配置。
isServer
configureWebpack
会在服务端和客户端构建时都被调用。服务端构建时 isServer
为 true,客户端构建时为 false。
utils
configureWebpack
还会接收一个工具对象:
getStyleLoaders(isServer: boolean, cssOptions: {[key: string]: any}): Loader[]
getJSLoader(isServer: boolean, cacheOptions?: {}): Loader | null
你可以根据需要使用这些工具返回你的 webpack 配置。
例如, 下面这个插件会修改 webpack 配置以编译 .foo
文件:
export default function (context, options) {
return {
name: 'custom-docusaurus-plugin',
configureWebpack(config, isServer, utils) {
const {getJSLoader} = utils;
return {
module: {
rules: [
{
test: \/\.foo$/,
use: [getJSLoader(isServer), 'my-custom-webpack-loader'],
},
],
},
};
},
};
}
content
configureWebpack
也会接收到插件加载的内容。
合并策略
我们会使用 webpack-merge 将插件的 Webpack 配置合并到全局配置中。
你可以指定合并策略。例如,如果你希望某条 webpack 规则前置而不是追加:
export default function (context, options) {
return {
name: 'custom-docusaurus-plugin',
configureWebpack(config, isServer, utils) {
return {
mergeStrategy: {'module.rules': 'prepend'},
module: {rules: [myRuleToPrepend]},
};
},
};
}
更多细节请参考 webpack-merge 策略文档。
配置开发服务器
你可以通过返回 devServer
字段配置开发服务器。
export default function (context, options) {
return {
name: 'custom-docusaurus-plugin',
configureWebpack(config, isServer, utils) {
return {
devServer: {
open: '/docs', // 启动时自动打开 localhost:3000/docs 而不是 localhost:3000/
},
};
},
};
}
configurePostCss(options)
用于修改客户端 bundle 生成过程中的 postcss-loader
的 postcssOptions
。
应返回修改后的 postcssOptions
。
默认情况下,postcssOptions
如下:
const postcssOptions = {
ident: 'postcss',
plugins: [require('autoprefixer')],
};
示例:
export default function (context, options) {
return {
name: 'docusaurus-plugin',
configurePostCss(postcssOptions) {
// 添加新的 PostCSS 插件
postcssOptions.plugins.push(require('postcss-import'));
return postcssOptions;
},
};
}
postBuild(props)
在(生产环境)构建完成后调用。
interface Props {
siteDir: string;
generatedFilesDir: string;
siteConfig: DocusaurusConfig;
outDir: string;
baseUrl: string;
headTags: string;
preBodyTags: string;
postBodyTags: string;
routesPaths: string[];
routesBuildMetadata: {[location: string]: {noIndex: boolean}};
plugins: Plugin<any>[];
content: Content;
}
示例:
export default function (context, options) {
return {
name: 'docusaurus-plugin',
async postBuild({siteConfig = {}, routesPaths = [], outDir}) {
// 控制台输出所有渲染的路由
routesPaths.map((route) => {
console.log(route);
});
},
};
}
injectHtmlTags({content})
向道格龙生成的 HTML 注入 head 和/或 body 标签。
injectHtmlTags
会接收插件加载的内容。
function injectHtmlTags(): {
headTags?: HtmlTags;
preBodyTags?: HtmlTags;
postBodyTags?: HtmlTags;
};
type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
type HtmlTagObject = {
/**
* HTML 标签的属性
* 例如:`{'disabled': true, 'value': 'demo', 'rel': 'preconnect'}`
*/
attributes?: {
[attributeName: string]: string | boolean;
};
/**
* 标签名,例如 `div`、`script`、`link`、`meta`
*/
tagName: string;
/**
* 标签内部 HTML
*/
innerHTML?: string;
};
示例:
export default function (context, options) {
return {
name: 'docusaurus-plugin',
loadContent: async () => {
return {remoteHeadTags: await fetchHeadTagsFromAPI()};
},
injectHtmlTags({content}) {
return {
headTags: [
{
tagName: 'link',
attributes: {
rel: 'preconnect',
href: 'https://www.github.com',
},
},
...content.remoteHeadTags,
],
preBodyTags: [
{
tagName: 'script',
attributes: {
charset: 'utf-8',
src: '/noflash.js',
},
},
],
postBodyTags: [`<div> This is post body </div>`],
};
},
};
}
标签会按如下方式插入:
headTags
会在 config 添加的脚本后、</head>
标签前插入。preBodyTags
会在<body>
标签打开后、所有子元素前插入。postBodyTags
会在所有子元素后、</body>
标签前插入。
getClientModules()
返回需要导入到客户端 bundle 的客户端模块路径数组。
例如,你可以让主题根据用户传入的 options
加载 customCss
或 customJs
文件路径:
export default function (context, options) {
const {customCss, customJs} = options || {};
return {
name: 'name-of-my-theme',
getClientModules() {
return [customCss, customJs];
},
};
}