latex-clean-use-client

latex公式渲染及利用客户端做公式资源的清洗

背景

历史背景

  • 最近接手的项目中,有大量的H5渲染latex公式的需求
  • 由于历史原因,我们的latex公式资源都是针对mathjax公式渲染引擎定制化制作的

存在的问题

我们存在的问题

  • 由于历史原因,我们的资源大部分都是针对mathjax制作的,无法直接用katex进行渲染。而mathjax在pc端使用时,效果、速度勉强还能接受,但在移动端却差强人意,渲染速度慢,公式闪烁等问题严重。

社区内的渲染方案

  • 要实现latex公式的渲染,社区内主流的方案有两种:
    • 一,使用katex渲染引擎来渲染。
      • 速度快,渲染效果好
      • 对数据格式有要求,部分字符无法渲染
    • 二,使用mathjax来渲染
      • 兼容性、容错性好
      • 速度较慢

解决方案(方案1)

思路

  • 优先使用性能高的katex进行渲染,在katex无法渲染时,再切换到mathjax

实现

  1. 资源处理
    1. 我们针对服务端返回的资源做了一些特殊处理。例如使用正则对资源进行规范化,让其能被katex 渲染。同时,剔除了一些无法识别的特殊字符。
  2. 出错降级处理
    1. katex无法渲染时,会抛出错误,我们捕获了错误,再利用mathjax进行渲染。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      try {
      return this.htmlString.replace(/\\\(.*?\\\)/g, function(html) {
      // * 剔除异常字符
      html = html
      .replace(/&lt;/g, '<')
      .replace(/&gt;/g, '>')
      return (
      ' ' +
      katex.renderToString(html, {
      throwOnError: false,
      unicodeTextInMathMode: true
      }) +
      ' '
      )
      })
      } catch (err) {
      // * 解析错误时,降级使用MathJax
      MathJax.Hub.Queue(['Typeset', MathJax.Hub, el])
      }
  • 经过上述操作,所有公式已经可以渲染出来了,但仍存在一些问题。

问题

  1. 虽然公式都能渲染出来了,但仍有问题
    1. 因为没有完备的特殊字符替换表,导致仍有部分特殊字符无法被katex渲染
      1. 降级使用mathjax时,能明显看到公式的渲染过程
      2. 题量过大时,由于一次性需要渲染的公式较多,无论是使用katex还是mathjax在渲染时都会明显感觉到页面的卡顿
      3. 题量大时,列表滚动卡顿

方案2

思路

  • 针对问题1
    • 提供一个错误上报机制,收集katex无法渲染的字符
  • 针对问题2
    • 既然使用mathjax速度慢,那干脆不用mathjax全使用katex
      • 需要对资源进行清洗(规范化、剔除特殊字符)
      • 需要依赖报错收集机制
  • 针对问题3
    • 减少首次渲染的题目数量(分时渲染)
    • 客户端不做运行时的公式的渲染工作,渲染工作后置,交给服务端,我们只拿渲染好的html字符串
  • 针对问题4
    • 无限列表(虚拟列表)

具体方法

  1. 错误上报机制
    • 可在katex渲染出错时,调用上报接口,将错误信息反馈给服务端,服务端做相关记录。
  2. 对资源进行彻底的清洗
    • 提供规范化、剔除了特殊字符能被katex渲染的资源版本
      • 能够解决根源问题
      • 问题
        • 工作量大,推动难
  3. 首次渲染的题目数量,可采用分时渲染方式,将渲染压力分散到不同时间点
  4. 公式渲染后移,前端不在运行时做公式渲染,交给服务端做。
    • 单独将公式的特殊字符替换和渲染放到服务端去做。服务端在返回公式资源时,直接返回使用渲染引擎渲染好的html字符串。这样,客户端就不用做公式渲染了,直接展示渲染好的html字符串。
      • 问题
        • 服务端并发压力大
  5. ssr
    • 将所有前端页面(包括公式渲染)直接通过ssr方式输出
      • 问题
        • 缺乏ssr相关经验

综上:错误上报机制是必须的。根源在底层公式资源,资源必须要做清洗。公式渲染放在后端做,是一个可行方案,但会增加后端压力。

方案3

方案2的问题

  • 底层做资源清洗难度较大,因为资源数量太庞大,清洗时间太长,技术方案调研、实现需要人力太多,推动较难。
  • ssr经验不足,公司不给试错机会,风险较大。放在服务端做渲染,会加大服务器压力

方案3的思路

  • 既然资源是必须要清洗的,但服务端实现压力较大,那有没有可能将数据清洗的主要工作转移到前端?
  • 前端将渲染好的公式html字符串 回传给服务端供其他调用者使用

    • 这样减轻了服务端压力,实际的渲染在客户端完成

    利用客户端做公式资源的清洗

  1. 前端仍然使用方案1的方式进行渲染,在katex报错时,将无法渲染的字符上报服务端,后期进行人工清洗。本地继续使用mathjax渲染。若katex未报错,则在渲染生成html节点后。将公式的html符串回传给服务端
  2. 服务端将公式html字符串保存起来,并提供给其他调用者使用
  3. 前端在拿到数据后,若发现已经有渲染好的公式html字符串,则直接将其插入到对应节点,否则,调用渲染引擎进行渲染,并重复1的步骤
  4. 经过上述步骤,服务端将会逐步完成资源的清洗工作。

优缺点

  • 优点
    • 服务端压力减小,将清洗工作分散到客户端上,利用客户端完成资源的清洗工作
    • 实施难度较小
  • 缺点
    • 前端工作量加大,清洗时间加长

扩展

  • 前端回传时,可以携带公式渲染后的相关信息
    • 例如公式渲染时的字体大小、公式渲染后的实际高度
      • 高度在做无限列表(虚拟列表)时,是一个必要字段,之前较难获取到公式渲染后的实际高度
    • 客户端还可以利用canvas生成公式渲染后的图片并回传给服务端。可供不同场景使用。