Docusaurus 101

By 增廣建文

Agenda

  • What's Docusaurus?
  • Main Features
  • Extra Plugins

What's Docusaurus?

  • 由FB團隊用React開發的文件撰寫工具,支援多國語系
  • 除了用於寫文件外,也可以用來當作blog
  • 用markdwon語法就能輕鬆完成文件
  • 對於熟悉React者能簡單的對網站做出調整
  • 有現成的插件可以擴充文件的互動性

Installation


V2版已經正式發布,就不建議在安裝之前的V1版

底下兩種方式都能進行安裝(typescript的選項可視需求刪減)

$ npx @docusaurus/init@latest init <my-website> classic --typescript
// if node.js > 16.14
$ npx create-docusaurus@latest init <my-website> classic --typescript

classic用的會是官方預設的模板,如果有其他喜歡的也能做替換

可以透過npm start來快速啟動網頁,訪問的網址會是http://localhost:3000

Setup


為了要讓document能夠客製化,通常會須改docusaurus.config.js中的以下屬性

  • 基礎網站屬性像是title, url, baseUrl, organizationName, projectName
  • navbar跟footer可以去修改裡面的items
  • code block的theme設定會在prism屬性下

Fix Hot-reload


因為套件版本的緣故有可能會遇到hot-reload失效(按鈕不能點)

打開console就會看到Uncaught ReferenceError: process is not defined

主要是因為webpack有缺少相依套件而導致的,可以透過底下的指令來補安裝

npm i process

Custom Theme


由於完整的docusaurus的code非常複雜, 官方提供swizzle機制讓使用者知道哪部分可以改

npm run swizzle @docusaurus/theme-classic --list

1. Content Type - Pages


比較常用在單頁的資訊呈現,像是首頁或是聯繫方式等

預設的範例可以在src/pages內找到,網頁路徑對應的方式如下

  • /src/pages/index.js[baseUrl]
  • /src/pages/foo.js[baseUrl]/foo
  • /src/pages/foo/test.js[baseUrl]/foo/test
  • /src/pages/foo/index.js[baseUrl]/foo/

基本規則就是要到某資料夾的根目錄的話就是會需要index.js

這裡的[baseUrl]會根據前面的config設定而有所不同,網址須放在footer/ navbar方便跳轉

Create Page with Markdown


用md寫的話最方便,但是可以客製化的地方就比較少且不方便

檔案會放在./src/pages/hello.md

---
title: my hello page title
description: my hello page description
hide_table_of_contents: true
---

# Hello

How are you?

下一頁會介紹如何使用react來寫page

Create Page with React

// ./src/pages/hello.js
import React from 'react';
import Layout from '@theme/Layout';

function Hello() {
  return (
    <Layout title="Hello">
      <div
        style={{
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          height: '50vh',
          fontSize: '20px',
        }}>
        <p>
          Edit <code>pages/helloReact.js</code> and save to reload.
        </p>
      </div>
    </Layout>
  );
}

export default Hello;

2. Content Type - Docs


Docs的功用和page非常的類似,但是只有docs才會有sidebar

Docs是以階層式架構出發的,所以可以有大的分頁裡面含更多分頁

如果是以markdown語法撰寫頁面的話,endpoint會由header的id決定

---
id: part1
slug: /hello
---

使用slug的話則可以去改變[baseUrl],路徑盡量不要和現有的page重複

在檔案內要link到其他md可以直接使用相對路徑(要保留md的副檔名)

Sidebar Settings 1/2


如果sidebarPath沒填,預設會從docs資料夾下生成對應的sidebar

也可以透過自己建立sidebar.js來設定custom sidebar

module.exports = {
  mySidebar: [
    {
      type: 'category',
      label: 'Getting Started',
      items: [
        {
          type: 'doc',
          id: 'doc1',
        },
      ]}]
};

Sidebar Settings 2/2

  • items中的id就是每個md文件檔header中設定的唯一值,可以協助sidebar知道要在那些頁面上顯示
  • type會決定要導向的文件屬性,可以分為以下三種
    • category會建立階層式架構的樣貌
    • doc可以直接跳轉到另一頁的doc
    • link則會直接跳轉到指定的page
  • docusaurus.config.js中的themeConfig可以對siderbar的預設行為有更多設定
    • autoCollapseSidebarCategories 預設會把下層細項收起來
    • hideableSidebar 允許使用者隱藏sidebar

Multiple Sidebars


可以在sidebar.js內設定多組的sidebar,這樣就能針對瀏覽的頁面顯示不同sidebar

module.exports = {
  tutorialSidebar: {
    'Category A': ['doc1', 'doc2'],
  },
  apiSidebar: ['doc3', 'doc4'],
};

如果讓同一個id同時出現在多個sidebar的設定內就會變成random顯示一個

Pagination


預設的換頁符號是根據sidebar的設定推導出來的

也可以在markdown的header設定previous跟next按鈕上要顯示甚麼文字

---
pagination_next: getting-started
---

如果不想要有換頁的按鈕話,則可以設定displayed_sidebar: null

Doc Version


可以透過指令來把舊的doc給先打包起來,再繼續撰寫新版的doc

npm run docusaurus docs:version 1.1.0

而之後使用者預設會讀到的doc版本也就是v1.1.0

website
├── versions.json        # file to indicate what versions are available
├── versioned_docs
│   ├── version-1.1.0
│   │   ├── foo
│   │   │   └── bar.md   # https://mysite.com/docs/foo/bar
│   │   └── hello.md
│   └── version-1.0.0
│       ├── foo
│       │   └── bar.md   # https://mysite.com/docs/1.0.0/foo/bar
│       └── hello.md

Multi-instance


可以將文件分成community, production等等,根據不同情境顯示各自的文件

docusaurus.config.js的presets中會定義預設的版本,而其他版本則可以寫在plugins中

module.exports = {
  plugins: [
    [
      '@docusaurus/plugin-content-docs',
      {
        id: 'community',
        path: 'community',
        routeBasePath: 'community',
        sidebarPath: require.resolve('./sidebarsCommunity.js'),
        // ... other options
      },
    ],
  ],
};

3. Content Type - Blog


用法比較像寫網誌或是工作日誌,可以從Navbar跟footer快速訪問

檔案的命名方式會類似以下:

  • 2019-05-30-welcome.md
  • 2019-05-30-welcome/index.md

上述檔案的訪問路徑都會是http://localhost:3000/2019/05/30/welcome

如果要瀏覽全部的blog文章清單可以到http://localhost:3000/blog

Blog Header 1/2


Blog也支援sidebar的功能,預設會顯示其他近期發表過的文章

可以透過header的設定來讓預覽內容只顯示特定部分

---
title: Truncation Example
---

All these will be part of the blog post summary.

Even this.

<!--truncate-->

But anything from here on down will not be.

Blog Header 2/2


作者資訊或是封面圖片等也都可以在header中做設定

---
authors:
  name: Joel Marcey
  title: Co-creator of Docusaurus 1
  url: https://github.com/JoelMarcey
  image_url: https://github.com/JoelMarcey.png
---

Show Estimated Reading Time


會根據文章內的字數來大概推測出閱讀所需的時間

// docusaurus.config.js
module.exports = {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        blog: {
          showReadingTime: true, // When set to false, the "x min read" won't be shown
          readingTime: ({content, frontMatter, defaultReadingTime}) =>
            defaultReadingTime({content, options: {wordsPerMinute: 300}}),
        },
      },
    ],
  ],
};

Blog Only Mode


不一定要用docusaurus來寫doc,也有不少人會把他當網誌使用

// docusaurus.config.js
module.exports = {
  // ...
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        docs: false, // Optional: disable the docs plugin
        blog: {
          routeBasePath: '/', // Serve the blog at the site's root
        },
      },
    ],
  ],
};

Close Blog


如果不會用到blog的功能,可以到docusaurus.config.js設定檔去關掉它

module.exports = {
  // ...
  presets: [
    '@docusaurus/preset-classic',
    {
      docs: {
        routeBasePath: '/', // Serve the docs at the site's root
        /* other docs plugin options */
      },
      blog: false, // Optional: disable the blog plugin
      // ...
    },
  ],
};

Load Static Files


可以import img等等,預設所有的東西要放在static資料夾下

這樣build時才會被include,還可以自行新增要被include的資料夾

// docusaurus.config.js
module.exports = {
  title: 'My site',
  staticDirectories: ['public', 'static'],
  // ...
};

Reference Static Files


實際上要去reference檔案時可以用相對或絕對路徑

You write a link like this: [Download this document](/files/note.docx)

Docusaurus changes that to: <a href={require('static/files/note.docx')}>Download this document</a>
// xxx.js
import DocusaurusImageUrl from '@site/static/img/docusaurus.png';
import useBaseUrl from '@docusaurus/useBaseUrl';
// 3 ways to reference images
<img src={DocusaurusImageUrl} />;
<img src={require('@site/static/img/docusaurus.png').default} />
<img src={useBaseUrl('/img/docusaurus.png')} />;

Code Block


可以去prisma官網找喜歡的theme來替換,或是新增想支援的語言

//  docusaurus.config.js
module.exports = {
  themeConfig: {
    prism: {
      theme: require('prism-react-renderer/themes/dracula'),
      additionalLanguages: ['php', 'powershell'],
    },
  },
};

Add Title in Code Block


可以幫code block新增title來描述檔名或是補充事項

Highlight Lines


在要特別highlight的行數前去註解指定

function HighlightSomeText(highlight) {
  if (highlight) {
    // highlight-next-line
    return 'This text is highlighted!';
  }

  return 'Nothing highlighted';
}

function HighlightMoreText(highlight) {
  // highlight-start
  if (highlight) {
    return 'This range is highlighted!';
  }
  // highlight-end
}

Highlight Lines by Number


也可以透過指定行數的方式來highlight

Admonition


警告的部分總共分五種,note(灰白色), tip(綠色), info(藍色), caution(黃色), danger(紅色)

Title的部分和code block一樣可給可不給

:::note Your Title

Some **content** with _markdown_ `syntax`.

:::

Features when Writing in React


再來會介紹一些只有使用react來創建檔案時才可以擁有的功能

  • Admonition
  • MDX
  • Raw Loader
  • Tabs
  • TOC

Admonition with js


寫法會和md版的差很多

import Admonition from '@theme/Admonition';

export default function MyReactPage() {
  return (
    <div>
      <Admonition type="info">
        <p>Some information</p>
      </Admonition>
<Admonition type="tip" icon="💡" title="Did you know...">
  <p>
      Some texts
  </p>
</Admonition>
    </div>
  );
}

MDX


所有會用到JSX/ Recat的部分都得寫在mdx檔案內而不能使用md

// markdown-file.mdx
import Highlight from '@site/src/components/Highlight';

<Highlight color="#25c2a0">Docusaurus green</Highlight>

Raw Loader


raw-loader可以把load進來的react component當純文字顯示

需要先安裝套件後才能夠使用npm install raw-loader

// xxx.mdx
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';

<CodeBlock className="language-jsx">{MyComponentSource}</CodeBlock>

Tabs


可以透過切換分頁的方式顯示不同的資訊

// xxx.mdx
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

<Tabs>
  <TabItem value="apple" label="Apple" default>
    This is an apple 🍎
  </TabItem>
  <TabItem value="orange" label="Orange">
    This is an orange 🍊
  </TabItem>
  <TabItem value="banana" label="Banana">
    This is a banana 🍌
  </TabItem>
</Tabs>

Table of Content

More Plugins

  • mdx-embed
  • create-docusaurus-openapi
  • docusaurus-openapi-docs
  • Live Code Blocks
  • Math Equation

MDX Embed


mdx-embed可以在網頁內鑲嵌其他內容,一樣需要先安裝套件npm install mdx-embed

Show OpenAPI Spec


目前有好幾個插件都可以做到這個功能,目前最熱門的是docusaurus-openapi

可以使用npm install create-docusaurus-openapi來安裝

這個插件會比較適合在創建docusaurus專案時就用

Show OpenAPI Spec Alternatives


也可以使用另一個套件docusaurus-openapi-docs

需要安裝plugin與theme
npm install docusaurus-plugin-openapi-docs docusaurus-theme-openapi-docs

Setup docusaurus-openapi-docs

Setup Live Code Blocks


需要先安裝套件npm install --save @docusaurus/theme-live-codeblock

這個額外的套件可以在不embed其他網頁的情況下來跑jsx/tsx的code

// docusaurus.config.js
module.exports = {
  plugins: ['@docusaurus/theme-live-codeblock'],
  themeConfig: {
    liveCodeBlock: {
      /**
       * The position of the live playground, above or under the editor
       * Possible values: "top" | "bottom"
       */
      playgroundPosition: 'bottom',
    },
  },
};

Example for Live Code Blocks


簡單寫一個會顯示現在時間的範例

```jsx live
function Clock(props) {
  const [date, setDate] = useState(new Date());
  useEffect(() => { var timerID = setInterval(() => tick(), 1000);
    return function cleanup() {
      clearInterval(timerID);
    };
  });
  function tick() {
    setDate(new Date());
  }
  return (
    <div>
      <h2>It is {date.toLocaleTimeString()}.</h2>
    </div>
  );
}

Add custom component to live code blocks


要透過swizzle去找live code blocks的原始碼來修改
npm run swizzle @docusaurus/theme-live-codeblock ReactLiveScope

// src/theme/ReactLiveScope/index.js
import React from 'react';
const ButtonExample = (props) => (
  <button
    {...props}
  />
);
// Add react-live imports you need here
const ReactLiveScope = {
  React,
  ...React,
  ButtonExample,
};

export default ReactLiveScope;

Use custom component in md

function MyPlayground(props) {
  return (
    <div>
      <ButtonExample onClick={() => alert('hey!')}>Click me</ButtonExample>
    </div>
  );
}

md內會顯示網址的tag (ex: <a>)都會被當成plain text處理

Math Equation