# @kangc/v-md-editor

https://code-farmer-i.github.io/vue-markdown-editor/zh/examples/base-editor.html

使用

import VMdEditor from '@kangc/v-md-editor'
import createKatexPlugin from '@kangc/v-md-editor/lib/plugins/katex/npm'

VMdEditor.use(createKatexPlugin())
1
2
3
4

我们先来看npm.js的代码

import creator from './creator';
import parserNpm from './parser-npm';

export default creator(parserNpm);
1
2
3
4

我们再来看creator的代码

export default function (parser) {
  return function createKatexPlugin(katexOptions) {
    return {
      install(VMdEditor) {
        VMdEditor.vMdParser.use(parser, katexOptions);
      },
    };
  };
}
1
2
3
4
5
6
7
8
9
  1. creator函数是一个高阶函数,接收一个参数parser
  • 参数parser,通常由parser-npm.js 提供,它负责扩展 Markdown 解析器以支持 KaTeX。

  • 返回一个函数createKatexPlugin

    该函数接受一个katexOptions参数,用于传递KaTeX相关的配置选项。

  1. createKatexPlugin函数返回一个包含install方法的对象:
  • 这个对象是一个Vue插件,内部定义了一个install方法。

  • install方法接受一个VMdEditor参数,表示Markdown编辑器实例。

  • 在install方法中,调用VMdEditor.vMdParser.use方法,传入先前的parser和katexOptions,将KaTeX解析功能添加到Markdown解析器中。

再来看parser-npm.js的代码

import parserCreator from './parser-creator';
import katex from 'katex';

export default parserCreator(katex);
1
2
3
4
  • parserCreator接收katex作为参数

综上可知,引入的parserNum就是parserCreator(katex)函数返回的结果,而parser-creator返回一个parser函数

将parser函数作为参数,传递给creator函数

也就是说最终得到的createKatexPlugin函数,已经绑定了parser函数

整个过程的执行流程如下:

用户代码
  ↓
VMdEditor.use(createKatexPlugin())
  ↓
createKatexPlugin(katexOptions) → 返回 { install }
  ↓
install(VMdEditor)
  ↓
VMdEditor.vMdParser.use(parser, katexOptions)
  ↓
parser(vMdParser, katexOptions)
  ↓
vMdParser.extendMarkdown((mdParser) => ...)
  ↓
mdParser.use(markdownItKatex, { ...katexOptions, katex })
  ↓
markdown-it 支持 KaTeX,公式渲染成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

parser-creator的代码

import markdownItKatex from '@/utils/markdown-it-katex';
export default function parserCreator(katex) {
  return function parser(vMdParser, katexOptions) {
    vMdParser.extendMarkdown((mdParser) => {
      if (katex) {
        mdParser.use(markdownItKatex, {
          ...katexOptions,
          katex,
        });
      }
    });
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • parserCreator是一个高阶函数,接收katex作为参数,返回另一个函数

当我们引入npm.js时,实际上引入的是creator(parserNpm)的返回值

import createKatexPlugin from '@kangc/v-md-editor/lib/plugins/katex/npm'

// 而parserNpm 则是parserCreator(katex)
// parserCreator也是一个函数,返回一个parser函数
// parserCreator 返回一个能使用 markdownItKatex 插件的解析器函数
// 当编辑器初始化时,这个解析器函数会被调用
export default function parserCreator(katex) {
  return function parser(vMdParser, katexOptions) {
    vMdParser.extendMarkdown((mdParser) => {
      if (katex) {
        mdParser.use(markdownItKatex, {
          ...katexOptions,
          katex,
        });
      }
    });
  };
}
// 所以creator(parserNpm) 就是createKatexPlugin(parser)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

1.export default function parserCreator(katex)

这是一个高阶函数,接受一个参数 katex。

参数 katex:这里传入的是 KaTeX 库的实例(通常是从 import katex from 'katex' 导入的)。

返回:parserCreator 返回另一个函数(parser),这使得 parserCreator 成为一个函数工厂(Function Factory),用于生成定制化的解析器函数。

为什么用高阶函数?

  • 高阶函数允许将 katex(KaTeX 实例)“绑定”到返回的 parser 函数中,形成闭包(Closure)。这样,parser 函数可以在后续调用时访问 katex,而无需重复传递。
  • 这种设计提高了代码的模块化和可复用性,允许在不同上下文中生成不同的解析器。

2.return function parser(vMdParser, katexOptions)

再来分析返回的parser函数,有两个参数

  • vMdParser:@kangc/v-md-editor 提供的解析器实例。它是一个对象,包含 extendMarkdown 方法,用于扩展底层的 Markdown 解析器。
  • katexOptions:一个可选的对象,包含 KaTeX 的配置选项,例如 { throwOnError: false, errorColor: '#cc0000' }。

返回:parser 函数本身不直接返回值,而是通过调用 vMdParser.extendMarkdown 修改 vMdParser 的行为。


3. vMdParser.extendMarkdown((mdParser) => {...})

VMdParser是定义的一个class,在utils/v-md-parser.js中,其中有个方法extendMarkdown

extendMarkdown(extender) {
    if (!this.themeConfig) {
        return console.error('Please use theme before using plugins');
    }

    const { markdownParser } = this.themeConfig;

    extender(markdownParser);
}
1
2
3
4
5
6
7
8
9
export default function parserCreator(katex) {
  return function parser(vMdParser, katexOptions) {
    vMdParser.extendMarkdown((mdParser) => {
      if (katex) {
        mdParser.use(markdownItKatex, {
          ...katexOptions,
          katex,
        });
      }
    });
  };
}
1
2
3
4
5
6
7
8
9
10
11
12

内部的extender实际就是传入的函数

(mdParser) => {
    if (katex) {
        mdParser.use(markdownItKatex, {
            ...katexOptions,
            katex,
        });
    }
}
1
2
3
4
5
6
7
8

函数参数mdParser则是markdownParser,markdownParser是themeConfig内的prismTheme.markdownParser(位于theme/vuepress/theme.js中)

markdown-it-katex源码

md.inline.ruler.after('escape', 'math_inline', math_inline);
1

这行代码是在 markdown-it 解析器中注册一个新的行内规则(inline rule),具体含义可以分解为:

  1. md.inline.ruler - 这是 markdown-it 的行内规则管理器
  2. .after('escape', ...) - 表示新规则将被添加到 'escape' 规则之后
  3. 'math_inline' - 这是新规则的名称
  4. math_inline - 这是处理该规则的函数

为什么要放在 'escape' 规则之后:

  1. markdown-it 按顺序处理规则
  2. 'escape' 规则处理转义字符(如 \)
  3. 数学公式解析需要在转义字符处理之后进行,以确保正确识别公式中的特殊字符

注册渲染规则:

md.renderer.rules.math_inline = inlineRenderer;
md.renderer.rules.math_block = blockRenderer;
1
2

# math_inline方法的参数state是什么

state 是 markdown-it 在解析过程中的状态对象,由 markdown-it 解析器内部创建和传递。它包含了当前解析过程中的所有必要信息。

主药属性:

  • src:当前正在解析的完整文本字符串
  • pos: 当前解析位置(索引)
  • posMax: 可解析的最大位置
  • pending: 待处理的普通文本
  • bMarks: 每行开始位置的数组
  • eMarks: 每行结束位置的数组
  • tShift:每行的缩进量,用于确定当前行的实际内容起始位置

# markdown-it-katex

markdown-it-katex已经很久不维护了,如需使用还请使用@vscode/markdown-it-katex