记一次markdown-it插件学习

2,619 阅读2分钟

开始的开始

开始前先说下需求背景;使用 vuepress 写api文档,不同环境下静态资源存放的路径不同,希望可以支持不同环境下的路径下;

markdown-it

了解到 vuepress 使用 markdown-it 来处理 .md 文件,首先想到的就是在 markdown-it 这里下功夫,根据构建环境动态生成部分 .md 文件中的路径(测试环境和正式环境在静态服务的路径不同);

vuepress 配置 markdown-it

vuepress 配置文件中(config.js)的导出对象中多加一个 markdown 的属性;其中可以添加一些针对 markdown-it 相关配置的选项;这里我们关注的是 markdown-it 的插件使用,所以首先要找到插件的配置入口,也是为了后边方便测试;

// vuepress config.js
module.exports = {
  markdown: {
    // markdown-it-anchor 的选项
    anchor: { permalink: false },
    // markdown-it-toc 的选项
    toc: { includeLevel: [1, 2] },
    extendMarkdown: md => {
      // 使用更多的 markdown-it 插件
      md.use(require('markdown-it-xxx'))
    }
  }
}

markdown-it

查阅了一些资料,并没有发现比较靠谱权威的插件开发指南,便参考了已有的插件;大概以下几点吧

  • markdown-it 插件是一个函数;
  • 函数接收一个参数(md)理解为当前 markdown-it 实例对象,上边存放了 markdown-it 的配置和解析到的数据(state);
  • markdown-it 插件需要往实例对象中添加解析规则,每条解析规则同样是一个函数;
  • 解析规则函数会接收当前一个state(当前解析到的数据)参数,插件的意义在于可以根据需求自由修改state的数据
  • 目前看到支持添加规则的场景(可能不全):
    • md.core.ruler.push('xx', () => {})
    • md.renderer.rules.xx = () => {}
    • md.block.ruler.push('xx', () => {})
    • md.inline.ruler.push('xx', () => {})
    • md.inline.ruler2.push('xx', () => {})
    • ...
  • ruler 除了支持 push,还支持 after、before、disable、enable 等方法;
// 示例代码
// 下边代码则是修改了开始提到的 md 文件中的路径
const markdownItPlugin = md => {
  md.core.ruler.push('my_rule', function (state) {
    state.tokens.forEach(token => {
      const { type, content } = token;
      if (type === 'html_block' && content.startsWith('<HtmlRunner')) {
        token.content = content.replace(
          /\/examples\//,
          `${process.env.BUILD_ENV === 'daily' ? '/test' : ''}$&`
        );
      }
    });
  });
};

个人愚见

markdown-it 插件个人理解和 ast 类似,先解析目标文件,使用类似抽象语法树(tokens)的形式来描述目标文件语法,后边提供可以修改 tokens 的能力,最后将修改后的 tokens 生成文件;

最后的最后

大功告成!markdown-it 插件在构建层面解决了问题,来个快乐水犒劳下自己吧;这时不知道为什么(想不起来了)又看起了vuepress 的文档,文档中发现了一个帮助函数$withBase(它被注入到了Vue的原型上);还记得开始的问题吗?支持不同环境下的路径下,根据环境构建同样使用了 base 配置;

问: 直接用这个是不是就解决了开始的问题?

答案: 是的。。。(小丑竟是自己)

最后符上大佬的表情包

image.png

参考