react-markdown 使用總結

xiaoAgiao發表於2020-08-27

示例和程式碼

react-code-previewer

前言

react-markdown 是一款非常優秀的 markdown 轉 html 的 react 元件,但是官方文件是全英文而且也沒有說明的太詳細,我就把自己開發遇到的問題以及總結記錄下來,希望幫助更多的開發人員。

安裝和使用

安裝

npm install --save react-markdown
yarn add react-markdown

基本使用

const React = require('react')
const ReactDOM = require('react-dom')
const ReactMarkdown = require('react-markdown')

const input = '# This is a header\n\nAnd this is a paragraph'

ReactDOM.render(<ReactMarkdown source={input} />, document.getElementById('container'))

這裡有個問題 source 屬性是直接設定成 input 字串,當然如果你想記錄的文字不多倒還好,如果你是想做成文件形式那麼設定成字串顯然不好,正確的操作應該是獲取.md 檔案內容給source屬性,下面我們就來講如何操作。

react-markdown source 屬性從 .md 檔案中獲取內容

配置webpack

在使用之前我們要設定 webpack 支援.md 檔案,要不然直接引入會報錯,程式碼如下:

npm install -D raw-loader
yarn add -D raw-loader
 rules: [
      {
        test: /\.md$/,
        use: "raw-loader"
      },
      ...

這樣設定好之後就可以了,然後我們就可以直接引入.md 檔案做為 source 屬性了,是不是非常方便!

api.md

## API

### PreviewLayout

| 引數     | 說明                       | 型別 | 預設值 | 版本  |
| :------- | :------------------------- | :--: | :----: | :---: |
| children | 傳遞的元件,可以是任意元件 | jsx  |  null  | 0.1.0 |

### MdPreviewer

| 引數 | 說明          |  型別  | 預設值 | 版本  |
| :--- | :------------ | :----: | :----: | :---: |
| md   | markdown 文件 | string |  null  | 0.1.0 |

### CodePreviewer

| 引數     | 說明           |  型別  | 預設值 | 版本  |
| :------- | :------------- | :----: | :----: | :---: |
| code     | 要顯示的程式碼   | string |  null  | 0.0.1 |
| showCode | 是否要展示程式碼 |  bool  |  true  | 0.1.0 |
import apiMd from "../md/api.md";
<ReactMarkdown
  source={apiMd}
  escapeHtml={false}
  renderers={{
    code: CodeBlock,
    heading: HeadingBlock
  }}
/>

執行看看

clipboard.png

SyntaxHighlighter 設定語法高亮

react-markdown 預設設定程式碼只有個灰色背景,並沒有對語法進行高亮設定,我們可以根據它提供的Node types 的 code 屬性進行自定義語法高亮,這裡我們要引入SyntaxHighlighter包。

npm install react-syntax-highlighter
yarn add react-syntax-highlighter

然後,新建一個CodeBlock.js 檔案

import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter";
// 設定高亮樣式
import { coy } from "react-syntax-highlighter/dist/esm/styles/prism";
// 設定高亮的語言
import { jsx, javascript, sass, scss } from "react-syntax-highlighter/dist/esm/languages/prism";

class CodeBlock extends PureComponent {
  static propTypes = {
    value: PropTypes.string.isRequired,
    language: PropTypes.string
  };

  static defaultProps = {
    language: null
  };

  componentWillMount() {
    // 註冊要高亮的語法,
    // 注意:如果不設定打包後供第三方使用是不起作用的
    SyntaxHighlighter.registerLanguage("jsx", jsx);
    SyntaxHighlighter.registerLanguage("javascript", javascript);
  }

  render() {
    const { language, value } = this.props;
    return (
      <figure className="highlight">
        <SyntaxHighlighter language={language} style={coy}>
          {value}
        </SyntaxHighlighter>
      </figure>
    );
  }
}

export default CodeBlock;

這裡有三點要說明一下:

  • 設定語法高亮的語言
import { jsx, javascript, sass, scss } from "react-syntax-highlighter/dist/esm/languages/prism";

node_modules/react-syntax-highlighter/dist/esm/languages/prism下有好多語言提供選擇,你們可以自己按照自己的意思挑選要引入的語言,上面是我引入的語言。

clipboard.png

  • 註冊你的語言

上面我們引入了語言包,我們還要註冊一下,如果不註冊的話,有可能打包後的高亮不起作用,以防萬一我們把需要的語言都註冊下。

SyntaxHighlighter.registerLanguage("jsx", jsx);
SyntaxHighlighter.registerLanguage("javascript", javascript);
  • 設定語法高亮樣式
import { coy } from "react-syntax-highlighter/dist/esm/styles/prism";

node_modules/react-syntax-highlighter/dist/esm/styles/prism下有好多樣式提供選擇,我選擇的是 coy,你們可以自己一個個試試挑選自己喜歡的語法高亮顏色。

clipboard.png

下面我們再看看這段程式碼

render() {
    const { language, value } = this.props;
    return (
      <figure className="highlight">
        <SyntaxHighlighter language={language} style={coy}>
          {value}
        </SyntaxHighlighter>
      </figure>
    );
  }

透過從 props 獲取要設定的語言 language,程式碼 value,樣式從本地設定為 coy,那麼父元件裡面我們這樣設定:

import CodeBlock from "../CodeBlock";
import codePreviewMd from "../md/codePreviewer.md";

<ReactMarkdown
  source={codePreviewMd}
  escapeHtml={false}
  renderers={{
    code: CodeBlock,
    heading: HeadingBlock
  }}
/>

那麼我們 codePreviewer.md 檔案裡面就可以這樣設定程式碼

codePreviewer.md

<div>
<section></section>
</div>
import { PreviewLayout } from "react-code-previewer";

<PreviewLayout></PreviewLayout>;

上面的 html & code 和 jsx & code 就會透過 code:CodeBlock 以 language 和 value傳入到CodeBlock。

設定 <h123456 標題anchor 錨點功能

透過發現我們知道 github 的 markdown 的 README.md 文件是有錨點功能的,我們現在使用的 react-markdown是沒有這個功能的,那麼我們只能自定義了,好在 react-markdown 為我們提供了重寫 h 標籤的屬性:heading,下面我們來看看如何設定。

什麼是錨點?

使用命名錨記可以在文件中設定標記,這些標記通常放在文件的特定主題處或頂部。然後可以建立到這些命名錨記的連結,這些連結可快速將訪問者帶到指定位置。

所以錨點功能包含兩個部分:id 標記(或者 name 標記)定位錨點的連結 兩部分構成,如下是我設定 h 標籤錨點的程式碼:

<h1 id='h1-anchor-id'>
  <span>{children}</span>
  <a href='#h1-anchor-id'>#</a>
</h1>

點選 a 標籤就可以定位到 id=’h1-anchor-id’的h1。這裡有個問題就是我的標題可能是 h1-h6 我不可能每次都自己寫 上面的程式碼如

<h2 id='h1-anchor-id'>
  <span>{children}</span>
  <a href='#h1-anchor-id'>#</a>
</h2>
<h3 id='h1-anchor-id'>
  <span>{children}</span>
  <a href='#h1-anchor-id'>#</a>
</h3>

這顯然程式碼有點冗餘了,我們只有自己寫個公共的方法去設定 h1-h6,我們建立一個 Heading.js檔案
Heading.js

import React from "react";

const elements = {
  h1: "h1",
  h2: "h2",
  h3: "h3",
  h4: "h4",
  h5: "h5",
  h6: "h6"
};

function Heading({ level, children, ...props }) {
  return React.createElement(elements[level] || elements.h1, props, children);
}

Heading.defaultProps = {
  type: "h1"
};

export default Heading;

這樣我透過你傳遞進來的 level屬性決定使用 h1-h6 中的某個標題了。
接下來我們建立HeadingBlock.js 檔案

HeadingBlock.js

import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import cls from "classnames";
import Heading from "./Heading";

class HeadingBlock extends PureComponent {
  renderHtml = () => {
    const { level, children } = this.props;

    if (children && children.length > 0) {
      const nodeValue = children[0].props.value;
      return (
        <Heading level={`h${level}`} id={nodeValue}>
          <span className="title">{children}</span>
          <a href={`#${nodeValue}`} className="link">
            #
          </a>
        </Heading>
      );
    } else {
      return <>{children}</>;
    }
  };
  render() {
    return <>{this.renderHtml()}</>;
  }
}

export default HeadingBlock;

如上面程式碼所示,我們透過父元件傳遞進來的 level:就是 1-6 數值, children:標題內容,如 level:2,children:’這是標題內容’,其實就是 markdown 裡的:## 這是標題內容。這樣就把 level 傳遞到 Heading元件就可選用你設定的 h 了,這樣錨點功能就設定完畢,我們再來看看父元件程式碼如何設定

import CodeBlock from "CodeBlock";
import HeadingBlock from "HeadingBlock";
import codePreviewMd from "../md/codePreviewer.md";

<ReactMarkdown
  source={codePreviewMd}
  escapeHtml={false}
  renderers={{
    code: CodeBlock,
    heading: HeadingBlock
  }}
/>

總結

1、react-markdown 解決了我們渲染 markdown檔案樣式和語法高亮問題
2、react-syntax-highlighter 解決我們渲染 markdown 語法高亮問題,其實它可以單獨使用的,具體的請檢視官方文件
3、react-syntax-highlighter設定的語言要註冊一下,如:SyntaxHighlighter.registerLanguage("jsx", jsx);否則可能不起作用
4、react-markdown 提供了好多替代方案都包含在 Node Types 裡面

本作品採用《CC 協議》,轉載必須註明作者和本文連結