使用 KaTeX 在 node.js 模板中渲染数学

Render math in node.js template with KaTeX

我想在 node.js 中使用乳胶模式在页面中呈现数学。我看过 MathJaXKaTeX

我用

呈现我的页面
router.get('/math', function (req, res) {
  res.render('math');
});

那么我如何确保此页面上的数学呈现为数学?

我可以用

const katex = require('katex');
const math = katex.renderToString("c = \pm\sqrt{a^2 + b^2}", { displayMode: true });

然后在模板中设置变量

router.get('/math', function (req, res) {
  res.render('math', { math: math });
});

但我宁愿直接在模板中编写所有数学运算,而不是在 javascript 代码中专门设置每个变量。

编辑

我正在使用

从模板中获取 html
router.get('/math', function (req, res) {
  res.render('math', function (err, html) {
    html = html.replace(/$$(.*?)$$/g, function (outer, inner) {
      return katex.renderToString(inner, { displayMode: true });
    });

    res.send(html);
  });
});

这是一个好方法吗?或者我可以在使用 res.send() 之前省略调用 res.render() 吗?

当我使用

html = html.replace(/$$(.*?)$$/g, function (outer, inner) {
  return katex.renderToString(inner, { displayMode: true });
}).replace(/$(.*?)$/g, function (outer, inner) {
  return katex.renderToString(inner);
});

服务器出现故障,我收到错误 ParseError: KaTeX parse error: Expected 'EOF', got '$' at position 1: $_

KaTeX 核心不关心文本输入的来源。识别 TeX 源代码片段不是其 objective 的一部分。有一个名为 auto-render 的贡献扩展,它作为 KaTeX 代码库的一部分进行维护。它将识别页面中的 TeX 输入,并将其替换为 KaTeX 渲染 HTML。但它在 DOM 树上运行客户端,而不是 HTML 标记文本上的服务器端。

所以我建议你在这里推出自己的代码。我想您不需要任何 DOM 解析。相反,我会尝试提出一些合适的正则表达式来描述数学块,然后用它们的 renderToString 类比替换它们。像

html = html.replace(/$$(.*?)$$/g, function(outer, inner) {
    return katex.renderToString(inner, { displayMode: true });
}).replace(/\\[(.*?)\\]/g, function(outer, innner) {
    return katex.renderToString(inner, { displayMode: true });
}).replace(/\\((.*?)\\)/g, function(outer, innner) {
    return katex.renderToString(inner, { displayMode: false });
});

根据您的用例,您可能希望将此替换应用于您的输入模板、您提供给模板的参数,或者您从渲染模板获得的结果。在所有这三种情况下,您应该尝试在某个时候将 HTML 文本的相关部分作为单个字符串。在某些情况下,这可能涉及缓冲基于流的模板输出。由于您没有说明模板和应用服务器使用的是什么框架,因此我无法提供更多详细信息。

请注意,上面给出的 TeX 优先级高于 HTML:像 $$a<p>b$$ 这样的输入被解释为 TeX 输入 a < p > b。这与客户端渲染(如自动渲染器)形成对比,在客户端渲染中,上面的内容将被视为两个段落,它们都不包含完整的 TeX 输入片段,并且在哪里实现 a < p > b 渲染一个会必须将 < 编码为 &lt;。如果您控制所有的输入,那么给 TeX 优先权可能是您想要的。但是,如果您接受用户提供的输入,那么此行为可能会导致某些内容清理程序或 Wiki 标记格式代码出现意外。因此,如果您打算按照这些思路做任何事情,请确保您知道自己想要什么行为,并让您的客户意识到这一点。

如果你想与 TeX 有更高的兼容性,你可以尝试支持额外的顶级环境。例如,您可以包括

html = html.replace(/\begin\{align\*\}(.*?)\end\{align\*\}/g, function(outer, inner) {
    return katex.renderToString("\begin{aligned}" + inner + "\end{aligned}", { displayMode: true });
})

利用 aligned 环境已实施而 align* 环境尚未实施的事实。


回应您的编辑:

res.render 与回调和回调中的 res.send 的组合对我来说很好。如果您自己调用模板呈现,则可以避免调用 res.render,但是否需要由您决定。

在不知道输入的情况下很难确定错误消息的原因。似乎您可能有一些以双 $$ 开头但仅以单个 $ 结尾的输入,因此第一个正则表达式无法匹配,第二个包含一个额外的 $它匹配的字符串。

/\\((.*?)\\)/g

在许多情况下不起作用,如以下情况:

多个公式被识别为一个,导致错误。

要防止这种情况,请使用

/\\(((.)*?(?=\\)))?\\)/

示例

html = html.replace(/\\(((.)*?(?=\\)))?\\)/g, function(a, b) {
  return katex.renderToString(b, { displayMode: false });
}).replace(/\\[((.)*?(?=\\]))?\\]/g, function(a, b) {
  return katex.renderToString(b, { displayMode: true });
}) ...

但是,仍有一些注意事项,例如未正确呈现的内容。另一种方法可能是 https://joa.sh/posts/2015-09-14-prerender-mathjax.html(这是我要尝试的下一个方法......)