野声

Hey, 野声!

谁有天大力气可以拎着自己飞呀
twitter
github

React 项目中动态加载 Mathjax

做一个 React 项目的时候,想在网页上渲染数学公式,不想用别人封装好的 React 组件,采用动态加载的方式直接渲染 DOM。

Mathjax@3 做了很大的一个更新,使用方式也和 2.x 版本不同。

React 动态加载 js#

首先需要知道的一件事就是如何动态加载 js,用 js 将一个新的 script 节点挂载到 dom 上即可。

创建一个 dom 元素 script,设置 src 属性为要加载的链接,然后将节点添加到 body 元素内。

封装成函数如下,只需要传入需要加载的 js 链接,然后使用 .then 方法进行后续操作。

export const loadJS = (url: string) =>
  new Promise(function (resolve, reject) {
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;
    document.body.appendChild(script);
    script.onload = function () {
      resolve(`success: ${url}`);
    };
    script.onerror = function () {
      reject(Error(`${url} load error!`));
    };
  });

在需要使用的地方:

loadJS(url)
  .then(() => {
    // do something
  })
  .catch(() => {
    // do something
  });

[email protected] 版本#

先给出参考的官方链接,本文讲的可能不太清楚。

加载了 mathjax 之后,我们只要在合适的时机让 mathjax 渲染 dom 即可。

举个例子:

componentDidMount() {
  const mathjaxUrl = 'https://cdn.bootcss.com/mathjax/2.7.4/MathJax.js?config=TeX-AMS_CHTML';
  loadJS(mathjaxUrl).then(() => {
    this.showMathjax();
  });
}

componentDidUpdate () {
  if (!(window as any).MathJax) {
    (window as any).MathJax.Hub.Queue(['Typeset', (window as any).MathJax.Hub, ReactDOM.findDOMNode(this)]);
  }
}

showMathjax = () => {
  if ((window as any).MathJax) {
    (window as any).MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [['$', '$']],
        displayMath: [['$$', '$$']],
        skipTags: ['script', 'noscript', 'style', 'textarea', 'code', 'a'],
      },
      CommonHTML: {
        scale: 120,
        linebreaks: { automatic: true },
      },
      'HTML-CSS': { linebreaks: { automatic: true } },
      SVG: { linebreaks: { automatic: true } },
      TeX: { noErrors: { disabled: true } },
    });
  } else {
    setTimeout(this.showMathjax, 1000);
  }
};

2.x 的版本,我们要使用 Mathjax.Hub.Config 来配置,这几个配置看起来也很好懂,tex2jax 中设置了在什么字符包裹的时候渲染数学公式。

设置了行内公式用 $...$ 包裹,多行公式用 $$...$$ 来包裹,对于什么什么标签内的则不渲染。
然后就是设置好几种模式下渲染的表现怎么样,上面我们加载的 js 时候后面有一个 query ?config=TeX-AMS_CHTML,说明我们加载的是 CHTML 的配置,各种配置显示效果不同。参见 https://docs.mathjax.org/en/v2.7-latest/config-files.html

Mathjax 加载好之后就会渲染页面,但是对于单页面应用来说, Mathjax 并不会在页面 DOM 更新的时候重新渲染,我们需要使用 MathJax.Hub.Queue(['Typeset', MathJax.Hub, ReactDOM.findDOMNode(this)]); 来让 Mathjax 手动渲染 DOM。添加在 componentDidUpdate 中即可。

mathjax@3 版本#

同样给出官方文档链接:

3 的版本 Mathjax 的加载,配置方式都有不同。

Upgrading from v2 to v3: http://docs.mathjax.org/en/latest/upgrading/v2.html

加载时可以直接加载不同配置的 js,不需要再使用 ?config=xxx 了,比如说加载 https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js

Mathjax.Hub 方法被移除,现在设置配置只需要给 window.Mathjax 赋值一个配置对象即可。

官方还提供一个一键转换配置的链接: MathJax Configuration Converter

还是给出我的配置:

(window as any).MathJax = {
  tex: {
    inlineMath: [['$', '$']],
    displayMath: [['$$', '$$']],
  },
  options: {
    skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'code', 'a'],
  },
  chtml: {
    scale: 1.2,
  },
  startup: {
    ready: () => {
      (window as any).MathJax.startup.defaultReady();
      (window as any).MathJax.startup.promise.then(() => {
        console.log('MathJax initial typesetting complete');
      });
    },
  },
};

Mathjax 脚本加载好之后会读取 window.Mathjax 为配置并且替换为 Mathjax 对象,然后你就可以调用相关函数了。

Mathjax 初始化时会调用配置中的 startup.ready 方法,你可以在里面做一下提示或者其他配置。

还是那个问题,Mathjax 在 DOM 更新时不会重新渲染,需要使用 MathJax.typesetPromise() 方法。也在 componentDidUpdate 中设置即可。

componentDidUpdate() {
  const MathJax = (window as any).MathJax;
  if (MathJax) {
    MathJax.typesetPromise && MathJax.typesetPromise();
  }
}

这个方法是异步方法,还有一个相同功能的同步方法: MathJax.typeset()

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。