latex公式渲染及利用客户端做公式资源的清洗
背景
历史背景
- 最近接手的项目中,有大量的H5渲染
latex公式
的需求 - 由于历史原因,我们的
latex公式
资源都是针对mathjax
公式渲染引擎定制化制作的
存在的问题
我们存在的问题
- 由于历史原因,我们的资源大部分都是针对
mathjax
制作的,无法直接用katex
进行渲染。而mathjax
在pc端使用时,效果、速度勉强还能接受,但在移动端却差强人意,渲染速度慢,公式闪烁等问题严重。
社区内的渲染方案
- 要实现
latex公式
的渲染,社区内主流的方案有两种:- 一,使用
katex渲染引擎
来渲染。- 速度快,渲染效果好
- 对数据格式有要求,部分字符无法渲染
- 二,使用
mathjax
来渲染- 兼容性、容错性好
- 速度较慢
- 一,使用
解决方案(方案1)
思路
- 优先使用性能高的
katex
进行渲染,在katex
无法渲染时,再切换到mathjax
实现
- 资源处理
- 我们针对服务端返回的资源做了一些特殊处理。例如使用正则对资源进行规范化,让其能被
katex
渲染。同时,剔除了一些无法识别的特殊字符。
- 我们针对服务端返回的资源做了一些特殊处理。例如使用正则对资源进行规范化,让其能被
- 出错降级处理
katex
无法渲染时,会抛出错误,我们捕获了错误,再利用mathjax
进行渲染。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19try {
return this.htmlString.replace(/\\\(.*?\\\)/g, function(html) {
// * 剔除异常字符
html = html
.replace(/</g, '<')
.replace(/>/g, '>')
return (
' ' +
katex.renderToString(html, {
throwOnError: false,
unicodeTextInMathMode: true
}) +
' '
)
})
} catch (err) {
// * 解析错误时,降级使用MathJax
MathJax.Hub.Queue(['Typeset', MathJax.Hub, el])
}
- 经过上述操作,所有公式已经可以渲染出来了,但仍存在一些问题。
问题
- 虽然公式都能渲染出来了,但仍有问题
- 因为没有完备的特殊字符替换表,导致仍有部分特殊字符无法被
katex
渲染- 降级使用
mathjax
时,能明显看到公式的渲染过程 - 题量过大时,由于一次性需要渲染的公式较多,无论是使用
katex
还是mathjax
在渲染时都会明显感觉到页面的卡顿 - 题量大时,列表滚动卡顿
- 降级使用
- 因为没有完备的特殊字符替换表,导致仍有部分特殊字符无法被
方案2
思路
- 针对问题1
- 提供一个错误上报机制,收集
katex
无法渲染的字符
- 提供一个错误上报机制,收集
- 针对问题2
- 既然使用
mathjax
速度慢,那干脆不用mathjax
全使用katex
;- 需要对资源进行清洗(规范化、剔除特殊字符)
- 需要依赖报错收集机制
- 既然使用
- 针对问题3
- 减少首次渲染的题目数量(分时渲染)
- 客户端不做运行时的公式的渲染工作,渲染工作后置,交给服务端,我们只拿渲染好的
html字符串
- 针对问题4
- 无限列表(虚拟列表)
具体方法
- 错误上报机制
- 可在
katex
渲染出错时,调用上报接口,将错误信息反馈给服务端,服务端做相关记录。
- 可在
- 对资源进行彻底的清洗
- 提供规范化、剔除了特殊字符能被
katex
渲染的资源版本- 能够解决根源问题
- 问题
- 工作量大,推动难
- 提供规范化、剔除了特殊字符能被
- 首次渲染的题目数量,可采用分时渲染方式,将渲染压力分散到不同时间点
- 公式渲染后移,前端不在运行时做公式渲染,交给服务端做。
- 单独将公式的特殊字符替换和渲染放到服务端去做。服务端在返回公式资源时,直接返回使用渲染引擎渲染好的html字符串。这样,客户端就不用做公式渲染了,直接展示渲染好的html字符串。
- 问题
- 服务端并发压力大
- 问题
- 单独将公式的特殊字符替换和渲染放到服务端去做。服务端在返回公式资源时,直接返回使用渲染引擎渲染好的html字符串。这样,客户端就不用做公式渲染了,直接展示渲染好的html字符串。
- ssr
- 将所有前端页面(包括公式渲染)直接通过ssr方式输出
- 问题
- 缺乏ssr相关经验
- 问题
- 将所有前端页面(包括公式渲染)直接通过ssr方式输出
综上:错误上报机制是必须的。根源在底层公式资源,资源必须要做清洗。公式渲染放在后端做,是一个可行方案,但会增加后端压力。
方案3
方案2的问题
- 底层做资源清洗难度较大,因为资源数量太庞大,清洗时间太长,技术方案调研、实现需要人力太多,推动较难。
- ssr经验不足,公司不给试错机会,风险较大。放在服务端做渲染,会加大服务器压力
方案3的思路
- 既然资源是必须要清洗的,但服务端实现压力较大,那有没有可能将数据清洗的主要工作转移到前端?
前端将渲染好的
公式html字符串
回传给服务端供其他调用者使用- 这样减轻了服务端压力,实际的渲染在客户端完成
利用客户端做公式资源的清洗
- 前端仍然使用方案1的方式进行渲染,在
katex
报错时,将无法渲染的字符上报服务端,后期进行人工清洗。本地继续使用mathjax
渲染。若katex
未报错,则在渲染生成html节点
后。将公式的html符串
回传给服务端 - 服务端将
公式html字符串
保存起来,并提供给其他调用者使用 - 前端在拿到数据后,若发现已经有渲染好的
公式html字符串
,则直接将其插入到对应节点,否则,调用渲染引擎进行渲染,并重复1的步骤 - 经过上述步骤,服务端将会逐步完成资源的清洗工作。
优缺点
- 优点
- 服务端压力减小,将清洗工作分散到客户端上,利用客户端完成资源的清洗工作
- 实施难度较小
- 缺点
- 前端工作量加大,清洗时间加长
扩展
- 前端回传时,可以携带
公式渲染后的相关信息
- 例如公式渲染时的字体大小、公式渲染后的实际高度
- 高度在做无限列表(虚拟列表)时,是一个必要字段,之前较难获取到公式渲染后的实际高度
- 客户端还可以利用
canvas
生成公式渲染后的图片并回传给服务端。可供不同场景使用。
- 例如公式渲染时的字体大小、公式渲染后的实际高度