01月04, 2017

regularDoc原理

我们平常看文档,像elementantd,都能看到组件有动态的效果及源码。

和element的成员沟通了一下,他们的文档是通过下面的方式实现的:

md -> vue-md-loader -> vue template file -> vue-loader

我想antd也是类似的方式吧。这两天在看regular-doc,看了一下它的实现方式,觉得蛮有意思的。

它是基于gulp流来生成doc文件夹。

const build = require("./src/gulp-build.js");
gulp.task("doc-build", (done) => {
    return gulp.src("./src/content/**/*")
        .pipe(build())
        .pipe(gulp.dest("./doc"));
});

gulp-build是自定义的gulp插件,它的作用是处理md文件生成html。gulp如何自定义插件

解析markdown

"use strict";

let markextend = require("markextend");
let codemirror = require("codemirror-highlight");

markextend.setOptions({
    // 代码高亮
    highlight(code, lang) {
        if(lang && !codemirror.modes[lang]) {
            if(lang === "coffee") lang = "coffeescript";
            if(lang === "json") lang = "javascript";
            if(lang === "html") lang = "xml";
            if(lang === "rgl") lang = "xml";
            if(lang === "console") return code;

            try {
                codemirror.loadMode(lang);
            } catch(e) {
                console.error(e);
            }
        }

        if(codemirror.modes[lang])
            return codemirror.highlight(code, {mode: lang});
        else
            return code;
    }
});

module.exports = markextend;

上面的代码就是markdown转成html,同时用codemirror高亮代码。

处理m-example的div容器

"use strict";

let rule = {
    example: /<div class="m-example(?:.*?)"><\/div>([\s\S]+?)(?:##|$)/g,
    pre: /```(.+?)\n([\s\S]+?)\n```/g,
}

function parse(content) {
    let examples = [];
    let cap, cap2;
    while(cap = rule.example.exec(content)) {
        let example = {};
        let part = cap[1];
        while(cap2 = rule.pre.exec(part))
            if(cap2[1])
                example[cap2[1]] = cap2[2];
        examples.push(example);
    }

    return examples;
}

exports.premark = function(content) {
    let result = {};

    let examples = parse(content);
    let i = 0;

    let strings = ["let index = 0;"];
    result.content = content.replace(/<div class="m-example"><\/div>/g, () => {
        let example = examples[i++];

        if(example.rgl) {
            strings.push("((index) => {");
            strings.push("    let template = `" + example.rgl + "`;");
            if(example.javascript)
                strings.push(example.javascript);
            else {
                strings.push("let component = new RGUI.Component({");
                strings.push("    template: template");
                strings.push("});");
            }
            strings.push("  typeof component != "undefined" && component.$inject($$(".m-example")[index]);");
            strings.push("})(index++);");
        }


        return "<div class="m-example">"
            + (example.css ? "<style>" + example.css + "</style>" : "")
            + (example.html || "")
            + "</div>";
    });

    result.script = strings.join("\n");

    return result;
}

这一步就是核心的部分了。简单地说,它会parse下面的结构:

doc

然后得到m-example容器后面的模板及javascript代码,将其组装到result.script里面,最终放到页面中,去执行。

需要注意的是:,m-example的DIV容器之间必须要有##来隔离,不然会报错。其实想想,一般标题,一个示例,也是正常的。

因此,最终生成的html光有md文件是远远不够的,肯定还需要头、侧边、底部等。

regular-doc里面,它是写成了一个个的ejs模板,如下图:

menu

通过获取它们的内容,然后ejs.render即可得到我们的最终效果。

让模板和javascript显示在Tab容器中

效果如下:

tab

它的源码简单如下:

// 生成tabs
(function() {
    var $examples = $$(".m-example");

    for(var i = 0; i < $examples.length; i++) {
        var $example = $examples[i];
        if(!$example)
            break;

        var source = [];

        var $next = $example.nextElementSibling;
        while($next && $next.className.indexOf("m-example") < 0) { // $next.tagName[0] !== "H" &&
            if($next.className === "lang-xml" || $next.className === "lang-rgl") {
                source.push({title: "rgl", content: $next.outerHTML});
                $prev = $next;
                $next = $next.nextElementSibling;
                $prev.remove();
            } else if($next.className === "lang-html") {
                source.push({title: "html", content: $next.outerHTML});
                $prev = $next;
                $next = $next.nextElementSibling;
                $prev.remove();
            } else if($next.className === "lang-javascript") {
                source.push({title: "javascript", content: $next.outerHTML});
                $prev = $next;
                $next = $next.nextElementSibling;
                $prev.remove();
            } else
                $next = $next.nextElementSibling;
        }

        if(!source.length)
            continue;

        new RGUI.Component({
            template: RGUI._.multiline(function(){/*
             <tabs>
             {#list source as item}
             <tab title={item.title}><div r-html={item.content}></div></tab>
             {/list}
             </tabs>
             */}),
            data: {
                source: source
            }
        }).$inject($example, "after");
    }
})();

更多代码请参考:regular-ui-doc,不过它这个代码并不完整,但还是可以从中分析出一些东西来。

本文链接:www.my-fe.pub/post/regular-doc-principle.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。