作者:刘欢
前言
最近写了一个简单的博客并放在github上,在此详述一下细节,以为分享。
方法并不高端,但是:
1. 简单易行不要钱。
不需要数据库,不需要服务器,不需要域名
,因为github都帮我们做了,壮哉我大github!
2. 完全自定义的纯HTML/JS/CSS代码。不需要学各种static sitegenerator的玩法,又能实现独一无二的个人博客。
好了,废话稍止,进入正文。
原理就一句话
写前端代码
并host在github pages, 利用githubissues做为后台,通过githubAPI完成前后端交互。
基本介绍
- Github 提供的托管
静态网页
的服务,基本使用方法是建立一个名为YOUR_USER_NAME.github.io
的repo, 并把代码push到master
branch。 - 注意其只支持
静态内容
。 - 另外,如果你有自己的域名,也可以将域名指向github pages
- Github 提供的托管
Github提供的API,可以拿到你的issues内容,可以rendermarkdown...更多请看文档
为何不直接使用issues作为博客
事实上,直接使用issues作为博客也是可行的,从这个角度,就是把github issues当成博客平台。
这个方案的缺陷是:
1. Githubissues并不是为作为博客而设计的,博客平台的很多功能,比如推荐、SEO等都是没有的
2. 你将受限于github的UI和用户(需要注册才能评论),无法自由的定义你想要的UI和交互
而使用github API来构建nobackend 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,如用其它框架实现,按同样的原理来就是:
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页面的实现
- 拿issues内容 我的api link为 https://api.github.com/repos/martin-liu/martin-liu.github.io/issues?labels=blog&page=1&per_page=10&state=open
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
可以看到,文章内容有一段注释,里面是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, 用handlebars或mustache也可轻松实现
<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. 文章页面的实现
- 拿文章内容 我的api link为https://api.github.com/repos/martin-liu/martin-liu.github.io/issues/3?labels=blog&page=1&per_page=10&state=open ,
javascript :
BlogRemoteService.getBlog(id).then((function(_this) { return function(blog) { if (blog.body) { _this.data.blog = BlogService.decorateBlog(blog); BlogRemoteService.renderMarkdown(blog.body).then(function(ret) { return _this.data.content = ret; }); return $rootScope.blog = _this.data.blog; } }; })(this));
post
blog.content
到
https://api.github.com/markdown
- html
<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,但是代码高亮是copyfrom 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标语