文档无感沉淀 – 更优雅的方式编写前端文档

团队的开发规范在哪儿?
xxx项目有没有具体文档说明?
xxx组件有没有人写过?怎么调用?
对于多人协作的团队来说,如果没有文档,常常会陷入上述追问或者因信息不对称造成效率的降低。
对于团队来说,文档主要承担着指引和备忘的作用,对于前端团队来说,还需要展示公共组件和接口文档。实际上,很多团队都具备这些文档,但是往往存在一些问题:

  1. 不直观。对于UI组件来说,需要能在文档中直观预览效果。
  2. 分散。每一类文档分散在不同的地方,比如项目文档和团队规范相互独立,不方便查看。
  3. 不及时。很多组件其实代码已经更新了,但是文档却还是旧的。

真正的科技就是你感受不到科技的存在,最好的文档编写方式就是不需要写文档,这里核心要解决的问题是两个自动化问题,分别是自动生成和自动归档。基于这两个点,我们结合现有的框架撸了一套适用于vue项目的文档框架:docsmaker
使用很简单,只需要在项目的package.json中简单配置一下(更详细的配置和功能请查看上面的github项目):

{
    ...
    "docs": {
        "title": "波洞APP",
        "name": "boodo-app",
        "components": "src/boodo-ui/packages",
        "docs": "docs"
      }
    ...
}

然后安装docsmaker,并执行文档命令即可:

//  安装文档工具docsmaker
$  npm   install   @dopro/docsmaker   -g
//  开发模式下执行文档工具
$  docsmaker
//  构建文档静态文件
$  docsmaker  build

接下来文档就自己愉快的玩耍起来了:

文档无感沉淀 - 更优雅的方式编写前端文档

下面介绍下该工具主要的能力和实现原理。

自动化生成

对于前端最常用的就是markdown来沉淀文档,但是markdown语法比较基础,对于一些项目来说,文档可能分多个markdown文件,为了方便查阅,很多时候需要导航索引,而且我们的项目是基于vue,有时候还需要在文档中呈现vue组件。而这些功能vuepress都能满足,它可以与vue组件联动,在markdown中内嵌vue代码,.vuepress / components 中的任意 * .vue 文件都会自动注册为全局的异步组件。例如:

.
└─ .vuepress
   └─ components
      ├─ demo.vue
      └─ OtherComponent.vue

那么 markdown 文件中,你可以直接使用这些组件:

<demo/>
<OtherComponent/>

并且支持导航和侧边栏的定制等等方便实用的功能,具体可查阅vuepress的官网。我们需要对vuepress的能力进行扩展,以满足更多自动化场景。

Demo预览能力扩展

前端经常跟UI打交道,所以会涉及很多UI组件的文档,这些文档只用文字是很不直观的,往往我们需要使用demo演示,但markdown(vuepress)不支持这种语法,需要自行扩展vuepress的插件。
由于vuepress支持vue语法,所以我们可以将预览功能写成一个vue组件:

// .vuepress/plugins/demo-code/DemoAndCode.vue
<template>
    <section class="demo-and-code-wrapper">
        <div class="demo-container">
           // 这里放展示demo,其实就是把代码直接运行
            <slot name="demo" />
        </div>
        <div class="code-wrapper" >
            <div ref="codeContainer">
                // 这里放格式化后的代码
                <slot name="code" />
            </div>
        </div>
    </section>
</template>
...

接下来就是扩展vuepress的markdown,往vue组件里面塞内容:

// .vuepress/config.js  vuepress的配置文件
const demoCode = require("./plugins/demo-code")
module.exports = {
    ...
    plugins: [
        [demoCode],
    ],
    ...
}

// .vuperess/plugins/demo-code.js  插件代码
const markdownItContainer = require('markdown-it-container'); // 用来解析markdown格式,识别:::开头和结尾的markdown格式。
const markdown=require("@vuepress/markdown"); // 获取vuepress的markdown解析器,保证跟原文档的样式一致。
module.exports = (options = {},context) => {
    const demoCodeMark = 'demo';
    const END_TYPE = `container_${demoCodeMark}_close`
    return {
        name: 'vuepress-plugin-demo-code',
        extendMarkdown: (md) => {
            md.use(markdownItContainer, demoCodeMark, { render })
        },
    }

    function render (tokens, idx) {
        const { nesting, info } = tokens[idx]
        // 如果已经结束,则闭合标签
        if (nesting === -1) {
            let ret= '</template></DemoAndCode>n';
            return ret;
        }

        let htmlStr = ''
        // 获取内容
        for (let index = idx; index < tokens.length; index++) {
            const { map, type, content } = tokens[index]

            if (type === END_TYPE) break
            if (type === 'html_block') {
                const delta = map[0] - (lastLine || map[1])

                if (delta > 0) {
                    htmlStr += 'n'.repeat(delta)
                }

                htmlStr += content
                lastLine = map[1]
            }
        }
        codeStr='n```'+language+'n'+htmlStr+'n```n';
        // 使用vuepress的解析器来解析代码。
        codeStr=markdown(markdownConfig).render(codeStr).html;
        return `
            <DemoAndCode>
                <template slot="code">
                    ${codeStr}
                </template>
                <template slot="demo">
        `
    }
}

经过扩展以后,我们就可以通过简单的格式,快速写好demo和代码的预览,并可以在codepen中预览(前面的代码为核心逻辑,不涉及这部分),下面是示例的markdown写法:

::: demo html codepen
<p class="common-html">
    this is <span style="color: red;">common</span> html
</p>
<style>
.common-html {
    color: green;
}
</style>
:::

渲染以后的效果:

文档无感沉淀 - 更优雅的方式编写前端文档

vue文件即文档

对于前端开发来说,组件除了需要预览,还需要API说明。针对不同的语言和框架已经有了很多自动生成文档的工具,比如针对js的jsdoc,借助注释的规范来输出文档。
这里主要针对vue来展开,vue本身有很好的代码组织方式,不用借助复杂的注释规范,就可以自动生成可读性较强的文档,你的vue文件就是你的文档了,就好像前面介绍的截图中所示,组件的文档除了预览还有API说明。
vuese就是用来解决这个问题的工具库,它可以将vue代码进行格式化,输出JSON或者markdown格式。vuese解析出来的内容如下图的右侧。

文档无感沉淀 - 更优雅的方式编写前端文档

内容已经解析出来了,只需要跟vuepress进行简单整合即可:

const {parser} = require("@vuese/parser");  // vuese解析vue文件的方法
const {Render} = require('@vuese/markdown-render'); // 将解析出来的json数据转换成markdown语法
const globby = require("globby");
async function genVueToMakedown(dir,docsDir,project){
    let files=await globby(path.join(dir,'**/*.vue')); // 遍历指定目录下的所有vue文件
    for(let i=0;i<files.length;i++){
        let file = files[i];
        const source = fs.readFileSync(file, 'utf-8');
        try {
            let parserRes = parser(source);
            const r = new Render(parserRes)
            const markdownRes = r.render();
            if(markdownRes){
                let md = `# ${parserRes.name}nn`;
                // 组件描述
                for(let key in parserRes.componentDesc){
                    let val = parserRes.componentDesc[key];
                    for(let i=0;i<val.length;i++){
                        md += val[i]+'n';
                    }
                }
                md+=`## 预览nn`;
                // 利用上面提到的预览能力生成组件预览
                md+=`::: demo vue
                n<${componentName}/>
                n:::nn`;
                // 剩下的就是vuese生成的api文档内容
                for(let key in markdownRes){
                    md +=`## ${key}nn`;
                    md +=markdownRes[key];
                }
                // 最后把md写入指定markdown文件中即可
            }
        } catch(e) {
            console.error(e)
        }
    }
}

到这里已经可以将vue文件生成markdown文档了,但是很多组件往往有很多不同的状态,docsmaker在处理props的注释的时候,对数组格式进行了进一步拆分,在生成的文档中,会将不同状态的预览同时显示出来,方便查阅,比如以下vue文件:

<template>
    <div class="ui-btn">{{text}}</div>
</template>
<script>
export default {
    props:{
        text:{
            // ["按钮","确定"]
            type:String,
            default:'按钮'
        }
    }
}
</script>
...

最终会渲染出两个不同状态的按钮:

文档无感沉淀 - 更优雅的方式编写前端文档

以上已经覆盖了前端文档在撰写方面的大部分自动化场景,但是撰写完了以后,往往还需要部署到线上,部署是否也可以自动化?

自动化归档

由于docsmaker生成的文件都是静态文件,所以只需要部署到静态服务器上即可,如果你是用github(公司的git仓库也是一样的)来托管项目,那么你可以利用github的pages功能来建立免费的文档站。
github pages支持三种方式,无论你选择那种方式都极为方便。

- The master branch
- The gh-pages branch
- A folder named "docs" located on the master branch

这里推荐使用第三种方式,只需要在package.json中配置生成目录到docs目录即可,执行docsmaker build即可将文档的静态文件构建到docs目录下:

{
    ...
    "docs": {
        // 该项目下的文档所在目录
        "docs":["docsmaker"],
        // 使用build模式生成的文档位置,默认为"docsmaker"
        "dest":"docs",
}

当然,如果你希望文档部署在指定的服务器上,跟其他项目的文档进行整合,那么利用git或者svn的钩子,当每次文档有更新的时候,自动构建到指定的服务器。

经过这些折腾,docsmaker可以让开发文档特别是组件文档在你不经意间以最好的方式呈现给你,让你有更多的时间去享受代码的乐趣。

始发于微信公众号: 腾讯DeepOcean

发表评论