一个有趣的单页面博客

昨日 mmmw 的博客加入了 V2MM 的专栏(欢迎!),他的博客简洁至极,就像 Hexo 一样也是部署在 Github Pages 上,不同点是他的文章写在 Github Issues 里,评论也在 Issues 里,然后通过 Github api 获取文章内容和评论展示在页面上。还有趣的一点是他的整个博客就是一个单页面web应用。如此简洁有趣的博客所以我忍不住想写一篇博文来介绍。

picture

打开他的博客你可以发现他是通过URL中的#号来标识和导航每篇博文的,#号是单页面web应用的常用导航方式,当年 Twitter 和 Facebook 都使用过这种方式导航(#搭配!一起使用效果更佳哦)。了解 Angularjs 的开发者在使用 Angular 默认搭建单页面应用程序的时候也是通过 # 来导航的。当#号在互联网上的应用越来越广的时候,Google等搜索引擎也对 # 号导航的页面内容进行了特殊的优化已获取 ajax 内容。

那么#号到底是什么呢? 为什么用它来做单页面应用? 原因介绍如下:

  • #代表网页中的一个位置 #号后面的字符,就是该位置的标识符。比如:http://www.example.com/index.html#print 就代表网页index.html的print位置。浏览器读取这个URL后,会自动将print位置滚动至可视区域。 为网页位置指定标识符,有两个方法。一是使用锚点,比如<a name="print"></a>,二是使用id属性,比如<div id="print" >
  • HTTP 请求会忽略 # 后面的内容, 所以 # 导航的内容需要通过 ajax 获取。
  • 改变#后面的内容不触发网页重载,却会在浏览器的历史记录里增加记录,这一条重要的特性为单页面应用提供了极大的便利。
  • 改变#后面的内容会触发 onhashchange 事件,通过监听这一事件就可以更改页面上的内容了。

所谓单页面应用嘛其实就是在第一次打开页面的时候加载了页面框架,其他的内容在浏览的时候都是通过 ajax 载入的,在浏览器看来整个网站就好像只有一个页面似的,而对用户来说访问这种网站就好像使用本地的app一样,速度快,页面不会整个刷新,是一种极好的体验呢。而且对于 mmmw 的博客来说,他也不用担心 Google 爬虫无法索引,因为他的内容保存在 Github 上,Google 可以通过 Github 爬取,不得不说是一种很好很简洁的博客。

昨日在用 Blog Comments2 这个插件集成进他的博客的时候,不得不针对这种单页面博客做了一点改进。

首先在插件载入的时候循环等待文章节点的载入(每秒钟检测一次),其次是监听 hashchange 事件重新载入评论系统。代码如下,目前来看是有效的,只是hashchange 事件只支持 IE8 以上的现代浏览器,大概也不是重大问题吧,大家有其他的办法吗?

update: mmmw 的博客使用的工具是 Mirror: A blog tool powered by GitHub issues ,在阅读了它的源码后更新了一次我们的博客评论系统的脚本,如下:

    var _times = 0;
    function _runtime() {
        _times += 1;
        if (_times <= 60) { // waiting at most for 60 seconds.
            console.log('[v2mm] waiting for markdown body ('+_times+')...');
            setTimeout(function() {
                if (document.getElementsByClassName('markdown-body')[0]) {
                    console.log('[v2mm] markdown body is ready.');
                    loadScript(nbb.url + '/plugins/nodebb-plugin-blog-comments2/lib/common.js', function () {
                        run();
                        window.addEventListener("hashchange", function () {
                            run();
                        }, false);
                    });
                } else {
                    _runtime();
                }
            }, 1000);
        } else {
            console.log('[v2mm] waiting timeout... something went wrong, please contact V2MM.TECH, so much appreciated!');
        }
    }

    if (location.hash) {  // article page
        _runtime();
    } else {  // list page
        window.addEventListener("hashchange", _runtime, {once: true}, false);
    }

参考文章: