まえがき
先日、数式を使ったブログ記事を書いた際、数式のレンダリングには MathJax の CDN を用いた。 Lighthouse でパフォーマンスを調べたところ、 「Reduce unused JavaScript」 の項目で不要なもののうち、MathJax に由来するものが 160 kB ほどあるということがわかった。 他にも不要なフォントファイルもあり、それらがパフォーマンススコアを下げる原因になっていた。 ビルド時に数式をレンダリングする処理を施すことができれば JavaScript は不要になり、 パフォーマンススコアが上がるだろうと推測される。
そこで CDN を使わずに数式をレンダリングする方法を調べたところ、 remark-math と、 rehype-katex もしくは rehype-mathjax を使う方法を知った。 それらの挙動を調べてみた。
なお、今回使用したコードは GitHub に保存してある。
rehype-katex と rehype-mathjax を試してみる
TeX 関連の rehype プラグインは rehype-katex と rehype-mathjax があるのだが、それらの違いを調べてみる。
下記マークダウンファイルをパースしてみる。
# Mathematical Document
## Formula
### Text Style
円周と直径の比を円周率と言い、 $\pi$ と表す。
### Display Style
$$
\zeta(s) = \sum^{\infty}_{n = 1} \frac{1}{n ^ s}
$$
コード全体は GitHub にある。 ここでは本質的な箇所のみ記載する。
remark-math も rehype-katex, rehype-mathjax も使わない場合
const contentNoMath = await unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeFormat)
.use(rehypeStringify)
.process(await read(inputFileName));
生成される HTMLは下記。
No Mathシンプルにマークダウンが HTML に変換されている。
remark-math のみ使った場合
const contentSimpleMath = await unified()
.use(remarkParse)
.use(remarkMath)
.use(remarkRehype)
.use(rehypeStringify)
.process(await read(inputFileName));
生成される HTMLは下記。
Simple Mathclass="math math-inline" のような属性が加えられている。
rehype-katex を使った場合
const contentKatex = await unified()
.use(remarkParse)
.use(remarkMath)
.use(remarkRehype)
.use(rehypeKatex)
.use(rehypeStringify)
.use(rehypeDocument, {
css: 'https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css',
})
.process(await read(inputFileName));
生成される HTMLは下記。
KaTeX<mi>, <mo> など細かくタグが作られている。
なお、remark-math を使わないと、きちんと生成されない。
rehype-mathjax を使った場合
const contentMathjax = await unified()
.use(remarkParse)
.use(remarkMath)
.use(remarkRehype)
.use(rehypeMathjax)
.use(rehypeStringify)
.process(await read(inputFileName));
生成される HTMLは下記。
MathJaxSVG が生成されている。
rehype-katex を使った場合と rehype-mathjax を使った場合の差異は下記のとおり。
| KaTeX | MathJax | |
|---|---|---|
| 生成される要素 | HTML | SVG |
| ファイルサイズ | 8 kB | 12 kB |
Astro への KaTeX の導入方法
HTML が生成されることと、ファイルサイズが小さいことから、KaTeX を使うことにした。 Astro への KaTeX の導入方法について記しておく。
Astro に KaTeX を導入するには rehype-katex, remark-math を使う。
パッケージをインストールする。
npm install --save rehype-katex remark-math
設定ファイル astro.config.mjs を編集する。
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
export default defineConfig({
markdown: {
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
},
});
CSS については KaTeX を使うページに下記を含めておく。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css" integrity="sha384-Xi8rHCmBmhbuyyhbI88391ZKP2dmfnOl4rT9ZfRI7mLTdk1wblIUnrIq35nqwEvC" crossorigin="anonymous">
これで KaTeX を使ってレンダリングされるようになる。
unified, remark, rehype
ここまでのサンプルコードは下記のパターンになっている。
const content = await unified()
.use(remarkXXX)
.use(rehypeXXX)
.process(await read(inputFileName));
ここで出てくる unified, remark, rehype について整理しておく。
- unified: 各言語を AST として扱うライブラリ(e.g.
remark,rehype)を統合して使えるようにする - remark: Markdwon を AST として扱う
- rehype: HTML を AST として扱う
remark, rehype の他に、自然言語を扱う retext というものもある。
remarkXXX は Markdown の AST を加工するもの、
rehypeXXX は HTML の AST を加工するものである。
Astro のコード も
unified を使っている。
まとめ
rehype-katex と rehype-mathjax を使って数式をレンダリングしてみた。
rehype-katex は HTML と CSS で数式を表示し、
rehype-mathjax は SVG で数式を表示する。
また、生成された HTML のファイルサイズは rehype-katex により生成されたものの方が
rehype-mathjax により生成されたものより 30 % ほど小さい。
これらの比較に加え、Astro に KaTeX を導入する方法についても述べた。
あとがき
設定ファイルを少し編集するだけで数式を表示させられることを知り、Astro の便利さをさらに実感した。
Astro を使わずとも unified().use().process() で Markdown, HTML 間の変換ができるという
他でも使える知識を得られてよかった。
また、Computer Science がこういったところで役に立つことを知り、勉強になった。