Mathjax is an open source javascript display engine for latex, MathML, and Ascimath notation. Version 3.0 of MathJax is a complete rewrite of MathJax, enabling componentization and customization for different needs. Usage and configuration are very different from the MathJAX2 version, so be aware of the version.

Recently, when refactoring a project, a requirement was added to support latex mathematical formula rendering and editing. After some research and comparison, there are two browsers with good compatibility, namely KateX and MathJax.

Performance Comparison Screenshot

MathJax3

MathJax2.7

KaTex

As you can see from the comparison, Both MathJax versions 2 and 3 use Tex-CHTML, but the performance gap is quite large. The rendering performance of Katex will be a little better than MathJax3, but the rendering performance of complex formulas will not be as good as MathJax, and the actual use of Katex will not make much difference. MathJax3 was chosen, largely due to the fact that it was already in use in a sister department with project dependencies.

Use in Vue

General contact with some new knowledge points, the author will first try to learn from the use of experience, github look at open source projects.This may not be very kind to JustForuse, but thank you for the idea. So before I start, it should be emphasized that all open source projects and authors’ efforts and achievements should be respected. The rest of the article is just a statement of what I saw and why I made the wheel.

The use of the vue – mathjaxMain code:

export default {
  / /...
  watch: {
    formula () {
      this.renderMathJax()
    }
  },
  mounted () {
    this.renderMathJax()
  },
  methods: {
    renderContent () {
      if (this.safe) {
        this.$refs.mathJaxEl.textContent = this.formula
      } else {
        this.$refs.mathJaxEl.innerHTML = this.formula
      }
    },
    renderMathJax () {
      this.renderContent()
      if (window.MathJax) {
        window.MathJax.Hub.Config({
          // ...
        })
        window.MathJax.Hub.Queue([
          'Typeset'.// ...])}}}}Copy the code

After looking at the whole project, several questions arise 🤔️

  1. What if I want to use it on demand?
  2. How much overhead does mathJax default to rendering the entire Document. body once it’s loaded? Will it lead to unnecessary errors in rendering?
  3. Every time the component rendering execution window. It is MathJax. Hub. The Config?
  4. When there are many formulas in a text, it is too cumbersome and ugly to transform each into a component.
  5. Performance gap between MathJAX2 and version 3.

In general, this project does not meet my needs, especially for large pages. Questions 2 and 3 will definitely bring performance problems. Personally, I guess that the starting point of question 3 is to make each component support different configurations, but the key point is that the author did not deal with the code well, burying performance problems.

Start building the wheel

According to the need to introduce

// Mathjax to be injected into the document head
export function injectMathJax() {
  if (!window.MathJax) {
    const script = document.createElement('script')
    script.src =
      'https://cdn.bootcdn.net/ajax/libs/mathjax/3.2.0/es5/tex-chtml.js'
    script.async = true
    document.head.appendChild(script)
  }
}
Copy the code

Load and configure MathJax Refer to: MathJax

/** * Initialize MathJax *@param Callback Mathjax callback */ after loading
export function initMathJax(callback) {
  injectMathJax()
  window.MathJax = {
    tex: {
      // The inline formula flag
      inlineMath: [['$'.'$']],
      // Block level formula flag
      displayMath: [['$$'.'$$']],
      // The following two main render support some formulas, you can understand
      processEnvironments: true.processRefs: true,},options: {
      // Skip rendered tags
      skipHtmlTags: ['noscript'.'style'.'textarea'.'pre'.'code']./ / skip mathjax processing elements in the name of the class, any element specifies a class tex2jax_ignore will be skipped, more tired = class name 'class1 | class2'
      ignoreHtmlClass: 'tex2jax_ignore',},startup: {
      // Callback when MathJax is loaded and initialized
      pageReady: () = > {
        callback && callback()
      },
    },
    svg: {
      fontCache: 'global',}}}Copy the code

Note: The pageReady function is best configured by itself for performance reasons. If it is not configured, the default automatic rendering function will be executed and the entire document.body will be rendered, resulting in unnecessary performance overhead (problem 2).

Render a Dom element or collection manually without transforming it into a component (Q4)

/** * The manual render formula returns Promise *@param El The DOM element or collection to render *@returns Promise* /
export function renderByMathjax(el) {
  // Version will not be injected until mathJax is initialized
  if (!window.MathJax.version) {
    return
  }

  if (el && !Array.isArray(el)) {
    el = [el]
  }

  return new Promise((resolve, reject) = > {
    window.MathJax.typesetPromise(el)
      .then(() = > {
        resolve(void 0)
      })
      .catch((err) = > reject(err))
  })
}
Copy the code

Note: these are only used to fit inside the vue, preach and can be converted into a selector, and then use the document renderByMathjax it. QuerySelectorAll, do not need to judge array, calls the concise convenient also.

Common usage

function onMathJaxReady() {
  // Render according to id
  const el = document.getElementById('elementId')
  renderByMathjax(el)
}

initMathJax(onMathJaxReady)
// Render according to class
renderByMathjax(document.getElementByClassNAme('class1'))
Copy the code

So far, support has been introduced in various front-end projects, some of which may need to be modified, such as the introduction of HTML does not support ES6 syntax.

Vue components (no problem 3)

<template>
  <span></span>
</template>

<script>
import { renderByMathjax } from '.. /utils'

export default {
  name: 'MathJax'.props: {
    latex: {  / / latex formula
      type: String.default: ' ',},block: { // Use block level formulas
      type: Boolean.default: false,}},watch: {
    latex() {
      this.renderMathJax()
    },
  },
  mounted() {
    this.renderMathJax()
  },
  methods: {
    renderMathJax() {
      this.$el.innerText = this.block
        ? ` $$The ${this.latex} $$`
        : ` $The ${this.latex} $`
      renderByMathjax(this.$el)
    },
  },
}
</script>
Copy the code

Based on the example code above, THE author released the MathJAX-Vue and MathJAX-vue3 plug-ins.

The usage mathjax – vue

The installation

// npm
npm i --save mathjax-vue

// yarn
yarn add mathjax-vue

// change mathJAX-vue to mathJAX-vue3
Copy the code

Global registration

import MathJax, { initMathJax, renderByMathjax } from 'mathjax-vue'

function onMathJaxReady() {
  const el = document.getElementById('elementId')
  renderByMathjax(el)
}

initMathJax({}, onMathJaxReady)

// vue 2
Vue.use(MathJax)

// vue3
createApp(App).use(MathJax)
Copy the code

Private components

import { MathJax } from 'mathjax-vue'
// initMathJax must be executed first
export default{...components: {
    MathJax,
  },
  ...
}
Copy the code

Do not want to insert components

// initMathJax must be executed first
import { renderByMathjax } from 'mathjax-vue'

renderByMathjax(document.getElementById('id1'))
Copy the code

Finally, as an aside, the author is preparing to open source a mathematical formula editor recently. The main thing is that the current open source mathematical formula editor cannot meet business needs, so if you need it, you can pay attention to it. If this article has helped you, give it a thumbs up

CodeSandbox Project repository: Github