关于魔改Docusaurus的Blog插件

502 阅读4分钟

前言

Docusaurus ,是基于 React 驱动的静态网站生成器搭建的文档网站

不过国内 Docusaurus 的资源是真的少。不像 Vuepress。

需求

目前我只懂得官方是通过 BlogListPage 博客列表页根组件来实现的。那么如何实现 Docusaurus 全局读取博客 MarkDown 里的内容呢?

官方的实现方法

首先,在网站目录按照指定要求在 src 文件夹下,新建theme/BlogListPage 等文件夹

未标题-2.png

代码

import React from "react";
​
const BlogListPage = (props) => {
​
  console.log('====>props:', props);
​
  const { metadata, items } = props;
​
  return (
​
   <> </>
​
  )
​
}
​
export default BlogListPage
​
​
​

未命名.png

那么有什么办法实现全局的 data 呢?我们接着往下看

自写 BlogListPage 插件,实现全局读取

至于全局数据抓取,我还得感谢 愧怍 通过他的思路和指点,我算是弄懂了如何实现全局数据。

首先我们在 src 文件下,创建 plugin/plugin-content-blog 文件夹,分别创建 type.tsindex.ts

未标题-3.png

`type.ts 通过引入源码的实现的思路去强制 index.ts 需要的类型

未标题-6.png index.ts 主要通过官方插件使用的方法来实现的,拿到值后,通过 setGlobalData 函数存取到全局数据中。

其次,由于 Docusaurus 只支持加载 js 文件,需要将 ts 文件打包或者转成 js 文件。由于打包比较麻烦,我就采用了 ts 转 js 的方式。具体方法如下:

  1. 在终端输入指令:npm install -g typescript 即可。

  2. 在终端输入指令,找到 网站 plugin 目录下 plugin-content-blog 文件夹

    如:cd dome/my-website/src/plugin/plugin-content-blog

  3. 然后 tsc index.ts,你就发现 plugin-content-blog 目录会生产 index.js 和 type.js 等文件

未标题-4.png

未标题-5.png 最后,配置 docusaurus.config.js,把官方默认的 blog 给 false 掉,然后在插件的配置方法中引入我们写自己的插件 path.resolve(__dirname, './src/plugin/plugin-content-blog')

  presets: [

​    [

​      'classic',

​      /** @type {import('@docusaurus/preset-classic').Options} */

​      ({

​        docs: {

​          sidebarPath: require.resolve('./sidebars.js'),

​          // Please change this to your repo.// Remove this to remove the "edit this page" links.

​          editUrl:

​            'https://github.com/xxx',

​        },

​        blog: false, // 把原来的 blog 插件给关了// blog: {//   showReadingTime: true,//   // Please change this to your repo.//   // Remove this to remove the "edit this page" links.//   editUrl://     'https://github.com/xxx',// },

​        theme: {

​          customCss: require.resolve('./src/css/custom.css'),

​        },

​      }),

​    ],

  ],

引入配置好的插件

plugins: [

​    [

​      path.resolve(__dirname, './src/plugin/plugin-content-blog'), {

​        path: 'blog',

​        editLocalizedFiles: false,

​        blogSidebarTitle: '近期文章',

​        blogSidebarCount: 10,

​        postsPerPage: 10,

​        showReadingTime: true,

​        readingTime: ({ content, frontMatter, defaultReadingTime }) =>

​          defaultReadingTime({ content, options: { wordsPerMinute: 300 } }),

​      }

​    ],

  ],

官方插件使用方法的介绍 docusaurus.io/zh-CN/docs/…

// @ts-check

// Note: type annotations allow type checking and IDEs autocompletion



const lightCodeTheme = require('prism-react-renderer/themes/github');

const darkCodeTheme = require('prism-react-renderer/themes/dracula');

const path = require('path')




/** @type {import('@docusaurus/types').Config} */

const config = {

  title: 'My Site',

  tagline: 'Dinosaurs are cool',

  url: 'https://your-docusaurus-test-site.com',

  baseUrl: '/',

  onBrokenLinks: 'throw',

  onBrokenMarkdownLinks: 'warn',

  favicon: 'img/favicon.ico',



  // GitHub pages deployment config.

  // If you aren't using GitHub pages, you don't need these.

  organizationName: 'facebook', // Usually your GitHub org/user name.

  projectName: 'docusaurus', // Usually your repo name.



  // Even if you don't use internalization, you can use this field to set useful

  // metadata like html lang. For example, if your site is Chinese, you may want

  // to replace "en" with "zh-Hans".

  // i18n: {

  //   defaultLocale: 'en',

  //   locales: ['en'],

  // },

  i18n: {

​    defaultLocale: 'en',

​    locales: ['en', 'zh-Hans', 'fa'],

​    localeConfigs: {

​      en: {

​        htmlLang: 'en-GB',

​      },

​      // 如果你不需要覆盖默认值,你可以忽略这个语言(比如 zh-Hans)

​      fa: {

​        direction: 'rtl',

​      },

​    },

  },



  presets: [

​    [

​      'classic',

​      /** @type {import('@docusaurus/preset-classic').Options} */

​      ({

​        docs: {

​          sidebarPath: require.resolve('./sidebars.js'),

​          // Please change this to your repo.// Remove this to remove the "edit this page" links.

​          editUrl:

​            'https://github.com/xxx',

​        },

​        blog: false, // 把原来的 blog 插件给关了// blog: {//   showReadingTime: true,//   // Please change this to your repo.//   // Remove this to remove the "edit this page" links.//   editUrl://     'https://github.com/xxx',// },

​        theme: {

​          customCss: require.resolve('./src/css/custom.css'),

​        },

​      }),

​    ],

  ],



  plugins: [

​    [

​      path.resolve(__dirname, './src/plugin/plugin-content-blog'), {

​        path: 'blog',

​        editLocalizedFiles: false,

​        blogSidebarTitle: '近期文章',

​        blogSidebarCount: 10,

​        postsPerPage: 10,

​        showReadingTime: true,

​        readingTime: ({ content, frontMatter, defaultReadingTime }) =>

​          defaultReadingTime({ content, options: { wordsPerMinute: 300 } }),

​      }

​    ],

  ],



  themeConfig:

​    /** @type {import('@docusaurus/preset-classic').ThemeConfig} */

​    ({

​      navbar: {

​        title: 'My Site',

​        logo: {

​          alt: 'My Site Logo',

​          src: 'img/logo.svg',

​        },

​        items: [

​          {

​            type: "localeDropdown",

​            position: "left",

​          },

​          {

​            type: 'doc',

​            docId: 'intro',

​            position: 'left',

​            label: 'Tutorial',

​          },

​          // {to: '/blog', label: 'Blog', position: 'left'},

​          {

​            href: 'https://github.com/facebook/docusaurus',

​            label: 'GitHub',

​            position: 'right',

​          },

​        ],

​      },

​      footer: {

​        style: 'dark',

​        links: [

​          {

​            title: 'Docs',

​            items: [

​              {

​                label: 'Tutorial',

​                to: '/docs/intro',

​              },

​            ],

​          },

​          {

​            title: 'Community',

​            items: [

​              {

​                label: 'Stack Overflow',

​                href: 'https://stackoverflow.com/questions/tagged/docusaurus',

​              },

​              {

​                label: 'Discord',

​                href: 'https://discordapp.com/invite/docusaurus',

​              },

​              {

​                label: 'Twitter',

​                href: 'https://twitter.com/docusaurus',

​              },

​            ],

​          },

​          {

​            title: 'More',

​            items: [

​              {

​                label: 'Blog',

​                to: '/blog',

​              },

​              {

​                label: 'GitHub',

​                href: 'https://github.com/facebook/docusaurus',

​              },

​            ],

​          },

​        ],

​        copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`,

​      },

​      prism: {

​        theme: lightCodeTheme,

​        darkTheme: darkCodeTheme,

​      },

​    }),

};



module.exports = config;

type.ts

// type.ts 
​
import type {BrokenMarkdownLink, ContentPaths} from '@docusaurus/utils';
​
import type {BlogPostMetadata} from '@docusaurus/plugin-content-blog';
​
// @ts-ignore
​
import type {Tag} from '@docusaurus/types';
​
// @ts-ignore
​
import type {Metadata as BlogPaginatedMetadata} from '@theme/BlogListPage';
​
​
​
export type BlogContentPaths = ContentPaths;
​
​
​
export type BlogContent = {
​
  blogSidebarTitle: string;
​
  blogPosts: BlogPost[];
​
  blogListPaginated: BlogPaginated[];
​
  blogTags: BlogTags;
​
  blogTagsListPath: string;
​
};
​
​
​
export type BlogTags = {
​
[permalink: string]: BlogTag;
​
};
​
​
​
export type BlogTag = Tag & {
​
  /** Blog post permalinks. */
​
  items: string[];
​
  pages: BlogPaginated[];
​
};
​
​
​
export type BlogPost = {
​
  id: string;
​
  metadata: BlogPostMetadata;
​
  content: string;
​
};
​
​
​
export type BlogPaginated = {
​
  metadata: BlogPaginatedMetadata;
​
  /** Blog post permalinks. */
​
  items: string[];
​
};
​
​
​
export type BlogBrokenMarkdownLink = BrokenMarkdownLink<BlogContentPaths>;
​
export type BlogMarkdownLoaderOptions = {
​
  siteDir: string;
​
  contentPaths: BlogContentPaths;
​
  truncateMarker: RegExp;
​
  sourceToPermalink: {[aliasedPath: string]: string};
​
  onBrokenMarkdownLink: (brokenMarkdownLink: BlogBrokenMarkdownLink) => void;
​
};

index.ts

import {LoadContext, Plugin} from '@docusaurus/types';
​
import * as blogPluginExports from '@docusaurus/plugin-content-blog';
​
import type {PluginOptions} from '@docusaurus/plugin-content-blog';
​
import {BlogContent} from './types';
​
​
​
const blogPlugin = blogPluginExports.default;
​
​
​
async function blogPluginEnhanced(
​
  context: LoadContext,
​
  options: PluginOptions,
​
): Promise<Plugin<BlogContent>> {
​
  const blogPluginInstance: any = await blogPlugin(context, options);
​
​
​
  return {
​
•    ...blogPluginInstance,
​
•    async contentLoaded({content, actions}) {
​
•      // Create default plugin pages
​
•      await blogPluginInstance.contentLoaded({content, actions});
​
​
​
•      // Create your additional pages
​
•      const {blogPosts, blogTags} = content;
​
•      const {setGlobalData} = actions;
​
​
​
•      setGlobalData({
​
•        blogs: blogPosts,
​
•        tags: blogTags,
​
•      });
​
•    },
​
  };
​
}
​
​
​
module.exports = {
​
  ...blogPluginExports,
​
  default: blogPluginEnhanced,
​
};

怎么使用全局 blog 数据

在任意组件中,使用 useGlobalData 函数来获取。具体步骤如下

import React from "react";
import useGlobalData from "@docusaurus/useGlobalData";
​
const Home = () => {
  const globalData = useGlobalData();
  const blogPluginData = globalData?.['docusaurus-plugin-content-blog']?.[
    'default'
  ]
  console.log('全局博客的内容:', blogPluginData);
  return (
    <></>
  )
}
​
export default Home

未标题-7.png

以上就是实现全局读取博客内容的方法了,至于读取全局文档的,思路也差不多,但是我只读到标题和描述之类的,内容还没有抓到,如果你有更好的方法,希望能指点一下。

关于Docusaurus