Vue 中 MathJax 的使用與渲染的監聽 (上)

在這裏插入圖片描述

本文做者:傅雲貴(網易有道技術團隊)javascript

引言

最近,在桌面 web 前端項目中使用了 MathJax 渲染數學公式,遇到一些坑,現在總結之。html

MathJax 是什麼

在 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 是開源的高質量的數學公式渲染引擎
  • 支持現代瀏覽器
  • 模塊化,按需加載
    • 高配置性
    • 接口豐富
    • 總文件很是大,只能按需加載

通常使用

正如 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 中使用

在 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 加載

通常來講,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() 的實現中,有如下幾點須要注意:

  1. 跳過Startup Typeset
  • MathJax cdn url 中必須加上 &delayStartupUntil=configured
  • 調用window.MathJax.Hub.Config 時, skipStartupTypeset 設置爲true
  • 調用 window.MathJax.Hub.Configured()
  1. 使用 window.MathJax.isReady 判斷 MathJax 是否可用

Vue 組件加載與使用 MathJax

爲了在 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

相關文章
相關標籤/搜索