Element Plus 组件库官网 API 跳转相关示例实现思路

433 阅读1分钟

前言

在 Element Plus 上看一个关于 docs 优化的 issue ,试着把它实现下。

问题-关键

问题:组件库官网在介绍组件时会实现相关示例和注意事项,同时也会列举组件 API 来提供给开发者使用,想要的效果是能不能自动实现从组件 API 跳转至相关的示例?

关键:示例在说明时会加上使用到的 API,这个就是核心,组件 API 和示例产生纽带的关键。

实现

Element Plus 官网 docs 使用第三库 markdown-it 解析 md 文档,以下简单通过插件实现跳转功能。

import type MarkdownIt from 'markdown-it'

const rControl = /[\u0000-\u001F]/g
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g
const rCombining = /[\u0300-\u036F]/g

// 处理示例标题的方法
function slugify(str: string): string {
  return (
    str
      .normalize('NFKD')
      .replace(rCombining, '')
      .replace(rControl, '')
      .replace(rSpecial, '-')
      .replace(/-{2,}/g, '-')
      .replace(/^-+|-+$/g, '')
      .replace(/^(\d)/, '_$1')
      .toLowerCase()
  )
}
export default (md: MarkdownIt) => {
  // 匹配到对应的规则时替换为 a 标签通过锚点跳转
  md.renderer.rules.api = (tokens, idx) => {
    const { content, info } = tokens[idx]
    return `<a href="#${info}">${content}</a>`
  }
  // add api rluer
  md.core.ruler.push('api', (state) => {
    const head: string[] = [],
      description: string[][] = [],
      map = {}

    for (let i = 0; i < state.tokens.length; i++) {
      const { type, tag, info } = state.tokens[i]
      // 收集示例标题
      if (type === 'heading_open' && tag === 'h2') {
        const { content } = state.tokens[i + 1]
        if (content.endsWith('API')) break
        head.push(slugify(content))
      }
    // 收集描述关键字
      if (type === 'container_demo_open' && info.startsWith('demo')) {
        const matchArr = info.match(/`[a-zA-Z-]+`/g)
        let res: string[] = []
        if (matchArr && matchArr.length > 0) {
          res = matchArr.map((item) => item.slice(1, -1))
        }
        description.push(res)
      }
      for (const [idx, item] of description.entries()) {
        map[head[idx]] = item
      }
    }
    // 收集示例标题和描述关键字建立为对象结构
    for (const [idx, item] of description.entries()) {
      map[head[idx]] = item
    }
    for (let j = 0; j < state.tokens.length; j++) {
      const { type } = state.tokens[j]
      if (type === 'tr_open' && state.tokens[j + 2]['content'] !== 'Name') {
        let { content, children } = state.tokens[j + 2]
        if (content.includes('`')) {
          content = content.slice(1, -1)
        }
        for (const key in map) {
          if (map[key].includes(content) && children) {
            children.length = 0
            children.push({
              type: 'api',
              content,
              info: key,
            } as any)
          }
        }
      }
    }
  })
}

问题

  1. 表格第一列的参数超过一个的问题;
  2. 相关描述关键字有重复,导致跳转位置在最后一个;
  3. AttributesEventsSlots等有重复属性问题;

解决方法:在组件属性上用标识符。

如果需要新增标识符的话,想到了一种半自动的解决方案:在需要跳转的属性上加上标识符来指定跳转的位置,这样更容易开发者理解。

希望可以给大家带来一些思考。