blog/docs/dev/frontend/docusaurus插件开发.md
王一之 e9c32c4118
Some checks failed
Release / deploy (push) Failing after 1m42s
docusaurus插件开发
2024-03-24 18:34:18 +08:00

6.8 KiB
Raw Blame History

docusaurus 的插件开发与自定义主题

我为了本博客的一些需求,开发了一些 docusaurus 的插件,这里记录一下开发过程。其中踩到了一些坑,折腾了我好几天,结果回过头来一看是那么简单,希望对你能有所帮助。

创建插件

你需要创建一个类似下面的 npm 包:

{
  "name": "docusaurus-plugin-docs-info",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@docusaurus/core": "^3.1.1",
    "dayjs": "^1.11.10",
    "reading-time": "^1.5.0"
  }
}

目录结构就像这样:

image-20240324133547293

其中源代码放在 src 目录下theme 是相关主题components 是组件

引用插件

创建完成插件后可以使用下面的方式进行引用

本地路径

可以通过本地相对路径来进行引用

  plugins: [
  ["./packages/docusaurus-plugin-docs-info", {}]
  ],

npm 包

也可以使用安装 npm 包的形式来引用

首先需要执行:npm i ./packages/docusaurus-plugin-content-docs-ex来安装包,然后再使用下方的配置进行配置

plugins: [
 ["docusaurus-plugin-docs-info", {}]
],

API

你可以看官方文档了解更多:Plugin Method References,官方主要分为以下四个部分:

  • Lifecycle APIs生命周期 API当到达构建的某些阶段docusaurus 会调用插件的生命周期 API你可以在这些 API 中做一些事情,比如修改配置,添加一些内容等。
  • Extending infrastructure扩展基础设施
  • I18n lifecyclesi18n 生命周期,一些 i18n 翻译相关的内容
  • Static methods静态方法主要是校验选项和校验主题两个方法

这里我简单的介绍一些常用的:

loadContent

这个 API 可以用来预加载一些内容,比如读取文件,将某些数据预加载好,这样在后续的 API 中就可以使用这些内容了。 例如官方的docusaurus-plugin-content-docs插件就是在此预加载好所有的文档数据,然后在contentLoaded中再添加路由的。

contentLoaded

这个 API 有两个参数,一个是 content一个是 actionscontent 就是 loadContent 返回的内容, actions 里面包含了addRoutecreateDatasetGlobalData3 个方法, 可以通过这三个方法添加数据和路由,这样就可以自定义一些页面了。

getThemePath

获取主题路径,在contentLoaded添加路由时,会要求填写一个组件:这里的组件填写的是一个字符串,你可以用@site/前缀表示主路径下的组件,也可以使用@theme/前缀表示getThemePath返回的路径下的组件。

export default function (context: LoadContext, options: any): Plugin {
  const themePath = path.resolve(__dirname, "./theme");
  return {
    name: "docusaurus-plugin-docs-info",
    getThemePath() {
      return themePath;
    },
  };
}
// 添加路由
addRoute({
  path: "/timeline",
  component: "@theme/Timeline",
  modules: {
    articles: pageData,
  },
  exact: true,
});

injectHtmlTags

在 body/head 中注入一些标签,比如添加一些统计代码,广告代码等。

postBuild

构建完成时调用,会有生成路由的信息,可以在这里做一些事情,比如生成 sitemap rss 等。

validateOptions

校验选项,可以在这里校验一些配置是否正确。

Hooks

除了上面的 API 之外,还有一些 hooks 可以使用,很多都是官方内部的,文档中并没有说明,也是我翻阅源码看到的,这里我列举一些我用到的:

useGlobalData/PluginData

用于获取在contentLoaded中设置的全局数据与插件数据,可以在组件中使用。

import useGlobalData, {
  usePluginData,
  useAllPluginInstancesData,
} from "@docusaurus/useGlobalData";

useDoc

获取文档数据,可以获取到文档的标题、内容、路径等。

import { useDoc } from "@docusaurus/theme-common/internal";

useColorMode

获取颜色模式,例如黑夜/白天模式。

import { useColorMode } from "@docusaurus/theme-common";

自定义主题

如果你想要自定义你自己的主题,你可以使用npm run swizzle --typescript命令,这样可以让你替换插件的默认组件,来自定义你自己的样式。

官方文档:Swizzling | Docusaurus

例如我就包装了原版的官方 docs 插件,然后自定义了主题,使我的博客 docs 稳定支持了创建时间与阅读时间。

使用包装器模式重制官方 docs 插件

虽然使用其它模式,例如再新写一个插件,也可以实现,但是使用包装器模式,可以更好的保持原有的插件的功能,只是在原有的基础上添加一些新的功能。

此处需要注意一个坑,如果使用包装器模式需要在docusaurus.config.ts中将原本的 docs 插件关闭掉,否则会产生冲突。

  presets: [
    [
      "classic",
      {
        docs: false,
        // docs: {
        //   routeBasePath: "/",
        //   sidebarPath: "./sidebars.ts",
        //   // Please change this to your repo.
        //   // Remove this to remove the "edit this page" links.
        //   editUrl: "https://github.com/codfrm/blog",
        //   showLastUpdateTime: true,
        // },
      } satisfies Preset.Options,
    ],
  ],

引用时在plugin单独引用

  plugins: [
    [
      "docusaurus-plugin-content-docs-ex",
      {
        routeBasePath: "/",
        sidebarPath: "./sidebars.ts",
        // Please change this to your repo.
        // Remove this to remove the "edit this page" links.
        editUrl: "https://github.com/CodFrm/blog/edit/main",
        showLastUpdateTime: true,
      },
    ]
  ]

然后就可以在插件中使用原有的 docs 插件,然后在原有的基础上添加一些新的功能。

export default async function pluginContentDocs(
  context: LoadContext,
  options: PluginOptions & { debug?: boolean }
): Promise<Plugin<LoadedContent>> {
  const ret = (await docsPlugin.call(
    this,
    context,
    options
  )) as Plugin<LoadedContent>;
  const themePath = path.resolve(__dirname, "./theme");
  ret.getThemePath = () => {
    return themePath;
  };

  const warpLoadContent = ret.loadContent;
  ret.loadContent = async () => {
	const ret = await warpLoadContent();
	//  ....一些操作
	return ret;
  }

  return ret;
}

更多详细的内容你可以看我的插件:docusaurus-plugin-content-docs-ex