martin-liu/martin-liu.github.io

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

Closed this issue · 19 comments

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

以下为旧文

前言

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

方法并不高端,但是:

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

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

原理就一句话

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

基本介绍

  • github pages

    • Github 提供的托管静态网页的服务,基本使用方法是建立一个名为YOUR_USER_NAME.github.io的repo, 并把代码push到master branch。
    • 注意其只支持静态内容
    • 另外,如果你有自己的域名,也可以将域名指向github pages
  • github issues

    每个github repo自带的tracking系统,支持markdown, 代码高亮,图片,表格,emoji表情

  • github API

    Github提供的API, 可以拿到你的issues内容,可以render markdown... 更多请看文档

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

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

  • Github issues并不是为作为博客而设计的,博客平台的很多功能,比如推荐、SEO等都是没有的
  • 你将受限于github的UI和用户(需要注册才能评论),无法自由的定义你想要的UI和交互

而使用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

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

  • RSS
  • SEO

最后

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

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

您好,请问这个博客的内容编写必须在线上编写嘛? 就说打开issue开始写作?我有个需求就是在本地写好,然后push到issues中,可以实现吗?

@JimmyLv 可以call github的API,https://developer.github.com/v3/issues/#create-an-issue, edit也可以 😃

像你这个需求,需要自己写点代码,比如你用的编辑器的插件,或者简单点写个shell script来post/update issues

@martin-liu 我找了一下sublime的插件,并没有,啊呜。

我把sublime配成了这样,太适合写作了~

@martin-liu 又找了。。。。https://packagecontrol.io/packages/TicketMaster 所以有个问题就是,任何人都可以在你这个blog里面写内容吗?issues是大家都可以提的呀。

@JimmyLv 所以你要判断issues的owner,只有owner是自己,并且符合某种条件(比如tag)才认为是一篇博文。

这个插件如果好用的话应该就解决所有问题了,如果不好用,你可以改改code或者自己写一个

大神,你这个教程有点复杂啊。

你能给封装成gist的Embed类似的方式吗? 一行js插入 issue

大佬,你这个博客fork过来了 怎么用啊 写自己的文档。。。

@syavingc readme里有介绍: https://github.com/martin-liu/martin-liu.github.io , 你需要install nodejs, 然后npm install -g grunt, 再follow README就可以了。
不过好几年过去了,可能会有其它问题 😅

@JimmyLv @xu-song @syavingc
鉴于录续有人问到这个project,且这个project的code已经严重落后时代了,所以我这两天抽时间用angular 6 + ng-zero 重写了一遍.

Follow instruction 即可直接使用

顶楼主一波
按照楼主的教程,用vue搭建了自己的个人博客https://pma934.github.io
基本功能算是有了,不过ui还没弄好。
还有就是感觉github的markdown api的效果不太好,想直接下载个解析效果好的markdown模块,不知道有没有推荐

@pma934 如果是觉得style不好,你可以自己改css的。你也可以试试https://github.com/markdown-it/markdown-it , 支持highlight, emoji等等

@martin-liu 谢谢大佬,之前忘了使用gfm模式,导致p标签里面没有br标签。不过,github那个api有访问限制,访问过于频繁ip会被限制,所以我换成了https://github.com/markedjs/marked
这个😁。大佬说那个我也去试试,看看哪个性能好点。顺便问一下大佬知不知道怎么实现留言板之类的。

@pma934 API访问限制,可以通过本地cache (session storage)来减少call number。

留言板我现在的实现是通过github API拿issue下的comments,然后render在文章下边,比如http://martin-liu.github.io/#/article/4 , code在这里

@martin-liu
session storage没用过,我下午看看。感觉我现在那个博客有时候加载挺慢的,说不定能用上
大佬那个方法我看了的,我的意思是如果可以不用render,而是直接在本页面放对话框就好了

@pma934 你可以考虑用Disqus(可能被�墙了)。

不过我觉得github comment是比较好的方案,不用再考虑存储问题,直接在issue里讨论也比较geek一点。
如果你想用github comment, 但又想本页面提交,也不是不行,但是访问者需要登录github才能写comment, 你可以做个check, 如果登录了github就给个框框写,没有就提示要登录github

@martin-liu 我按照https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/ 的流程来实现登录功能,在开发环境下成功了,但是在build后出现了问题。
a y b1fqd2 t lsgr4 79f
tlmzqgut r u jhfisoiwph
b5 19g7gsckp4 igcrr 7
我明明在Network收到了access_token的值,但是在console里却报错了,大佬你知道是怎么回事么

@pma934 这个是cross domain issue, 需要CORS support. 本质上来说就是github禁止你从其它domain通过ajax来访问这个url

你需要用api.github.com而不是github.com, 前者enable了CORS. see https://developer.github.com/v3/oauth_authorizations/ or https://developer.github.com/v3/auth/

@martin-liu 仔细又看了https://developer.github.com/v3/oauth_authorizations/https://developer.github.com/v3/auth/页面的api ,还是没找到 api.github.com 里可以替代https://github.com/login/oauth/authorize的api。最后看到https://segmentfault.com/a/1190000011100934?utm_medium=referral&utm_source=tuicool 这篇文章,用cors-anywhere的反向代理api解决了跨域问题。
不过还是谢谢大佬的帮助

您好,请问这个博客的内容编写必须在线上编写嘛? 就说打开issue开始写作?我有个需求就是在本地写好,然后push到issues中,可以实现吗?

可以啊