本文做者:傅云贵(网易有道技术团队)javascript
最近,在桌面 web 前端项目中使用了 MathJax 渲染数学公式,遇到一些坑,现在总结之。html
在 MathJax 官网 能看到如下的介绍:前端
A JavaScript display engine for mathematics that works in all browsers.java
No more setup for readers. It just works.es6
在 MathJax 的官方文档 What is MathJax? 中有如下文字:web
MathJax is an open-source JavaScript display engine for LaTeX and MathML that works in all modern browsers. ...ajax
MathJax uses web-based fonts (in those browsers that support it) to produce high-quality typesetting that scales and prints at full resolution (unlike mathematics included as images)....npm
MathJax is modular, so it loads components only when necessary, and can be extended to include new capabilities as needed. MathJax is highly configurable, allowing authors to customize it for the special requirements of their web sites. Finally, MathJax has a rich application programming interface (API) that can be used to make the mathematics on your web pages interactive and dynamic.浏览器
要点总结以下:markdown
正如 MathJax 官网 所说的 「 It just works.」同样, 通常使用 mathjax 很是简单:
If you write your own HTML (directly or via a template/theme engine), you can include MathJax by adding this snippet to your page:
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
复制代码
且提供了一个示例:
Here's a pre-populated example on jsbin you can re-use.
即只要在 html 引入 MathJax 种子文件,MathJax 会在 ready 后,会自动去渲染document.body 中的数学公式——MathJax 将该过程称为Startup Typeset。
在 Vue 中,组件在mounted/updated生命周期后,才完成组件 html 的渲染。 故可在组件mounted/updated生命周期后调用 MathJax 对组件 html 进行数学公式渲染——MathJax 将这一过程叫作Typeset。
使用代码表示以下:
@Component({})
class SomeComponent extends Vue {
private callMathJaxTypeset(): void {
// call window.MathJax to typeset
const { typesetElement } = this.$refs
MathJax.Hub.Queue(['Typeset', MathJax.Hub, typesetElement])
}
mounted(): void {
this.callMathJaxTypeset()
}
updated(): void {
this.callMathJaxTypeset()
}
}
复制代码
通常来讲,MathJax 种子文件不该当放在 html——即不一开始就加载,除非整个 webapp 都用到了 MathJax。
更为理想的情况是 MathJax 种子文件按需加载, 故实现一个加载 MathJax 的函数 loadMathJax():
const CDN_URL =
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?' +
'config=TeX-MML-AM_CHTML' +
'&delayStartupUntil=configured'
let isLoading = false
let isConfigured = false
function waitUntil(callback: () => void, failCallback: () => void): void {
// TODO
}
function isLoaded(): boolean {
if (window.MathJax) {
if (!isConfigured) {
isConfigured = true
window.MathJax.Hub.Config({
skipStartupTypeset: true,
messageStyle: 'none',
tex2jax: {
inlineMath: [
// for recommend
['$', '$'],
// the default config
['\\(', '\\)'],
],
},
})
window.MathJax.Hub.Configured()
}
if (window.MathJax.isReady) {
return true
}
}
return false
}
function loadScript(): void {
if (isLoaded() || isLoading) {
return
}
isLoading = true
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = CDN_URL
document.getElementsByTagName('head')[0].appendChild(script)
}
async function loadMathJax(): Promise<typeof MathJax> {
return new Promise((resolve, reject) => {
if (window.MathJax) {
resolve(window.MathJax)
return
}
waitUntil(
() => {
resolve(window.MathJax)
},
() => {
reject()
},
)
loadScript()
})
}
export { loadMathJax }
复制代码
在loadMathJax() 的实现中,有如下几点须要注意:
为了在 Vue 组件中按需加载与使用 MathJax,可在组件的created 生命周期中加载 MathJax:
@Component({})
class SomeComponent extends Vue {
private mathJax: typeof MathJax | null = null
private needTypeset: boolean = false
private callMathJaxTypeset(): void {
const { mathJax } = this
if (mathJax) {
const { typesetElement } = this.$refs
mathJax.Hub.Queue(['Typeset', MathJax.Hub, typesetElement])
} else {
this.needTypeset = true
}
}
created(): void {
const mathJax = await loadMathJax()
this.mathJax = mathJax
if (this.needTypeset) {
this.callMathJaxTypeset()
}
}
mounted(): void {
this.callMathJaxTypeset()
}
updated(): void {
this.callMathJaxTypeset()
}
}
复制代码
此时,能够在 Vue 组件使用 MathJax 渲染数学公式,知足单页面应用中显示数学公式的应用场景。
最近的产品中有一个需求
待 MathJax 渲染(Typeset)数学公式后,用户使用浏览器的打印功能打印网页。
在此需求中,须要判断全部组件实例的 MathJax Typeset 是否完成。
如何监听全部组件实例中的 MathJax Typeset 是否完成呢?且听下回分解。
网易技术热爱者队伍持续招募队友中!网易有道,与你同道,由于热爱因此选择, 期待志同道合的你加入咱们,简历可发送至邮箱:bjfanyudan@corp.netease.com