本文做者:傅雲貴(網易有道技術團隊)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