microCMS

コンテンツAPIのパース処理の選択肢(Next.js編)

りゅーそう

この記事は公開後、1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは野崎です。
先日「繰り返しフィールド・カスタムフィールドをマスターしよう」という記事を書かせていただきました。
https://blog.microcms.io/intro-custom-repeatfield/

そこで様々な形式のコンテンツを扱う方法を紹介させていただきました。
microCMSはAPIベースのヘッドレスCMSです。
画面上でスタイルをつけるにはパース処理をしたり、CSSを書いたりなどして加工・装飾する必要があります。

当記事では、前回の記事を題材にコンテンツをパースする選択肢を紹介します。

要約

  • Jamstack構成にして、コンテンツを事前生成するようのがベストプラクティス
  • サイトの規模や要件によってはフロントエンドでパース処理を行うことも検討する
  • スタイルを適用するという目的だけならCSSを当てるだけでもOKなケースも多い


コンテンツをパースする方法

それでは具体的に紹介しようと思います。

サーバーサイドで処理を行う

Jamstackの特徴の一つとして「Pre-Rendring」があげられます。
従来のSSRなどとは異なり、ビルド時にコンテンツを事前取得し、ページを生成します。

サーバーサイド側でパース処理を行うことで、コンテンツを事前に加工することができパフォーマンスの向上が期待できます。
Next.jsやNuxt.jsなどを使用するとサーバーサイド側の処理を簡単に記述することができます。

Next.jsではgetStaticPropsを利用して以下のようにシンタックスハイライトなどの処理を行うことができます。
パースを行うライブラリcheerioを使用しています。
cheerioはNode.js(サーバーサイド)上でHTMLをパースするライブラリです。詳しくはドキュメントをご覧ください。
https://github.com/cheeriojs/cheerio

type Props = {
  blog: BlogType;
  content: string;
};


export const getStaticProps: GetStaticProps = async (
  context: GetStaticPropsContext<{ id: string }>,
) => {
  const { id } = context.params;
  const blog = await getBlog(id);
  const { content } = blog;
  const $ = cheerio.load(content);
  $('pre code').each((_, element) => {
    const result = hljs.highlightAuto($(element).text());
    $(element).html(result.value);
    $(element).addClass('hljs');
  });
  $('img').each((_, element) => {
    $(element).html();
    $(element).addClass(`${styles.blogContentImg}`);
  });
  $('a').each((_, element) => {
    $(element).html();
    $(element).addClass(`${styles.blogContentLink}`);
  });
  $('h1').each((_, element) => {
    $(element).html();
    $(element).addClass(`${styles.heading1}`);
  });

  return {
    props: {
      blog,
      content: $.html(),
    },
  };
};


export const getStaticPaths = async () => {
  const { blogs } = await getBlogList();
  const paths = blogs.contents.map((content) => `/blogs/${content.id}`);


  return { paths, fallback: false };
};

(コード全容はこちらをご参考ください)

サーバーサイド側(Next.jsでは getStaticProps がそれに当たります。)で処理を行い、Jamstack構成を取ることでパフォーマンスの向上が期待できます。
またクライアント側にパース処理が露出しないのでセキュリティも高まります。

Nuxt.jsの例はこちらを参考にしてみてください
https://blog.microcms.io/syntax-highlighting-on-server-side/

クライアントサイドで処理を行う

SSRの構成を取る場合やAPIのスキーマが複雑な場合、クライアントサイドのパースライブラリを活用したいケースなどはクライアントサイドでの処理も検討すると良いと思います。
パフォーマンスではJamstackに劣るケースもありますが、その分ライブラリを活用することにより処理を簡単に行えます。

前回の記事のmicroCMSのテキストエリアのマークダウン記法を読み込んでパースする例を紹介します。
マークダウンをパースするライブラリは多くありますが、ここでは react-markdown を使用しました。

実装は react-markdown のシンタックスハイライトの例を参考にしました。
https://github.com/remarkjs/react-markdown#use-custom-components-syntax-highlight

import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { tomorrow } from 'react-syntax-highlighter/dist/cjs/styles/prism';


type Props = {
  text: string;
};

// propsのtextにはAPIのテキストフィールドの値を入れます。
export const MarkdownField: React.VFC<Props> = ({ text }) => {
  return (
    <ReactMarkdown
      children={text}
      components={{
        code({
          node,
          inline,
          className,
          children,
          ...props
        }) {
          const match = /language-(\w+)/.exec(className || '');


          return !inline && match ? (
            <SyntaxHighlighter
              children={String(children).replace(/\n$/, '')}
              style={tomorrow}
              language={match[1]}
              PreTag="div"
              {...props}
            />
          ) : (
            <code className={className} {...props}>
              {children}
            </code>
          );
        },
      }}
    />
  );
};


このようにシンタックスハイライトを細かくカスタマイズしたいなどの要件がある場合は、クライアントサイドのライブラリを活用するのも選択肢の一つです。

クライアントサイドでHTMLをパースしたいという場合には、先程紹介したcheerioはNode.js上(Next.jsだとgetStaticProps、getServerSideProps内の処理がそれにあたります)でしか動作しないため、その他のライブラリを使用する必要があります。

クライアントサイドでHTMLをパースするには unified 関連のライブラリが便利です。
unifiedとはざっくりまとめると「構文木(自然言語を名詞や動詞の構文で解析して木構造にしたもの)を使用して様々なテキストを処理するためのインターフェース」です。

構文木から文法のための記号などの情報を除いたものをASTと言います。
コーティングをするとき様々なタグを使用しますが、実際にブラウザに表示される時はタグは表示されません。それをイメージしていただければと思います。
例)<h1>見出し</h1>見出し

詳しくはドキュメントをご覧ください。
https://unifiedjs.com/

ただ、実際に基本的な処理をするときはこれらの知識がなくても問題はありません。
HTMLをパースするには rehype というライブラリを使用します。
rehype-parse というunifiedのライブラリを使うことによって、受け取ったテキストを解釈してASTを生成することができます。
Reactでrehypeを使用する際には rehype-react を使用することによって、さらにReactDOMが解釈できるようにパース処理を行うことができます。
これらの処理をまとめると以下のようになります。

import { Fragment, createElement } from 'react';
import { unified } from 'unified';
import parse from 'rehype-parse';
import rehypeReact from 'rehype-react';
import styles from './paragraph.module.scss';


type Props = {
  children: React.ReactNode;
};


export const Paragraph: React.VFC<Props> = ({ children }) => {
  return <p className={styles.main}>{children}</p>;
};




export const parseHtml = (content: string) => {
  const htmlAst = unified()
    .use(parse, { fragment: true })
    .use(rehypeReact, {
      createElement,
      Fragment,
      components: {
        p: Paragraph,
      },
    })
    .processSync(content).result;

  return htmlAst;
};


クライアントでパースを行うことで、様々なユースケースに対応できるケースも広がります。
またパフォーマンス面でもCDNなどを活用することによって対応する事例もあるようです。
詳しくは、microCMSアドベントカレンダーやジャムジャム!!Jamstackの過去のアーカイブなどをご覧いただくと参考になるかと思います。
microCMS Advent Calender 2021
https://qiita.com/advent-calendar/2021/microcms
ジャムジャム!!Jamstackアーカイブ
https://www.youtube.com/channel/UCjdOq0rAB8Or1uHio9N0_pQ/videos

パース処理を行わないという選択肢

ここまで、どのようにパース処理を行うという話をさせていただきました。
しかしスタイルを当てたいだけの場合など、パース処理をしなくても良いケースも存在します。
Next.js(React)の場合、dangerouslySetInnerHTML を使用します。ドキュメントの通り正しく使用してください。
https://ja.reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml

<div key={index} className={styles.className}>
  <div dangerouslySetInnerHTML={{ __html: body.richText }} />
</div>


CSSをラップしたclassNameに適用します。

.className {
  //スタイルを当てたい要素にCSSを指定する
  h1 {
    color: red;
  }

  iframe {
    height: auto;
    width: 100%;
    aspect-ratio: 16 / 9;
  }
}


加工の柔軟度はなくなりますが、スタイルを適用するのが簡単になるかと思います。
とりあえずスタイルを当てたいというケースやパースしきれない要素などがある場合はこの方法も基本的ではありますが、知っておくと良いと思います。

最後に

この記事ではコンテンツをパースする選択肢を紹介させていただきました。
実務においては、要件やスケジュールなどに応じて方法を選択できると良いかと思います。他にも良い方法がありましたらぜひSNSやイベントなどでシェアしてください!


-----

microCMSは日々改善を進めています。
ご意見・ご要望は管理画面右下のチャット、公式Twitterお問い合わせからお気軽にご連絡ください!
引き続きmicroCMSをよろしくお願いいたします!

まずは、無料で試してみましょう。

APIベースの日本製ヘッドレスCMS「microCMS」を使えば、 ものの数分でAPIの作成ができます。

microCMSを無料で始める

microCMSについてお問い合わせ

初期費用無料・14日間の無料トライアル付き。ご不明な点はお気軽にお問い合わせください。

お問い合わせ

microCMS公式アカウント

microCMSは各公式アカウントで最新情報をお届けしています。
フォローよろしくお願いします。

  • X
  • Discord
  • github

ABOUT ME

りゅーそう
1994年生まれ。 前職は高校地理歴史科教員。2021/9〜microCMS入社。React/TypeScriptが好きです。