鲸鱼的树洞

在 Astro 中显示数学公式

发布于

编辑于


精简步骤

在这个时间点(2023 年 10 月),你大概率会遇到多行公式渲染报错的 bug,可见 remark-math throws an error on multiline math。你可以在这个 issue 中找到解决方案,也可以翻到这篇文章的最后。

现在(2023 年 12 月),Astro 4.0 已经发布,上述 bug 已经被修复,你可以直接使用最新版本的 remark-mathrehype-katex

安装依赖

npm install remark-math rehype-katex katex

编辑配置

---
...
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
...
---
export default defineConfig({
...
  markdown: {
    remarkPlugins: ["remarkMath"],
    rehypePlugins: ["rehypeKatex"]
  },
...
});

编辑模板

---
import "/node_modules/katex/dist/katex.min.css";
---

如果你已经看懂了,那你可以关闭这个网页了;如果你没看懂,请你继续往下看,大致原理和详细步骤。

大致原理

Astro 处理 Markdown 的程序

我并不了解 Astro 的工作过程,那就问问 ChatGPT 吧(以下回答由 ChatGPT 3.5 生成,经过我修饰):

Astro 使用 remarkrehype 来将 Markdown 文件转换为 HTML。这两个工具协同工作,处理和转换文档内容。以下是一个 Markdown 文件转换为 HTML 时经历的主要步骤:

  1. 解析 Markdown:
  1. 操作抽象语法树:
  1. 转换为 HTML 抽象语法树:
  1. 操作 HTML 抽象语法树:
  1. 生成 HTML:

显示数学公式的原理

为了在 Astro 中显示数学公式,我们会使用 remark-math 将 Markdown 中的数学公式转换为一个中间结构,再使用 rehype-katex 将中间结构转换为 HTML。

详细步骤

我们都用 Astro 这样新的框架了,那么就不用纠结 MathJax 与 KaTeX 了——肯定不会考虑 MathJax 这种老东西。

安装相关依赖

这一步没什么好说的,直接在命令行中运行这一行命令,安装 remark-math rehype-katex katex 即可。

npm install remark-math rehype-katex katex

编辑 Astro 配置

现在,插件已经安装好了,但是 Astro 并不会去用它们。编辑 Astro 的配置文件 astro.config.mjs,告诉 Astro 应该使用这些插件,两个省略号之间的是要添加的内容:

---
...
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
...
---
export default defineConfig({
...
  markdown: {
    remarkPlugins: ["remarkMath"],
    rehypePlugins: ["rehypeKatex"]
  },
...
});

也许你的 Astro 文件并不是 astro.config.mjs,而是 astro.config.js,那么只需要这样添加:

---
...
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
...
---
module.exports = {
...
  markdown: {
    remarkPlugins: ["remarkMath"],
    rehypePlugins: ["rehypeKatex"]
  },
};
...

astro.config.ts

---
...
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
...
---
export default {
...
  markdown: {
    remarkPlugins: ["remarkMath"],
    rehypePlugins: ["rehypeKatex"]
  },
...
};

astro.config.json

{
...
  "markdown": {
    "remarkPlugins": ["remark-math"],
    "rehypePlugins": ["rehype-katex"]
  },
...
}

在完成以上步骤后,你可以运行 npm run dev 来看看现在是什么样的。在你的网站中找一篇带有数学公式的网页,你会发现公式看起来很奇怪;查看页面源代码。你会发现数学公式都被 <span> 包裹了起来。这说明 Astro 已经能够正确处理数学公式了,但是浏览器并没有内置 KaTeX,它只能和渲染正常的字符一样渲染处理过后的数学公式。

编辑页面模板

为了告诉浏览器如何该如何渲染数学公式,我们需要引入 katex.csskatex.min.css,这里以 katex.min.css 为例,不过使用 katex.css 也是没有问题的,因为 Astro 会自动压缩 CSS(仅限于本地托管,不适用于 jsDelivr 第三方托管的方法)。

本地托管

如果你和我一样使用了 Astro 的 Blog 模板,那你可以在打开 src/components/BaseHead.astro,在 Frontmatter(也就是文件顶部用“---”包裹起来的部分)中添加一行:

---
...
import "/node_modules/katex/dist/katex.min.css";
...
---

这行代码不一定要添加到 BaseHead.astro 的 Frontmatter 中,理论上任意一个 .astro 文件都行,只要这个 .astro 文件参与到了含有数学公式的网页的生成即可。例如 src/layouts/BlogPost.astro 等。这种方法是最简便的,Astro 会自动处理 CSS 和字体文件。

还有一种方式也可以在本地托管 KaTeX:

<link rel="stylesheet" href="/node_modules/katex/dist/katex.min.css">

Astro 同样会自动处理 CSS 和字体文件。不过这种方式意义不大,不如第一种方便,就不详细讲了,步骤和下面 👇 使用 jdDelivr 的那个方法一样,有需要的可以参考。

当然还有一种更加鸡肋的本地托管方法,大致就是将 CSS 和字体文件都复制到 public 文件夹下,然后手动引入 katex.min.css。这种方法默认情况下会找不到字体,还要手动修改 katex.min.css 中的字体链接。由于实在鸡肋,就不展开了。有需要的可以自己去试试。

第三方(jdDelivr)托管

如果你不希望自己托管 CSS 和字体文件,也可以使用 jdDelivr。只需要将以下代码复制到负责生成 <head> 部分的 .astro 文件中,例如 BaseHead.astro。注意这段代码应复制到正文部分,而不是 Frontmatter。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" integrity="sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV" crossorigin="anonymous">

完成!

使用 npm run dev 命令运行本地服务器,然后在浏览器中打开 localhost:4321。使用你喜欢的编辑器打开你的一篇博客,然后添加以下内容:

$$E = MC^2$$

回到浏览器,你就可以看到

E=MC2E = MC^2

了!

也许你遇到了一些小问题…

现在(2023 年 12 月),这个问题已经被修复,你可以忽略以下的步骤。


你可能习惯在公式块中换行:

$$
E = MC^2
$$

保存之后回到浏览器,就会发现 报!错!啦! 满屏密密麻麻的文字,头都要大了!

总之,如果你安装了最新的 remark-mathrehype-katex,并且在 $$ … $$ 中使用了换行,就会报错。似乎官方人员对于这个 bug 也做不了什么,直接把相关的 issue 给 close 了。

但是,要(暂时)解决这个问题也很简单!打开 package.json,找到:

{
  "rehype-katex": "^7.0.0",
  "remark-math": "^6.0.0"
}

把它修改成:

{
  "rehype-katex": "^6.0.3",
  "remark-math": "^5.1.1"
}

然后运行一下 npm install,就好了!这个问题困扰了我很久,希望它没有对你造成什么麻烦~