react(gatsby.js)でhighlight.jsを使う

react(gatsby.js)でhighlight.jsを使う

react(gatsby.js)でhighlight.jsを使う

目的

  • 自身のgatsby.jsで作成したブログで何かしらのJSライブラリを用いてコードのハイライトを行う。

実装時の要件

  • ライブラリでサポートされている言語が多ければ多いほど、今後の記事のハイライトを制限しないので良い
  • ファイルサイズが小さいとナイス

使用するライブラリ

  • highlight.js

    • npm管理可能
    • 動的にハイライトをする言語を設定可能
    • サポートされている言語多種多様
    • スタイルがすでに用意されていて、どれもどこかで見たようなUIでベンチマーク感があり安心感

実装

highlight.jsを追加する。今回はES6以上で書きます。

// npmモジュール追加
yarn add highlight.js

今回はコードハイライトを使用する機会が記事のページでしか使わないため、Util的なものは実装せずに直接該当のReact Componentに突っ込む。 以下のようにimportをする。

// メインインスタンス。こいつを通して処理を走らす
import hljs from 'highlight.js/lib/core';
// ハイライトしたい言語
import javascript from 'highlight.js/lib/languages/javascript';
// `javascript`の実態はオブジェクトを返すパース関数。registerすると対象言語を初期化時にパースしてくれる
hljs.registerLanguage('javascript', javascript);

なお、以下のようなimportをすると上述のimport hljs from 'highlight.js/lib/core'に全ての言語をregisterLanguageしたhljsインスタンスが返されます。それぞれのパーサー関数はそこまで大きいサイズではないですが、なるべく最小限に抑えてhighlight.js/lib/coreを使うようにします。

// 全言語対応のhljsインスタンス
import hljs from 'highlight.js';

ここまでは公式でも書いてます。 私のブログではこちらの実装は以下のようにしました。 useEffectがわからない方はこちらを確認してください。

import hljs from 'highlight.js/lib/core';
import javascript from 'highlight.js/lib/languages/javascript';
import 'highlight.js/styles/atom-one-dark.css';

hljs.registerLanguage('javascript', javascript);

const ArticleComponent = () => {
    useEffect(() => {
        hljs.initHighlighting();
    });
    return <Article />;
}

ここではhljs.initHighlighting()を使うようにしてください。 hljs.initHighlightingOnLoad()の実態はDOMContentLoaded時にinitHighlighting()を呼び出すようにaddEventListenerするだけのものです。 しかし、おそらくReactの仕組み上、要素がアクセス可能になるタイミング、いわゆるマウント済みになるタイミング(componentDidMount)とDOMContentLoadedのタイミングが同じではないため、リスナーがあってもハイライト処理がReactのマウント時より前に走ってしまうようです。 なので、呼び出す際は上記のようにReactのマウント時にinitHighlightingを実行するようにします。 するとこんな感じでハイライトしてくれます。何となくよくあるハイライトだと思ったのでatom-one-dark.cssを選んでます。こちらの例のスタイルがそれになります。

demo

また、今回の実装だと初回時のみにハイライトをしてくれますが、初回以降上記のコードだとハイライトしてくれなくなります。これはhighlight.jsのインスタンスが初期化以降は処理を走らせないようにフラグを用いてテキストのパース処理をスキップしているためです。 今回は初回以降もパースしてもらわないといけない要件なのでフラグを折りに行きます。

import hljs from 'highlight.js/lib/core';
import javascript from 'highlight.js/lib/languages/javascript';
import 'highlight.js/styles/atom-one-dark.css';

hljs.registerLanguage('javascript', javascript);

const ArticleComponent = () => {
    useEffect(() => {
        hljs.initHighlighting();
        // React環境だと初回以降ハイライト処理が入らないため外部からフラグをfalseに
        hljs.initHighlighting.called = false;
    });
    return <Article />;
}

こんな感じに実装すると初回以降もハイライトしてくれるようになります。 アウトローな実装になりましたが、今回はこんなところで問題なく動いているので開発を終了しました。

公式ドキュメント