生命周期 API
在构建过程中,插件会被并行加载,各自获取自己的内容并将它们变成路由。 插件还可以配置 webpack 或后处理构建生成的文件。
async loadContent()
插件应该使用这个生命周期从数据源(文件系统、远程 API、无头 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 = {
  /**
   * With leading slash. Trailing slash will be normalized by config.
   */
  path: string;
  /**
   * Component used to render this route, a path that the bundler can `require`.
   */
  component: string;
  /**
   * Props. Each entry should be `[propName]: pathToPropModule` (created with
   * `createData`)
   */
  modules?: RouteModules;
  /**
   * The route context will wrap the `component`. Use `useRouteContext` to
   * retrieve what's declared here. Note that all custom route context declared
   * here will be namespaced under {@link RouteContext.data}.
   */
  context?: RouteModules;
  /**
   * Nested routes config, useful for "layout routes" having subroutes.
   */
  routes?: RouteConfig[];
  /**
   * React router config option: `exact` routes would not match subroutes.
   */
  exact?: boolean;
  /**
   * React router config option: `strict` routes are sensitive to the presence
   * of a trailing slash.
   */
  strict?: boolean;
  /**
   * Used to sort routes.
   * Higher-priority routes will be matched first.
   */
  priority?: number;
  /**
   * Optional route metadata
   */
  metadata?: RouteMetadata;
  /**
   * Extra props; will be available on the client side.
   */
  [propName: string]: unknown;
};
/**
 * Plugin authors can assign extra metadata to the created routes
 * It is only available on the Node.js side, and not sent to the browser
 * Optional: plugin authors are encouraged but not required to provide it
 *
 * Some plugins might use this data to provide additional features.
 * This is the case of the sitemap plugin to provide support for "lastmod".
 * See also: https://github.com/facebook/docusaurus/pull/9954
 */
export type RouteMetadata = {
  /**
   * The source code file path that led to the creation of the current route
   * In official content plugins, this is usually a Markdown or React file
   * This path is expected to be relative to the site directory
   */
  sourceFilePath?: string;
  /**
   * The last updated date of this route
   * This is generally read from the Git history of the sourceFilePath
   * but can also be provided through other means (usually front matter)
   *
   * This has notably been introduced for adding "lastmod" support to the
   * sitemap plugin, see 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 页面,显示 你的朋友是: Yangshun, Sebastien:
import React from 'react';
export default function FriendsComponent({friends}) {
  return <div>你的朋友是:{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 prop
      addRoute({
        path: '/friends',
        component: '@site/src/components/Friends.js',
        modules: {
          // 从 prop 名称 -> JSON 文件路径
          friends: friendsJsonPath,
        },
        exact: true,
      });
    },
  };
}
setGlobalData(data: any): void
这个函数允许创建全局插件数据,在任何页面都可以被读取,包括其他插件创建的页面以及你的主题布局。
你的主题代码可以通过 useGlobalData 和 usePluginData 钩子在客户端访问这一数据。
全局数据是……全局的:它的体积会影响你的网站的每一页的加载时间,所以把它弄小一点。 如果可能,请选择用 createDate 创建页面专有的数据。
比如,下面的插件会创建一个 /friends 页面,显示 你的朋友是: 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;
      // 生成全局朋友数据
      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,由 Docusaurus 根据当前为客户端还是服务端构建生成。 你可以把它当作是会被和你的配置合并的基本配置。
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', // Opens localhost:3000/docs instead of localhost:3000/
        },
      };
    },
  };
}
configurePostCss(options)
在生成客户端包裹时修改 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})
在 Docusaurus 生成的 HTML 中注入 head 或 body HTML 标签。
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;
  /**
   * inner 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>`],
      };
    },
  };
}
标签将添加如下:
headTagswill be inserted before the closing</head>tag after scripts added by config.preBodyTagswill be inserted after the opening<body>tag before any child elements.postBodyTagswill be inserted before the closing</body>tag after all child elements.
getClientModules()
Returns an array of paths to the client modules that are to be imported into the client bundle.
作为例子,如果你的主题会根据用户提供的 options 加载 customCss 或 customJs 文件:
export default function (context, options) {
  const {customCss, customJs} = options || {};
  return {
    name: 'name-of-my-theme',
    getClientModules() {
      return [customCss, customJs];
    },
  };
}