likes
comments
collection
share

手撸组件库之文档工具

作者站长头像
站长
· 阅读数 14

背景

因为最近失业中,便闲来无事,就整了个React组件库(不想面试),主要不喜欢antd,还有一些其他原因。也发过沸点,也有一些好奇实现方案的,所以便有了本文。

作为一个组件库,还是要有个文档的,所以搜了下市面的一些文档方案:

  1. vitepress
  2. storybook
  3. dumi

vitepress是基于vitevue实现,基于SSR进行渲染,适合静态文档,如果需要支持react的话,需要魔改一些东西,懒得折腾。

至于storybookdumi,个人原因不太喜欢。所以就打算自己撸一个,顺便了解下文档工具是怎么实现的。

这边和大部分组件库一样,都将通过markdown去实现文档工具,最终要把markdown渲染到页面上。

效果图

最终的一个效果图:

手撸组件库之文档工具 手撸组件库之文档工具

实现

要如何把markdown渲染到页面呢?

我们知道markdown最终会解析成html,可以根据这点做文章。由于我这里用的是webpack,所以将基于webpack进行说明。

webpack有个重要的概念是loaderloader的主要作用是对接收到的内容做转换处理,并返回出去,传递给下一个loader,而且每个loader的职责是单一的。所以我们需要实现loader,用于对markdwon做转换处理。

一个简单的loader

// 自己的loader
module.exports = functin mdLoader (source) {
    return source
}

// webpack.config.js
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.md$/,
                use: path.resolve(__dirname, '../loader/mdLoader.js')
            }
        ]
    }
}

这边会用到几个库

一个button组件的文档: 手撸组件库之文档工具

由于我们需要把markdown渲染到页面上,并且匹配对应路由,所以我们需要通过loader去实现一个组件。

module.exports = function mdLoader (source) {
   // 初始化markdown-it
  const md = new MarkdownIt({
    html: true,
    breaks: true,
    typographer: true,
    highlight(str, lang) {
       // 高亮代码块
      const template = `
          <HighlightCode
            lang={\`${lang}\`}
            code={\`${str}\`} 
          >
          </HighlightCode>
      `
      return template
    }
  })

  const mdToHtml = md
    .render(source)
    .replace(/<hr>/g, '<hr />')
    .replace(/<br>/g, '<br />')
    .replace(/class=/g, 'className=')

  // 返回的react component 用于做路由
  return `
    import * as React from 'react'
    import Block from 'Block'
    import { CodeExample } from 'CodeExample'
    import { HighlightCode } from 'bangumi-ui'

    function MdReact () {
      return <div className='b-md-container markdown-body'>${mdToHtml}</div>
    }

    export default MdReact
  `
}

上面就是我们实现的一个react component,并且通过markdown-itmarkdown渲染成了html,并放入了我们写好的一个react component字符串中。

目前这个东西算是完成了一小部分,但是这样依然是不够的,我们还需要改造下webpack config

// webpack.config.js
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.md$/,
                use: ['babel-loader', path.resolve(__dirname, '../loader/mdLoader.js')]
            }
        ]
    }
    // ...
}

现在的话应该是这个样子,这里使用babel-loader的主要作用是通过把我们自己写好的loader处理后的结果传递给babel-loaderbabel-loader会进一步转换,变成可以被React所认识的格式。

至此,你所写的markdown就可以被react所渲染了,只要配好router就可以。

至于anchor、toc参考github文档说明,这里简单说下containercontainer的作用是自定义代码块,这点在vitepress、vuepresss有被使用,指warning、success、danger这是里面提前定义好的内容。

这是container的语法,demo是你自定义的名字。

::: demo
:::

实现:

const md = new MarkdownIt({
    html: true,
    breaks: true,
    typographer: true,
    highlight(str, lang) {
      const template = `
          <HighlightCode
            lang={\`${lang}\`}
            code={\`${str}\`} 
          >
          </HighlightCode>
      `
      // process.exit()
      return template
    }
  })
  .use(container, 'desc', {
      // 渲染时候会执行该函数,tokens是解析后的一个语法结构,index是当前的一个开始和结束下标
      render(tokens, index) {
          if (tokens[index].nesting === 1) {
              // opening tag 开始标记
              return `<Block>`
            } else {
              // closing tag 结束标记
              return '</Block>'
            }
      }
  })

还有一个是gray-matter,这个是因为组件在渲染的时候,会找不到该组件,我用来自定义引入的。

module.exports = function mdLoader (source) {
   // 初始化markdown-it
  const md = new MarkdownIt({
    html: true,
    breaks: true,
    typographer: true,
    highlight(str, lang) {
       // 高亮代码块
      const template = `
          <HighlightCode
            lang={\`${lang}\`}
            code={\`${str}\`} 
          >
          </HighlightCode>
      `
      return template
    }
  })
 
  const { content, data } = matter(source)
  const mdToHtml = md
    .render(content)
    .replace(/<hr>/g, '<hr />')
    .replace(/<br>/g, '<br />')
    .replace(/class=/g, 'className=')

  // 返回的react component 用于做路由
  return `
    import * as React from 'react'
    import Block from 'Block'
    import { CodeExample } from 'CodeExample'
    import { HighlightCode } from 'bangumi-ui'
    ${data.import}
    
    function MdReact () {
      return <div className='b-md-container markdown-body'>${mdToHtml}</div>
    }

    export default MdReact
  `
}

至此一个loader的编写就算结束了,这是一个完整的代码。 手撸组件库之文档工具

end

这篇文主要是说了下markdown是如何被渲染的,简单的了解下组件库的文档工具都是一个怎么样的实现方式,并且也撸了一个loader用于解析我们的md文件。顺便也提到了loader的工作原理。

转载自:https://juejin.cn/post/7002898476388319269
评论
请登录