基于vue渲染Latex数学公式(simplemde-editor)
如意同学 2020/3/20 11:01:29
网络关于vue + mathjax的中文教程和博客都弱爆了,基本都是2年前MathJax 2.7.5版本的使用反复在被炒冷饭,甚至在2020年2月份的文章中还有人在引用。许久不写前端文章的我忍不出山了。
说句题外话,在我当年想要入门前端的时候,互联网上的学习总结一抓一大把,各种项目坑都可以找到,而且还都有模有样。当我现在想要入门深度学习算法的时候,却发现学习资源,尤其是优质的中文资源就没那么好找了。曾经跟朋友聊过这个话题,当时朋友开玩笑说,前端工程师都太闲了,日常解决个大bug都可以去写总结篇文章。在掘金其实也可以发现这一点,前端社区非常活跃,深度学习领域的板块就显得相对冷清。
活跃当然是好事,只不过还是希望能够有更多真正原创内容的产出,从而能帮到更多的人吧。
本文从实际项目出发,从一开始问题都不知道怎么定义,基本一窍不通,到现在基本解决了实际中遇到的问题,梳理了整个开发思路。
本文提到的Mathjax版本是3.0.0
项目需求
需求很简单,需要让我们的markdown编辑器支持数学公式latex的写法,在发表后公式也可以正常显示。
例如,把$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
给识别成公式
(掘金编辑器也支持了latex~,点赞)
寻找开发思路,锁定目标
在google上初步的筛查,锁定了两个插件:katex
和MathJax
。他们都是渲染公式的工具,看demo是都可以做到输入latex,输出html,展示为数学公式。
mathJax是公式渲染的鼻祖了,历史悠久,但依然在持续更新。不过好多跳转链接都失效了
而katex后来者居上,star更多,官网更美观,而且也展示了katex和mathjax的渲染速度对比
所以一开始我就选择了katex进行开发。
开发探索阶段
1. katex与simplemde的纠缠
我目前使用的编辑器是simplemde
,katex的英文官网给出的实例写法是这样
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}", { throwOnError: false }); 复制代码
非常简洁,也没有更多的demo。我的思路是,把markdown中的公式找出来,用这个方法转化一下再加到正常的渲染结果里去,应该就可以了吧?
simplemde官网给出了一个方法previewRender
,可以用于在预览生效前自定义一些渲染效果。

这样一来,问题就变成了,把公式从markdown中找出来,再把公式塞回html里去
。
我主要是想通过正则匹配去做这件事情,查资料查了不少时间,就是匹配不出来,更别说塞回去了(我讨厌正则)。
中间去吃了个晚饭,中断了一下前进的脚步,回来后鬼使神差打开了MathJax的官网...
这一看,就发现了神奇的大陆,在demo中,mathJax只要引入全局资源和全局配置,就能自动把全站的公式都识别出来,这样不管是在预览区还是发表之后,就一步到位全部识别了。果然姜还是老的辣。
2. 转而探索MathJax
深入地看了MathJax的Gihub和官方文档之后,了解到mathJax其实是有两种识别公式的方式。
- 第一种就是我刚刚说的全局配置,配置好之后,脚本会在网页中寻找符合条件的公式符号(比如被包裹在
$
中的内容),并把它编译。
点击查看demo效果以及对应html代码
- 第二种是将用户在表单中输入的公式,用事件触发的方式,比如点击按钮,再去编译输入框中的内容,返回html(如知乎的形式)
点击查看demo效果以及对应html代码
尤其第一种,看上去非常简单省心。
引入资源(我为了防止国外镜像不稳定,把资源下载到了本地),全局配置,几分钟就搞定了。然后打开页面。emmm...没效果。由于太简单了,甚至都不知道问题出在哪里。。
写了个静态页面,同样的配方,是有效果的,看来问题是出在vue上啊。初步猜测,是因为vue的数据都是渲染上去的,所以MathJax脚本加载的时候,页面还没有内容。等内容有了之后,脚本早执行完了,所以导致没有效果。不过也是瞎猜的,google了其他人有没有遇到同样的问题,就遇到了开头我所说的,网上蜜汁全是2.7.5版本的实现方式,有一个window.MathJax.Hub.Queue(["Typeset", MathJax.Hub])
方法好像是实现了“重载脚本”的效果。然而,我现在3.0.0版本,这个方法已经undefined了....
再次陷入僵局。
期间尝试过很多其他思路,当然现在看来都是在走弯路了。
- 想在编辑框加一个按钮,退而求其次,用输入框的方式去生成公式。但是公式编译后的html又长又臭,就这样插入markdown显然不好。
- 尝试刚刚提到的mathJax的第二种方法,把vue渲染的数据看作是表单内容,仿照demo,直接把整个markdown数据扔进
MathJax.tex2chtmlPromise()
函数里。这个方法在内容中只有公式的时候表现优异,还让我激动了一把。结果一加入其他dom元素就歇菜,整个dom被重组,样式也全部丢失。
3. 文档还是硬道理
google无果,能想到的方法也都试了,最终还是回归文档,看看有什么被我忽略的方法。事实证明,只有文档才能解决一切问题,我看到了一个Handling Asynchronous(异步处理)的方法MathJax.typesetPromise()
。

在数据渲染完成的地方,加上这个方法之后,世界就变得美好了...
- 在内容展示页,只需要在数据返回后加上这个方法,为保证脚本在页面渲染完成后再执行,可以稍作延迟:
ajax.get(xxx).then(res => { // 页面渲染 settimeout(res => { MathJax.typesetPromise() }, 500) }) 复制代码
在markdown编辑区,在预览渲染后,执行渲染脚本,这里就用到了开始提到的simplemde的自定义渲染方法previewRender
,同样的,为保证脚本在页面渲染完成后再执行,可以稍作延迟。其他编辑器,只要找到类似的方法也都可以这么处理:
this.simplemde = new SimpleMDE({ { //...其他配置项 previewRender: (plainText) => { settimeout(res => { MathJax.typesetPromise() }, 500) return this.simplemde.markdown(plainText) } }, element: document.getElementById('simplemde') }) 复制代码
想来,这个方法应该就是在废弃2.x版本window.MathJax.Hub.Queue
之后的替代方法吧。随后,果然如此,发现了这一页,官网也有非常详细的说明。(为什么不早发现呢。T_T)

之后还处理了行内公式的识别,MathJax也出了自定义的识别方法,更多的config也都能在官网上找到。
window.MathJax = { startup: { pageReady: () => { // alert(111) // 检验脚本首次是否加载成功 return window.MathJax.startup.defaultPageReady() } }, tags: 'all', // 为方程式编号 tagSide: 'left', // 方程式编号的位置 tex: { processEscapes: true, processEnvironments: true, // process \begin{xxx}...\end{xxx} outside math mode processRefs: true, // process \ref{...} outside of math mode inlineMath: [ ['$', '$'], ['\\(', '\\)'] ], displayMath: [ // start/end delimiter pairs for display math ['$$', '$$'], ['\\[', '\\]'] ] } 复制代码
4. 对MathJax探索的总结与梳理
这整个探索流程花费了两个半天的时间。个人对于MathJax的整体机制
也随着探索的过程逐渐有了更深的了解。我觉得对于后来者,更重要的应该是解决问题的思路,而不是照搬方法。所以想在这里根据我的理解,简单解释一下mathjax的机制。
上文也有提到,对于MathJax来说,有两种渲染公式的方式。我不厌其烦地再唠叨一遍:
第一种,就是我刚刚说的全局配置。首先引入CND资源,并设置好全局配置。等页面渲染好后,脚本开始执行,在网页中寻找符合条件的公式符号(比如被包裹在$
中的内容),并把它编译。
你仔细看就会发现,官网的示例都给CDN加上了async
,同步加载,也就是说,脚本会等页面内容都加载完成后,再执行,从而保证公式正常渲染。
但是,在像vue这些数据驱动的MVC框架下,外部资源要想对dom进行渲染,那真是太天真了,async
肯定无效,这时候就需要“重新执行脚本”。在3.x版本中,也就是MathJax.tex2chtmlPromise()
方法。
第二种,是比较好理解的,和katex给出的示例类似。是将指定的公式内容,全部转成公式格式。通常可以用于用户在表单中输入,用事件触发的方式,比如点击按钮,再去编译输入框中的内容,返回html。
相比mathjax,Katex我是只是初步进行了探索,很有可能我没有注意到,说不定它的第一种实现也是有。毕竟它官网列举了辣么多的优点
结语
katex,mathjax,还有simplemed,全都没有中文文档。可能开发过程中如果遇到坑了,很多人第一反应都不是翻文档(我自己也是),而是去搜索有没有人遇到类似的问题,甚至直接去github上去提issue。
而这次经历给我的经验是,适可而止的google,答案都在文档里。

关于找一找教程网
本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[基于vue渲染Latex数学公式(simplemde-editor)]http://www.zyiz.net/tech/detail-120058.html
- 2023-03-22接口思维:如何使用 Context API 构建灵活、可维护的 React 组件-icode9专业技术文章分享
- 2023-03-22掌握 ReactJS Hooks:现代 Web 开发综合指南-icode9专业技术文章分享
- 2023-03-12热点面试题:Vue2、3 生命周期及作用?
- 2023-03-06【备战春招】第19天 新版 Node.js+Express+Koa2 开发Web Server博客 10-13
- 2023-02-14【备战春招】第6天 6-14 Vue3如何实现响应式
- 2023-02-14【备战春招】第六天+vue复习
- 2023-02-14【备战春招】第6天 新版 Node.js+Express+Koa2 开发Web Server博客 8-8~8-9
- 2023-02-13【老卫拆书】009期:Vue+Node肩挑全栈!《Node.js+Express+MongoDB+Vue.js全栈开发实战》开箱
- 2023-02-13【备战春招】第5天 React零基础入门到实战,完成企业级项目简书网站开发——React基础篇
- 2023-02-13【备战春招】第5天 新版 Node.js+Express+Koa2 开发Web Server博客 8-6