野声

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()

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。