首页

    博客开始支持标签

    标签:blog,openresty,javascript

    文章越来越多了,需要给每篇文章打上标签,方便分类
    由于博客是静态的,我也不打算把标签保存在数据库,页面加载时发出异步请求,获取标签数据.

    博客的所有页面都是通过gulp-template-html生成的,因此只要在模板页面加上标签侧边栏就行了

    • markdown文件添加标签,占位符和title,content类似
    <!-- build:tags -->my-tag<!-- /build:tags -->
    
    • 编写gulp插件收集标签数据
    function tags() {
        function process (file, enc, cb) {
            ...
    
            if (file.isBuffer()) {
                var title_reg=/build:title.+>(.*?)</,
                    tag_reg=/build:tags.+>(.*?)</,
                    path_reg=/markdown\/(.+)\./,
                    content=file.contents.toString(enc),
    
                    title_result=title_reg.exec(content),
                    tag_result=tag_reg.exec(content),
                    path_result=path_reg.exec(file.history[0]);
    
                var tags,title,path
                if(title_result!=null){
                    title=title_result[1]
                }
                if(path_result!=null){
                    path=path_result[1]
                }
                if(tag_result!=null){
                    var item={title:title,path:path}
                    tags=tag_result[1].split(';')
                    for(var i=0;i<tags.length;i++){
                        var posts=all_tags[tags[i]]
                        if(posts==undefined){
                            all_tags[tags[i]]=[item]
                        }else{
                            all_tags[tags[i]].push(item)
                        }
                    }
                }
                file.contents = new Buffer(file.contents, enc);
            }
            cb(null, file);
        }
        ...
    }
    
    module.exports = tags;
    

    标签数据存在all_tags变量,格式如

    {
        "nginx":[
            {"title":"openresty定制prometheus metrics监控nginx(1)","path":"monitor-nginx-with-openresty-and-prometheus-1"},
            {"title":"openresty定制prometheus metrics监控nginx(2)","path":"monitor-nginx-with-openresty-and-prometheus-2"},
            ...
        ],
        "tmux":[{"title":"我的tmux配置","path":"tmux_conf"}],
        ...
    }
    
    • 模板页面添加标签列表html

    上面的stream注册end事件,收集完所有markdown文件的标签后,生成标签列表的html

    return through.obj(process).on('end',function(){
        co(gen_tags_html,Object.keys(all_tags));
        ...
    });
    
    var through = require('through2'),
        PluginError = require('plugin-error'),
        fs = require('fs'),
        path = require('path'),
        thunkify = require('thunkify'),
        writeFileThunk=thunkify(fs.writeFile)
        readFileThunk = thunkify(fs.readFile),
        co = require('co');
    
    var root_path=path.resolve(__dirname),
        all_tags={};
    
    function* gen_tags_html(tag_list){
        var template_path=root_path+'/src/template.html',
            content=yield readFileThunk(template_path),
            html='<ul>\n';
        for(var i=0;i<tag_list.length;i++){
            html+="<li><a href='../tag/"+tag_list[i]+"'/>"+tag_list[i]+"</a></li>\n"
        }
        html+='</ul>\n'
        content=content.toString().replace('{{tag_list}}',html)
        yield writeFileThunk(template_path,content)
    }
    

    这里使用ES6的Generator函数,避免异步函数的回调金字塔,使用tj大神co执行Generator函数

    • gulp配置添加tags task
    gulp.task('tags',function(){
        return gulp.src([root_path+'/src/markdown/!(index)*'])
        .pipe(tags())
        .pipe(gulp.dest(root_path+'/src/markdown'))
    });
    
    gulp.task('markdown',['css','js','tags'],function(){
        var manifest = gulp.src(root_path+'/build/*');
        return gulp.src(root_path+'/src/markdown/*.md')
        .pipe(markdown())
        .pipe(template(root_path+'/src/template.html'))
        .pipe(rename({extname: ".html"}))
        .pipe(revRewrite({ manifest }))
        .pipe(gulp.dest(root_path+dist_dir+'/html'))
    });
    

    tags details

    还需要标签详情页,就是点击侧边栏标签跳转到的页面,页面内容是该标签下的所有文章

    • 生成标签json文件
    var through = require('through2'),
        PluginError = require('plugin-error'),
        fs = require('fs'),
        path = require('path'),
        thunkify = require('thunkify'),
        writeFileThunk=thunkify(fs.writeFile)
        readFileThunk = thunkify(fs.readFile),
        co = require('co');
    
    ...
    return through.obj(process).on('end',function(){
        co(gen_tags_html,Object.keys(all_tags));
        co(gen_tags_json,all_tags);
    });
    
    function* gen_tags_json(all_tags){
        yield writeFileThunk(root_path+'/tags.json',JSON.stringify(all_tags))
    }
    
    • nginx加载标签json文件

    当然要先配置gitlab CI,将json文件添加到docker镜像

    .gitlab-ci.yml

     build:
       stage: build
       ...
       script:
         - docker pull $CACHE_IMAGE_URL || true
    -    - tar -cf source.tar nginx.conf lua src gulpfile
    +    - tar -cf source.tar nginx.conf lua src gulpfile tags*
         - docker build --cache-from $CACHE_IMAGE_URL -f ./dockerfile/build -t $BUILD_IMAGE_URL -t $CACHE_IMAGE_URL .
         ...
    

    nginx.conf

    init_by_lua_file /root/lua/init.lua;
    

    init.lua

    local json = require("cjson")
    local tags_json=""
    file = io.open("/root/blog/tags.json", "r")
    for line in file:lines() do
      tags_json=tags_json..line
    end
    file:close()
    tags = json.decode(tags_json)
    
    location ^~ /tag {
        root /root/blog;
        content_by_lua_file /root/lua/tags.lua;
        add_header content-type text/html;
        ...
    }
    

    tags.lua

    --请求是/tag/nginx,提取后为nginx
    local key=string.sub(ngx.var.request_uri,6)
    local template = require "resty.template"
    template.render("tags.html", { tag_list =tags[key] })
    

    通过tags[key],由上面的标签json,得到该tag下所有文章

    模板tags.html

    <!-- build:content -->
    {% for _, tag in ipairs(tag_list) do %}
        <li><a href="../{{tag.path}}.html">{{tag.title}}</a></li>
    {% end %}
    <!-- /build:content -->