Skip to content

使用github pages + issues + api建立个人博客

Posted on:November 15, 2014 at 07:32 PM

[2023-05-28] 本博客已经基于astro-paper + giscus重写

[2018-09-27] 此文已经 outdated,我使用 angular 6 重写了这个 blog system. 现在只需 fork 并简单操作即可使用。see instructions. 不过原理仍是类似的,此文可作为参考.

Table of contents

Open Table of contents

以下为旧文

前言

最近写了一个简单的博客并放在github上,在此详述一下细节,以为分享。

方法并不高端,但是:

  1. 简单易行不要钱。 不需要数据库,不需要服务器,不需要域名,因为 github 都帮我们做了,壮哉我大 github
  2. 完全自定义的纯 HTML/JS/CSS 代码。不需要学各种 static site generator 的玩法,又能实现独一无二的个人博客 

好了,废话稍止,进入正文。

原理就一句话

前端代码并 host 在github pages, 利用github issues做为后台, 通过github API完成前后端交互

基本介绍

为何不直接使用 issues 作为博客

事实上,直接使用 issues 作为博客也是可行的,从这个角度,就是把 github issues 当成博客平台。
这个方案的缺陷是:

而使用 github API 来构建 no backend app, 即可以合理利用 github 提供的强大功能,又能随心所欲的定义自己的网站,还能集成任意的第三方服务(评论、分享等),十分潇洒

我的玩法

本博客基于m-angular-boilerplate开发,这是我写的一个前端快速开发框架,主要技术为angularJS + bootstrap + grunt + coffeeScript,有兴趣的朋友可以看看。😀

这个框架的 scope 不同于博客系统,在此先不多说。本文会主讲博客涉及到的内容。

上酸菜和代码

首先要在 Github 上建立 repo,名字为YOUR_USER_NAME.github.io, 比如我的martin-liu.github.io。 拉到本地后开始 coding。 以下为 coffee 编译出来的 js 代码,主要使用 angularJS,如用其它框架实现,按同样的原理来就是

  1. 注册 routing。就是把 url 和页面逻辑对应,比如http://martin-liu.github.io/#!/这个 url 就找partials/home.html这个 html,并执行HomeCtrl这个 function。如果找不到,就去 404 页面

    angular.forEach(Config.routes, function (route) {
      if (route.params && !route.params.controller) {
        route.params.controller = "BaseCtrl";
      }
      $routeProvider.when(route.url, route.params);
    });
    $routeProvider.otherwise({
      templateUrl: "partials/404.html",
    });

    Config.routes 内容为:

    [
      {
        url: "/",
        params: {
          name: "home",
          label: "Home",
          templateUrl: "partials/home.html",
          controller: "HomeCtrl",
        },
      },
      {
        url: "/article/:id",
        params: {
          name: "article",
          hide: true,
          templateUrl: "partials/article.html",
          controller: "ArticleCtrl",
        },
      },
      {
        url: "/about",
        params: {
          name: "about",
          label: "About",
          templateUrl: "partials/about.html",
        },
      },
    ];
  2. Home 页面的实现

    code 如下,BlogRemoteService.getBlogs()就是 ajax call 刚刚那个 url,拿 issues 数据

    BlogRemoteService.getBlogs().then(
      (function (_this) {
        return function (blogs) {
          return (_this.data.blogs = _this.processBlogs(blogs));
        };
      })(this)
    );
    
    processBlogs = function (blogs) {
      return _.map(blogs, BlogService.decorateBlog);
    };

    BlogService.decorateBlog 就是下面的取 summary

    • 文章 summary image

    可以看到,文章内容有一段注释,里面是 json 代码。注释不会显示,但可被获取,做为 metadata

    <!--
    {
    "summary":"渺小如我们,是风吹动水面,是蝴蝶一次振翅。在正确的位置,也能掀起远方的风暴;找到那个支点,也能撬动地球。"
    }
    -->

    BlogService.decorateBlog 的内容如下,用来解析注释内容,赋值给blog.meta

        decorateBlog: function(blog) {
          var e, meta, metaStr;
          if (!blog.body) {
            return blog;
          }
          metaStr = blog.body.substring(0, blog.body.indexOf('-->'));
          metaStr = metaStr.replace(/\n|\r|<!-{2,}/gm, ' ');
          try {
            meta = JSON.parse(metaStr);
          } catch (_error) {
            e = _error;
            console.log(e);
          }
          blog.meta = meta;
          if (blog.meta.summary) {
            BlogRemoteService.renderMarkdown(blog.meta.summary).then(function(data) {
              return blog.meta.summary = data;
            });
          }
          return blog;
        }
    • html 页面, 展示 blog list, 带 summary。如果不用 angularJS, 用handlebarsmustache也可轻松实现
    <m-loading ng-if="!vm.data.blogs"></m-loading>
    <div ng-if="vm.data.blogs" ng-repeat="blog in vm.data.blogs">
    <div style="cursor:pointer"
         ng-click="Util.redirect('/article/' + blog.number)">
      <h3 ng-bind="blog.title"></h3>
      <p class="summary" ng-bind-html="blog.meta.summary"></p>
      <span ng-bind="blog.created_at | date:'yyyy-MM-dd'"</span>>
    </div>
    <hr/>
    </div>
  3. 文章页面的实现

    <m-loading ng-if="!vm.data.content"></m-loading>
    <div ng-if="vm.data.content">
      <h2 class="align-center" ng-bind="vm.data.blog.title"></h2>
      <p
        ng-bind="vm.data.blog.created_at | date:'yyyy-MM-dd hh:mm:ss'"
        class="created-at"
      ></p>
      <br />
      <div ng-bind-html="vm.data.content"></div>
    </div>
    
    <br />
    <br />
    <hr />
    <p>欢迎扫码订阅公众号:</p>
    <img width="120" src="/image/qrcode_wechat.jpg" />
    <div
      ng-if="vm.data.blog.number"
      duoshuo
      data-thread-key="{{vm.data.blog.number}}"
    ></div>
  4. 关于 css

    css 主要是用的 bootstrap, 但是代码高亮是 copy from github, 代码在这里

  5. 使用多说评论百度统计jiathis 社会化分享

    需要到各自的网站上注册,得到相应代码

    以下为异步加载多说和百度统计的代码

    function addScript(src) {
      var el = document.createElement("script");
      el.src = src;
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(el, s);
    }
    
    // duoshuo
    var duoshuoQuery = {
      short_name: "martin-liu",
    };
    
    // baidu statistics
    var _hmt = _hmt || [];
    _hmt.push(["_setAutoPageview", false]);
    
    var scriptSrcs = [
      "http://static.duoshuo.com/embed.unstable.js", // duoshuo
      "//hm.baidu.com/hm.js?a67e974dea316e70836c07c3e3576a29", // baidu statistics
    ];
    
    for (var i = 0; i < scriptSrcs.length; i++) {
      addScript(scriptSrcs[i]);
    }

    另外,对于多说使用angular-duoshuo来支持 angularJS

    <div
      ng-if="vm.data.blog.number"
      duoshuo
      data-thread-key="{{vm.data.blog.number}}"
    ></div>

    百度统计, url 变化时触发

    $rootScope.$on("$routeChangeSuccess", function ($event, current) {
      if (_hmt) {
        return _hmt.push(["_trackPageview", $location.url()]);
      }
    });
  6. fork me on github

    https://github.com/blog/273-github-ribbons

  7. 使用locache做本地 cache, 减少 request 数量,提高用户体验。我设置为 5 分钟失效

    this.getWithCache = function (key, isSession, getFunc, timeout) {
      var cache, data, defer, promise;
      cache = Cache;
      if (isSession) {
        cache = Cache.session;
      }
      defer = $q.defer();
      data = cache.get(key);
      if (data) {
        defer.resolve(data);
        return defer.promise;
      } else {
        promise = getFunc();
        promise.then(function (data) {
          return cache.set(key, data, timeout);
        });
        return promise;
      }
    };
  8. push 到 github,等几分钟,一个新鲜的热乎乎的博客就出现了!

    以下是我的部署 script,因为有 build 过程(concat, uglify 之类)

    #!/bin/bash
    grunt build
    ( cd dist
    git init
    git add .
    git commit -m "Deployed to Github Pages"
    git push --force --quiet "https://github.com/martin-liu/martin-liu.github.io.git" master
    )

Next

还有一些问题没有解决,如

最后

可以看到,这是个非常简单的 blog,并不完善,但是 workable,可以在此基础上迭代开发。这一点相当重要,因为

Done is better than perfect.(完成更胜完美)
— facebook 标语