做一个 React 项目的时候,想在网页上渲染数学公式,不想用别人封装好的 React 组件,采用动态加载的方式直接渲染 DOM。
Mathjax@3 做了很大的一个更新,使用方式也和 2.x 版本不同。
首先需要知道的一件事就是如何动态加载 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
});
先给出参考的官方链接,本文讲的可能不太清楚。
加载了 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
中即可。
同样给出官方文档链接:
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()
。