[聚合文章] Angular 上用 MathJax 的一些问题

Angular 2017-12-14 21 阅读
Angular 上用 MathJax 的一些问题

Angular 上用 MathJax 的一些问题

AxurezAxurez

首发于 Eliseos:我是如何解决 Angular 上用 MathJax 的一些问题的 - Eliseos


话说我本来是倾向于 KaTeX 的,因为我感觉他很快,而且 MathJax 似乎很难配。但是大家表示对缺少功能的 KaTeX 并无好感,给我提供了一些钻研 MathJax 的动力。

其实也不算钻研,因为实际上 MathJax 很简单,调用 MathJax.Hub.Queue(['Typeset', MathJax.Hub, this.element.nativeElement]); 就可以渲染一个元素(这个 this.element.nativeElement 是从 Angular 中调用它 DOM 的语法),这个 .Queue 实际上是 MathJax 自己实现的回调格式,语法非常清奇,参数个数不定,每个都是数组,代表一个回调,顺序执行。比如这个 ['Typeset', MathJax.Hub, this.element.nativeElement],第一个元素是方法名,第二个元素是 this,之后的元素都是参数……

我们可以看到这个就相当于执行 MathJax.Hub.Typeset(this.element.nativeElement),那为啥不执行这个?因为这方法是同步的,会导致页面十分卡。于是 MathJax 就自己封装了一个异步队列(它的 API 可能几百年没改了)

我们说回 Angular。因为要用 markdown,我的思路是用 marked 封装一个 directive。那么我们就应该在 marked 渲染完成之后用 MathJax 去 Typeset 这个组件。但真的这样做了,却产生了奇妙的效果——切换页面之后,要等将近一分钟才开始渲染。我在它的队列里放了几个 log,发现每个元素都被 queue 了 4 次,几十个元素,难怪要一分钟才开始渲染下一页的内容,即使大部分 markdown 里面根本没有数学。

这时候我开始灰心了,这个问题就没有解决办法了吗?绝望之时,我想到能不能直接 Typeset document,结果是可以的,而且十分快。所以渲染并不慢,可能是渲染的初始化过程比较慢。那么这时候方案就出来了,我们可以尽量减少渲染次数,同时只渲染 document。只要这个渲染还在进行,那么有再多的元素 queue 上来,我们也只当作 queue 了一次。

于是我就写了这么个 service:

@Injectable()export class MathjaxService {  public isQueued = false;  public isRunning = false;  window: any;  constructor(@Inject(PLATFORM_ID) private platformId: Object) {    if (isPlatformBrowser(this.platformId)) {      this.window = window as any;    }  }  finishRunning() {    this.isRunning = false;    if (this.isQueued) {      this.queueChange();    }  }  queueChange() {    if (this.isRunning) {      this.isQueued = true;    } else {      this.isQueued = false;      this.isRunning = true;      if (isPlatformBrowser(this.platformId)) {        if (this.window.MathJax) {          this.window.MathJax.Hub.Config({            messageStyle: 'none',            tex2jax: {              // preview: 'none',              inlineMath: [['$', '$']],              processEscapes: true            }          });          this.window.MathJax.Hub.Queue(['log', console, 'start'], ['Typeset', this.window.MathJax.Hub, document], ['log', console, 'end'], ['finishRunning', this]);        }      } else {        this.finishRunning();      }    }  }}

事实证明,它能圆满完成任务,它也就是现在运行在这个网站上的代码。