到 ICARUS 中去 —— 记对 Icarus 主题的使用及微调

到 ICARUS 中去 —— 记对 Icarus 主题的使用及微调

伊卡洛斯乘风飞跃迷楼,Icarus 倚水谈笑生风
写写迁移到 Icarus 后做的一点微小工作

动机

大约 16 年左右吧,我开了自己的个人博客,打算记录一下各种事情。从 Wordpress 到 Typecho,最终还是归於 Hexo。

在还用着 Typecho 的时候,发掘到了 Hexo-Theme-Typecho 这个主题。直到现在我也依然觉得它足够惊艳。后来作者转坑到了 Hexo 上。自然地,我也到了 Hexo 这个平台,继续用 Hexo-Theme-Material 这个主题,也误打误撞提交了一些 PR。慢慢地,包括作者在内的协作者团队全部解散,这个主题终於寿终正寝。虽说小修小补也不是不能用,但其过於臃肿的资源库是不可逆转的问题。自己小修小补又用了三年,还是换了吧。

这两三年也因为各种原因,写的博客没有传上来。还是留下自己看为好,就不上传啦。

这次换到了 Icarus,也在这记录一下对它做的一点调整和功能定制吧(一点微小的工作)

老规矩先分享一首歌 —— 陈奕迅《失忆蝴蝶》

改动概要

所改动的几乎都是一些实用但小的地方,具体胪列如下:

  • 新增 网站备案号
  • 新增 版权声明
  • 调整 文章发布时间的显示格式
  • 调整 黏性目录
  • 调整 Valine 评论的样式
  • 调整 个人资料卡片的按钮
  • 新增 对个人主页的 DNS-Prefetch
  • 新增 个人映像图库页面(开发中)
  • 新增 夜间模式(开发中)

改动详情

在自己动手前亦查阅过一些前辈们的资料,但几乎都是在 Icarus 2.0 时代写就的。2.0 版本的 Icarus 依赖是采用 .ejs 文件的,而作者在 3.0 版本采用了 .jsx 进行重构,因而这些教程已不再适用。

虽然代码结构已经天差地别,但大致思路仍然相似,因而写就了这篇文章以供参考吧。

新增 网站备案号

众所周知,若果希望在国内比较舒心地建站(如欲使用 CDN 或者 OSS 对象存储等服务),备案是必不可少的。

可惜的是,Icarus 並未集成该功能,所以需要自己加上。

源码改动胪列如下:

  • themes\icarus\layout\common\footer.jsx
themes\icarus\layout\common\footer.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 第 28 行  -  Line 28 -->
<div class="level-start">
<a class="footer-logo is-block mb-2" href={siteUrl}>
{logo && logo.text ? logo.text : <img src={logoUrl} alt={siteTitle} height="28" />}
</a>
<p class="size-small">
<span dangerouslySetInnerHTML={ { __html: `&copy; ${siteYear} ${author || siteTitle}` } }></span>
&nbsp;&nbsp;Powered by <a href="https://hexo.io/" target="_blank" rel="noopener">Hexo</a>&nbsp;&&nbsp;
<a href="https://github.com/ppoffice/hexo-theme-icarus" target="_blank" rel="noopener">Icarus</a>
<!-- 加入 start -->
<br/><a class="has-link-black-ter-2 -link" href="http://beian.miit.gov.cn/" target="_blank">粤ICP备XXXXXXXX号-1</a> <!-- 替换为你的备案号 -->
<!-- 加入 end -->
{showVisitorCounter ? <br /> : null}
{showVisitorCounter ? <span id="busuanzi_container_site_uv"
dangerouslySetInnerHTML={ { __html: visitorCounterTitle } }></span> : null}
</p>
</div>

新增 版权声明

版权声明不仅仅是对作者著作权的保护,同时亦保障用户可以更方便地进行转载、演绎等。更重要的是,以自身为起点逐步培养起全体社会的版权意识,由此促进文化的交流,看到思想碰撞产生的动人火花

Icarus 的作者已经将这项功能纳入 Todo 清单,相信不久的将来就能体验到。

此处我简要阐述下自己的实现思路:获取当前文章页信息,並在每篇文章页的最后位置添加模块,同时使用 CSS 对其进行样式渲染。

Icarus 内部采用的时间标记为 ISO 标准时间,其广泛采用於各种跨时区的应用系统中。同时这套系统兼顾了一些历史因素,了解后有用的知识增加了,详情参阅:

以一个 ISO 标准时间为例:2020-04-13T05:04:39.000Z。显然,其並不适合直接显示在前端上,因而我们需要对其进行处理。我的解决思路是将其转化为一个 Date 对象,再使用其自带的方法对其进行格式化输出。

由于 JavaScript 的 Date 类型可以接受 ISO 标准时间为参数,当对象新建完毕后,就能够对其进行相应的操作了。这里给出一点关於 Date 类型的方法:

方法描述
getDate()从 Date 对象返回一个月中的某一天 (1 ~ 31)
getDay()从 Date 对象返回一周中的某一天 (0 ~ 6)
getMonth()从 Date 对象返回月份 (0 ~ 11)
getFullYear()从 Date 对象以四位数字返回年份
getTimezoneOffset()返回本地时间与格林威治标准时间 (GMT) 的分钟差
toUTCString()根据世界时,把 Date 对象转换为字符串
toLocaleString()根据本地时间格式,把 Date 对象转换为字符串
toLocaleTimeString()根据本地时间格式,把 Date 对象的时间部分转换为字符串
toLocaleDateString()根据本地时间格式,把 Date 对象的日期部分转换为字符串

更多方法请参阅 W3School - JavaScript Date 对象

惟须改动若干处,源码改动胪列如下:

  • themes\icarus\layout\common\article.jsx
themes\icarus\layout\common\article.jsx
1
2
3
4
5
6
/* 第 27 行  -  Line 27 */
const indexLaunguage = config.language || 'en';
const language = page.lang || page.language || config.language || 'en';
/* 加入 start 关於时间的格式下文将详细解释 */
const DateUTC = new Date(date_xml(page.date)); // 传入一个 ISO 时间作为参数生成一个 Date 对象
/* 加入 end */
themes\icarus\layout\common\article.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 第 81 行  -  Line 81 -->
{/* Content/Excerpt */}
<div class="content" dangerouslySetInnerHTML={ { __html: index && page.excerpt ? page.excerpt : page.content } }></div>
<!-- 加入 start -->
{/* Copyright */}
{!index ? <div>
<ul class="post-copyright">
<li><strong>本文标题:</strong><a href="">{page.title}</a></li>
<li><strong>本文作者:</strong><a href="https://uiharu.top">Kitcham</a></li>
<li><strong>本文链接:</strong><a href="">https://blog.uiharu.top{url_for(page.link || page.path)}</a></li> <!-- 此处替换为自己博客的 URL -->
<li><strong>发布时间:</strong>{date(page.date)} {DateUTC.toTimeString()}</li> <!-- 关於时间的格式下文将详细解释 -->
<li><strong>版权声明:</strong>如非特别声明,本博客文章均依据 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="external nofollow" target="_blank">CC BY-NC-SA 4.0</a> 协议发行。详情请参阅<a href="https://blog.uiharu.top/CC-4-0" rel="external nofollow" target="_blank"> 相关说明 </a></li>
</ul>
</div>: null}<br/>
<!-- 加入 end -->

实际上可见,我仅仅是使用了 DateUTC.toTimeString() 方法获取时间,而年月日的获取则是采用 themes\icarus\layout\common\article.jsx 中自带的时间生成方法 {date(page.date)},通过二者的拼接生成形如 2020-04-13 13:04:39 GMT+0800 (中国标准时间) 的时间标签。

好了,到这里,版权模块的添加已经完成,剩下的就是采用 CSS 样式表对其进行渲染了。

  • themes\icarus\source\css\style.styl
themes\icarus\source\css\style.styl
1
2
3
4
5
6
7
8
9
10
/* 第 19 行  -  Line 19 */
.post-copyright {
font-size: 1rem;
letter-spacing: 0.02rem;
word-break: break-all;
margin: 2.5rem 0 0;
padding: 1rem;
border-left: 3px solid #3273dc;
background-color: #f9f9f9;
}

实际上,.styl 文件的本质是一个公共样式表。简单而言即是一个抽象的 CSS 样式表模板,因而直接书写 CSS 代码亦无任何问题。

做完这些工作后,就可於文章页末尾处看到以下样式的版权声明:


调整 文章发布时间的显示格式

Icarus 自带的文章时间解析器会显示为一个约数,如几日前,3年前等。这无论对作者抑或是读者都是一个不太友好的设计。

一开始我认为时间格式是在渲染阶段就已经确定好的了,於是乎我翻遍了所有渲染代码,也未曾发现对应的代码。最后,我用 Chrome 查看生成好页面的源代码,发现关於时间的标识是这样的:

  • https://blog.uiharu.top/index.html
https://blog.uiharu.top/index.html
1
2
3
4
<div class="level-left">
<time class="level-item" dateTime="2020-08-13T03:55:24.000Z" title="2020-08-13T03:55:24.000Z">2020-08-13</time>
<span class="level-item"><a class="link-muted" href="/categories/technical-repo/">技术录</a></span><span class="level-item">13 分钟 读完 (大约 1983 个字)</span>
</div>

嗯~ o( ̄▽ ̄)o???好像有甚么地方不对。

你确实没看错,源码里的时间格式是 2020-08-13 这样很人性化的,但为什么显示出来就是几日前,几年前。我初步判断,应该是某个 JS 函数控制了这个字段。於是我带着目标寻找这个函数,最后证实了在 main.js 中存在这个转换函数。

该函数事先获取了当前本地时间,再与文章页标签中的 ISO 标准时间进行比较,转换为一个时间约数。

  • themes\icarus\source\js\main.js
themes\icarus\source\js\main.js
1
2
3
4
5
6
/* 第 19 行  -  Line 19 */
- if (typeof moment === 'function') {
- $('.article-meta time').each(function() {
- $(this).text(moment($(this).attr('datetime')).fromNow());
- });
- }*/

此处仅需将该函数注释或删除即可显示正常时间格式。

调整 黏性目录

因 Icarus 自带的目录並不具备黏性,在 PC 等宽屏终端浏览时,目录会随之滚动。细思一下,还是将目录设定为黏性较好,便於读者随时切换章节。

Icarus 的作者将 Icarus 相关挂件插件剥离,存放於 node_modules\hexo-component-inferno\lib\view\widget 中,便於移植及更新,同时缩减主题的体积。而这与 2.0 版本的 Icarus 主题目录结构相异甚远。

此处需对两个文件进行调整,胪列如下:

  • Hexo\node_modules\hexo-component-inferno\lib\view\widget\toc.js
Hexo\node_modules\hexo-component-inferno\lib\view\widget\toc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 第 195 行  -  Line 195 */
{
key: "render",
value: function render() {
var toc = getToc(this.props.content);

if (!Object.keys(toc).length) {
return null;
}
- return (0, _inferno.createVNode)(1, "div", "card widget", (0, _inferno.createVNode)(1, "div", "card-content", (0, _inferno.createVNode)(1, "div", "menu", [(0, _inferno.createVNode)(1, "h3", "menu-label", this.props.title, 0), this.renderToc(toc)], 0), 2), 2, {
+ return (0, _inferno.createVNode)(1, "div", "card widget is-3-column-toc", (0, _inferno.createVNode)(1, "div", "card-content", (0, _inferno.createVNode)(1, "div", "menu", [(0, _inferno.createVNode)(1, "h3", "menu-label", this.props.title, 0), this.renderToc(toc)], 0), 2), 2, {
"id": "toc"
});
}
}]);

修改好 JS 之后,还需对 CSS 样式表进行处理,仅在宽屏设备上开启黏性目录,以免在手机等设备上因列数为 1 而无法正常弹出目录。

  • themes\icarus\source\css\style.styl
themes\icarus\source\css\style.styl
1
2
3
4
5
6
7
/* 第 29 行  -  Line 29 */
.is-1-column-toc /* 如当前列数为1,则目录采用原来的样式 */
.is-2-column-toc, .is-3-column-toc /* 如当前列数为2或3,则目录采用粘性布局 */
position: sticky
position: -webkit-sticky
top: 0
z-index: 1

调整 Valine 评论的样式

如惟须调整默认文字提示、表单项等,请参照 Valine 配置文档 在 Icarus 配置文件中配置相应项目即可。

以调整默认文字提示为例,修改如下:

  • themes\icarus\_config.yml
themes\icarus\_config.yml
1
2
3
4
5
6
7
/* 第 141 行  -  Line 141 */
comment:
...
required_fields: ["nick", "mail"] # 可选填
- placeholder: Just Go Go.
+ placeholder: Endless interflow - 可使用 Markdown 评论
...

调整 个人资料卡片的按钮

因个人资料卡片的“关注我”似乎对我用处不大,因此修改成“关于我”吧。

此处亦需对文件进行调整,胪列如下:

  • themes\icarus\languages\zh-CN.yml
themes\icarus\_config.yml
1
2
3
4
5
6
7
8
/* 第 19 行  -  Line 19 */
...
widget:
- follow: '关注我'
+ follow: '关于我'
recents: '最新文章'
links: '链接'
...

可谓是这里最简单粗暴的修改了。

新增 对个人主页的 DNS-prefetch

在 HTTP 1.0/1.1 协议中,浏览器的並发请求数量是受到限制的。於此同时,用户打开一个新页面后,势必会先浏览当前页面,再行点击打开新页面。而其中的“空白时间”如果不加以利用,则显得略微浪费了。

为对我个人主页和博客之间的跳转体验进行提升,我采用了 DNS-prefetch
域名的 DNS 解析是需要耗费一定时间的,通常约为 10-120 ms 左右。如果我们可以如上所述对“空白时间”加以利用,则可以免于发送 DNS fetch 请求从而节省掉这 10-120 ms,一定程度上提升用户的浏览体验。这个加以利用的方法即采用 DNS-prefetch

考虑到前端需要考虑不同浏览器的兼容问题,我们需要调查 DNS-prefetch 对不同浏览器的兼容性。那么,甚么浏览器可以支持该特性呢?我可以用 DNS-prefetch 提升用户的浏览体验吗?下图是一个来自 Can I use - Dns-Prefetch? 的兼容性报告,其列出了主流浏览器对其的兼容性。

Can I use Dns-Prefetch?

可见,绝大多数浏览器甚至 IE ~都能够兼容,那么马上开始动工。

此处惟须修改一处地方:

  • themes\icarus\layout\common\head.jsx
themes\icarus\_config.yml
1
2
3
4
5
6
7
8
9
10
11
12
/* 第 150 行  -  Line 150 */
...
{canonical_url ? <link rel="canonical" href={canonical_url} /> : null}
{rss ? <link rel="alternative" href={url_for(rss)} title={config.title} type="application/atom+xml" /> : null}
{favicon ? <link rel="icon" href={url_for(favicon)} /> : null}
<link rel="stylesheet" href={iconcdn()} />
{hlTheme ? <link rel="stylesheet" href={cdn('highlight.js', '9.12.0', 'styles/' + hlTheme + '.css')} /> : null}
<link rel="stylesheet" href={fontCssUrl[variant]} />
<link rel="stylesheet" href={url_for('/css/' + variant + '.css')} />
+ <link rel="dns-prefetch" href="https://uiharu.top" /> /* 修改为你需要 Prefetch 的 URL */
<Plugins site={site} config={config} helper={helper} page={page} head={true} />
...

仅需简单地添加 <link rel="dns-prefetch" href="https://example.com/">,你就可以感受到 DNS-Prefetch 的强大。

新增 个人映像图库页面

开发中 - Developing…

请参照 印记 | 无垠映像 - Boundless Image

新增 友链页面夜间模式

请参照 Kitcham的友链页面没人比我更懂方便 —— 透过 Github Action 优雅生成友链数据为 ICARUS 而生的友情链接页面

新增 夜间模式

开发中 - Developing…

结语

虽说 .ejs.jsx 均为 JavaScript 的扩展,但二者的确是性格迥异。在这里感谢一些前辈们资料的帮助。接下来的时间就让我继续慢慢钻研 .jsx 吧。

参考资料

  1. 白眼图图的回答 - js在new date(“1947-07-27”)时为什么会发生时区错误?
  2. Wiki - 中國時區
  3. W3School - JavaScript Date 对象
  4. Can I use - Dns-Prefetch?
  5. Icarus 官方文档
  6. 旺阳的博客

到 ICARUS 中去 —— 记对 Icarus 主题的使用及微调

https://blog.uiharu.top/archives/use-and-tune-for-hexo-icarus-theme.html

作者

Kitcham

发布于

2020-08-13

更新于

2020-09-02

评论