今天晚上直播 Hexo 主题初入门,主要目标是将我的 Typecho 老牌主题 Single 移植 Hexo,也就是 GitHub 上我新开的 Hingle 仓库。什么?你竟然不知道我在 B 站直播写代码?还不 赶紧关注 ?
打开 Hexo 官网,进入眼帘的第一步,就是首先安装需要的命令行工具。
一键安装
简单几条命令即可完成,hexo-cli
可以理解成方便你初始化和管理一个 Hexo 博客的命令行工具。
yarn global add hexo-cli
hexo init Hexo-Playground
cd Hexo-Playground
hexo server
如果过程出现错误,那大部分是因为 GitHub 访问不上导致的(阿里云的 Windows 服务器上死都拿不到,挂全局代理也不行,太坑了,也不知道为什么)
EJS
编写 Hexo 模板使用的是 EJS 语法,模板里面主要用到的写法,大概有这些:
<%= date() %> // 转义输出表达式返回的内容(纯文本)
<%- paginator() %> // 直接输出表达式返回的内容(一般是 HTML)
// JS 逻辑块(不得不说,这种写法下只能把 else 和两括号放一行了)
<% if (true) { %>
<% } else { %>
<% } %>
虽然没特别学习过 EJS,但总体来说和 PHP 还是大同小异,并不是很困难。
主题架构
由于没有任何 Hexo 的开发经验,于是就从官方的文档一步一步开始看起。定位到 Hexo 目录的 themes
,创建一个文件夹,这里以 hingle
为例。官方文档 的描述我就不详细扯了,以下是我对主题结构的个人理解和汇总:
如果你写过 Next.JS 项目,可以把layout.ejs
理解成_document.tsx
生成网站根结构。它放在layouts
文件夹里面,是不是只看官方文档的话就会很懵逼呢?
- source(前端静态文件)
- languages(国际化,暂无需求先忽略)
- layouts(布局相关模板)
--- _partial(内容切片)
------ head.ejs(页头、CSS、Link 等标签)
------ header.ejs(页头,各种菜单等)
------ footer.ejs(页尾和 JS)
--- layout.ejs(放 HTML,根架构)
--- post.ejs(文章模版)
--- page.ejs(页面模板)
--- index.ejs(首页模版)
--- archive.ejs(归档页面模板)
这个架构我感觉 官方文档 也没详细说清楚,尤其是 layouts
文件夹里面要有个 layout.ejs
这个情况,我还是参考了 官方模板 才得出的结论。
期间我还错误的将 layouts.ejs
的内容放到了 index.ejs
里面(虽说刚开始也能用,但其实后续加了模板之后你会发现是错的)。
不得不说,官方中文文档的质量真的很一般。
引入主题资源
在输出页面之前,我需要引入主题的样式和 JS,分别放在 Header 和 Footer 里面,使用内置函数 css()
和 script()
函数引入即可,支持字符串或数组形式。
<!-- Head -->
<head>
<meta charset="UTF-8">
<title><%= page.title %></title>
<%- css("static/kico.css", "static/hingle.css") %>
</head>
<!-- Footer -->
<%- js(["static/kico.js", "static/hingle.js"]) %>
在我这个架构里面,head.ejs
输出 head
标签的内容,header.ejs
里面输出 header
,是我比较常用的方式。虽说内置的 partial()
函数可以传参数实现「模块化」管理,实现各个小模块的切片(很像 React/Vue 的组件概念),但我现阶段还是以化繁为简为主,前期方便理解一些。
变量替换
随后就是各个页面的函数替代工作,需要将 PHP 的输出换成对应的 EJS 语法。Hexo 的核心变量我们经常用到的,大概就这些:
名称 | 用途 |
---|---|
site | 网站变量 |
page | 页面变量(包括文章) |
config | 网站配置(Hexo 路径下的 _config.yml) |
theme | 主题配置(使用主题路径下的 _config.yml) |
首页
首先是网站首页,这个页面主要是将博文进行遍历和输出,这里使用 page.posts
全局变量完成。这个变量返回多个子对象,可以拿到文章的 title
categories
path
excerpt
date
等属性。
首页才有 page.posts
对象,如果是文章页面,这个对象估计就是不存在的了,所以需要注意判断和使用
- config.title 站点名称
- config.description 站点介绍
page
变量在不同页面情况下的值不相同,这个需要注意。这块 官网文档 都有详细说明,对照着来就行。
文章页
文章页有一处用户自定义的选项,叫做「作者信息」。这块需要使用到主题 theme
变量,这需要将主题路径的 _config.yml
配置起来才行。
author: 'Paul'
author_avatar: ''
author_text: ''
对应的输出:
<% if (theme.author && theme.author_text) { %>
<section class="post-author">
<% if (theme.author_avatar) { %>
<figure class="author-avatar">
<img src="<%- theme.author_avatar %>" alt="<%= theme.author %>" />
</figure>
<% } %>
<div class="author-info">
<h4><%= theme.author %></h4>
<p><%- theme.author_text %></p>
</div>
</section>
<% } %>
代码片段
代码片段,就是前面提到的 _partial
文件夹下的 Header Footer 了。这里面用到的属性基本上和前面提到的大同小异,但是有一些功能是不能直接用变量获取,需要一个叫做「辅助函数」的操作才能实现。
辅助函数
一些属性无法直接使用,需要配合“辅助函数”完成。最简单的例子就是你博客主页的文章分页器,这玩意儿总不可能自己遍历一次数组吧,DOM 结构自己拼一次也挺麻烦。这就是它存在的意义了。在我的 Hingle 主题里面,初步用到了这些函数:
list_posts
列出博文(List 的函数都是 A 链接形式)list_categories
列出文章所属分类(支持传对象,不传就是全部,可以去掉列表结构)list_tags
列出文章包含的标签paginator
输出分页器(无法自定义结构)tagcloud
输出标签云,随机大小url_for
输出地址(空就是站点地址,带相对链接就转换为绝对链接)url_for()
输出站点首页链接url_for(post.path)
输出文章链接date(new Date(), "YYYY.MM.DD")
格式化输出当前时间
它们可以帮你生成一个 ul
列表,或是一个全是 a
的序列等等,这些函数的使用情形放在侧边栏、页脚一类的地方比较合适,比你用 for
遍历是要方便不少。更多辅助函数,可以看看 官方文档
上的其他说明。
文档的大坑
直播过程中踩了个大坑,就是我文章页面底部位置需要调用上下两篇间隔文章,看文档的属性是 page.prev
和 page.next
没错,但是上面写着这个东西返回 string
或者 null
。
<%= page.prev %>
我用 EJS 直接输出它们是报错的,我尝试用 console.log
打印出来才发现这两个的实际返回类型是 Object
,对象确实会更符合开发的使用情形,反倒字符串拿不到齐全有效的信息。
这个是 Hingle 模板里面完全复刻 Single 的实现方法:
<% if( page.prev !== undefined) { %>
<li>上一篇: <a href="<%= url_for(page.prev.path) %>"><%= page.prev.title %></a></li>
<% } else { %>
<li>上一篇: 看完啦 (つд⊂)</li>
<% } %>
<% if( page.next !== undefined) { %>
<li>下一篇: <a href="<%= url_for(page.next.path) %>"><%= page.next.title %></a></li>
<% } else { %>
<li>下一篇: 看完啦 (つд⊂)</li>
<% } %>
这样一搞我倒是发现了 Hexo 的 Debug 方法了,console.log
会打印在控制台里面。但是官方文档写错返回类型这个问题,对新手开发者难免还是不太友好。看起来官网是一个 Git 仓库,或许我可以提交一个 PR 改进一下?
尚未解决的问题
代码高亮
目前发现文章的代码块默认采用了 Highlight.JS,且修改 Hexo 项目下的 _config.yml
配置文件并重启服务之后依旧无效,无法关闭,具体原因不明。主要是这块和之前的 CSS 有些冲突,这才想让我先关闭取消的。
更新:使用以下配置即可关闭 Hightlight.JS 仅启用 PrismJS
highlight:
enable: false # 关闭 Highlight
line_number: true
auto_detect: false
tab_replace: ''
wrap: true
hljs: false
prismjs:
enable: true # 开启 Prism
preprocess: true
line_number: true
tab_replace: ''
如果修改后代码高亮并未生效,可尝试执行 hexo clean 命令修复这个问题。
评论功能
在 Hexo 平台下的评论支持也是一个没踩过的坑,还得后期看看其他人的解决方案...
后续
我会陆陆续续把 Single 的逻辑进行移植,也可能会进行过程直播,希望大家持续关注!如果你也认为 Single 是一款值得移植的优秀主题,那么不妨去 GitHub 仓库下为我 点个赞 支持一下吧!
录播
直播折腾过程有 录播,信息量较大,感觉整出来也是给各位看笑话的...
已有 7 条评论
hexo 的 prismjs 文档确实很烂,我摸索了一天才逐渐理解怎么 preprocess=false 的情况下让高亮功能恢复工作... 还得修改 style.styl 删掉本来的 highlight css 才行
话说博主是这么样把 Typecho 通过 CloudFlare 的 https 来评论的噢
@niconiconi 啥都没设置啊,也就把 IP 来源的部分做了修改,你可以参考下我的文章《Typecho 一些冷门小技巧》里面第一条
打算在typecho再蹲一段时间~主要是换成hexo,手机不便于管理
@若志奕鑫 你可以以部署一个 vscode-sever
什么时候教教 HEXO 移植 TY
@Meteor 根据这篇文章就能看出来 Hexo 的基本架构和逻辑了呀,移植基本上做的都是替换这种操作