Abstract

  2017年从自豪地采用WordPress迁移至Hexo,但是Hexo基于Node.js,安装了一些额外的插件后,依赖关系过于复杂,随着Node.js和Hexo版本的更新,带来了更多的兼容问题。相比之下Hugo虽然不同版本之间也会存在一些兼容性问题,但是单一执行文件方便太多了,而且也不至于像Hexo那样特定的Hexo版本需要使用特定的Node.js版本。

  另外Hugo的html模板引擎在使用上和pongo2比较类似,相较于Hexo使用的JADE,个人还是在这种类Django模板引擎上比较有使用经验。

  基于生命在于折腾的原则,额外做了一些个性化修改与优化,同时也部署到了Cloudflare Pages上,除了isso评论后端还部署在VPS上,其余静态资源都属于各种白嫖。

Jane主题个性化修改

  在JaneEven两个主题中最终选择了Jane,其实这两个主题大体上的布局是类似的,不过Even界面有点太空白了,所以最后选择了Jane。

扩大文章页面宽度


  1080p屏幕,win10自定义缩放124%的场景下,屏幕长度在1532px左右,然而其中只有720px用来显示内容,占比不到一半。当然,如果屏幕是纵向的话没这个问题,但是大部分应该还是横向,因此修改css稍微放大宽度,观感会好一些。
jane/assets/sass/_partial/_post.scss文件修改.post部分:

1
2
3
4
5
6
.post {
  padding: 3em 2.4em;
  margin: 0 2.4em 3em;

  @import '_post/header';
  ···

注意修改assets/目录里的内容后,需要使用hugo_extended版本进行构建,生成最终的jane.css文件。
修改后文章宽度为907px(907px/1532px=0.592≈黄金分割比0.618

交换上一篇与下一篇链接

  大多数主题都有一个奇怪的问题,比如三篇文章按发布时间从先到后排序为1、2、3,结果在浏览第2篇时,下一篇的链接是1,而上一篇的链接是3。
  按常理来说1的下一篇当然是2,2的下一篇当然是3。但默认构建出来的结果却是1没有下一篇,1的上一篇是2,2的上一篇是3,完全反了。 修改jane/layouts/post/single.html模板Post Pagination部分:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- Post Pagination -->
<nav class="post-nav">
  {{ with .PrevInSection }}
    <a class="prev" href="{{ .RelPermalink }}">
      {{/* icon left */}}
      <i class="iconfont">
        {{ partial "svg/left.svg" }}
      </i>
      <span class="prev-text nav-default">{{ .Title }}</span>
      <span class="prev-text nav-mobile">{{ i18n "prev_post" }}</span>
    </a>
  {{- end }}
  {{ with .NextInSection }}
    <a class="next" href="{{ .RelPermalink }}">
      <span class="next-text nav-default">{{ .Title }}</span>
      <span class="next-text nav-mobile">{{ i18n "next_post" }}</span>
      {{/* icon right */}}
      <i class="iconfont">
        {{ partial "svg/right.svg" }}
      </i>
    </a>
  {{- end }}
</nav>

  原先NextInSection对应prev_postPrevInSection对应next_post,不懂为什么要反过来。想想应该是因为现在的信息流如weibo、twitter、朋友圈等的时间线都是最新的在头部,next对应的确实是时间较久远的数据。但是文章尤其是带章节的还是不太适用这种逻辑。

使用instant.page提升响应速度

  instant.page是一个js库,它的前身是instantclick,作者都是同一个人。原理很简单,人类的反应时间基本都在数百毫秒,也就意味着从鼠标移动到一个链接上,然后再点击下去的间隔至少在百毫秒以上。因此可以在鼠标移动到某个链接上时,就去进行请求并将应答缓存在本地,在实际点击后,就能立即从本地缓存加载数据。这样子,即使源站在美国西海岸,到中国有着140ms左右的网络物理延迟,也能在浏览体验上达到极致的响应速度。


  介绍页面提供了一个测试按钮,移动到这个按钮上并且用最快的速度点击下去,能够测试出对应的耗时。正常的浏览行为一般都会在链接上悬停更长的时间,达到300ms以上。
  也可以直接在下面这个按钮上测试:


  instant.page和instantclick的原理是一致的,但是实现方法不一致。大致了解了一下,instantclick是使用ajax,将整个网站变成一个单页应用,在做页面绘制替换时,有些js就会存在兼容性问题,比如某个js只能加载一次,但是基于ajax替换就可能导致同一个js在同一个页面被加载多次,导致异常。另外我在测试中发现instantclick还存在一些别的小问题,作者最终也弃坑重新开发了instant.page。
  而instant.page是基于prefetch,Chrome从61版本开始支持该特性,Firefox老早就支持了,Safari则是从14.0开始才默认支持。prefetch的实现逻辑下,浏览器只是预先下载html页面进行缓存,点击链接后依旧会进行页面刷新,因此不存在js的兼容性问题。对于不支持该特性的浏览器也不会有任何影响。


当鼠标移动到链接上后,就已经开始请求链接内容,点击链接后,直接从prefetch cache返回内容。


修改jane/layouts/partials/scripts.html模板,在最后面加上instantpage控制逻辑:

1
2
3
{{ if .Site.Params.instantpage.enable }}
  {{ .Site.Params.instantpage.instantpageJS | safeHTML }}
{{ end }}

在配置文件中配置对应的参数:

1
2
3
[params.instantpage]
  enable = true
  instantpageJS = '<script src="https://cdn.jsdelivr.net/npm/instant.page@5.1.0/instantpage.min.js" type="module"></script>'

增加isso comments

  isso是一个开源的基于Python+SQLite的评论系统,Jane虽然支持一堆评论框架,如DisqusGitment等等,但是没有isso,因此需要自己添加。 修改jane/layouts/partials/comments.html模板,在末尾引用对应的css和js文件,再添加一个section就可以了:

1
2
3
4
5
{{ if .Site.Params.isso.enable }}
  {{ .Site.Params.isso.css | safeHTML }}
  {{ .Site.Params.isso.js | safeHTML }}
  <section id="isso-thread" class="content-post-comments"></section>
{{ end }}  

在配置文件中可以设置具体的css和js路径:

1
2
3
4
[params.isso]
  enable = true
  css = '<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/ggggle/cdn/jane/css/isso.min.css">'
  js = '<script data-isso="https://comments.xuanyu.li/isso" data-isso-css="false" src="https://cdn.jsdelivr.net/gh/ggggle/cdn@master/isso/embed.min.js" data-isso-reply-to-self="true" data-isso-require-author="true" data-isso-require-email="true" data-isso-max-comments-top="10" data-isso-max-comments-nested="5" data-isso-avatar="true"></script>'

增加Telegram图标

  Jane主题没有附带Telegram.svg图标,有Pull requests添加了,但是因为进行了CrLf转换,导致一堆文件显示变更,所以没合入。提取其中的图标放在/jane/layouts/partials/svg/目录下,文件名为telegram.svg,然后在[params.social]中配置Telegram链接即可。

CDN拉满


  静态资源全部CDN,包括html、css、js、img,其中html主体部署在Cloudflare Pages上,其余静态资源均使用jsdelivr CDN,通过GitHub仓库或者npm公共仓库获取。感谢Cloudflare和jsdelivr提供优秀又免费的CDN
  需要注意的是Cloudflare pages在使用自定义域名后,默认不会在边缘节点缓存html,详见Cloudflare默认缓存规则,同时浏览器缓存也不会生效,不过可以通过创建自定义页面规则解决。


  可以看到默认情况下max-age=0,浏览器端不会进行缓存;另外cf-cache-status: DYNAMIC,表示Cloudflare内部进行了回源请求。后面这个回源实际上影响不大,因为源站就在Cloudflare Pages上,但是前面的max-age=0影响了浏览器缓存,不修改的话上面的instant.page优化就没用了。

  进入Cloudflare控制面板创建对应域名的页面规则,开启浏览器缓存并设置缓存级别为所有内容,需要的话可以把边缘缓存也打开。


  添加页面规则后,第一次访问显示cf-cache-status: MISS,表示边缘缓存中未找到对应资源,因此会进行回源请求。


  第二次访问显示cf-cache-status: HIT,在边缘缓存中找到了对应的资源。同时max-age=1800,30分钟内浏览器端的缓存是有效的,后面再请求直接从本地缓存返回了,对于静态网站来说足够了。

写在最后

  至此,一个大部分基于白嫖资源的站点就迁移完成了,响应速度上也比之前基于Hexo的快很多。有朝一日再把isso部署到SaaS上就完全没有自部署后端了,可惜现在Cloudflare Workers免费版还不能使用耐用对象,只能部署一些无状态的服务。
  遥想2015年也是白嫖阿里云学生计划的云虚拟主机,附送了免费的MySQL实例,搭建了WordPress,还还在万网做了域名备案,使用七牛的CDN。当时LNMP正火热,现在已经是容器、服务治理的天下,能白嫖的资源也是越来越多,Oracle CloudCloudflare谷歌云等等。

2021-08-12 update

  后续还是从Cloudflare Pages迁移到了vps上,这段时间测试下来cf pages有些时候TTFB太过缓慢,有些时候甚至能到十多秒,CN2 GIA线路还是稳。