撰写于 2016年6月19日 修改于 2016年6月19日 分类编程杂记 标签Blog /Hexo /前端 当你看到你用的主题出现在两个以上的博客的时候,那你就要考虑自己写一个了。本文的主角是 Hexo ,如果你没有用过,那可以考虑 Hexo 你的博客 了,如果你还没有写博客,那你真的 该试试了 。根据 阮一峰提出的博客三阶段 ,技术人员早晚会选择 Github Pages 类似的服务,而 Hexo 绝对是值得尝试的。 本文内容与目的写这篇文章的目的,当然是希望帮你快速的制作一款主题,将要包含的内容如下:
最终结果写这篇文章的原因,就是因为制作了一个主题Random( 代码 、 Demo ),大家可以先看看,下面的代码,大都来自于这个主题。 基础知识制作 Hexo 主题,除了需要了解 HTML / CSS / JavaScript 之外,还需要了解两个主要的技术,首先一个是模板引擎,Hexo支持主流的模板引擎,像 EJS / Jade / Swig 等,另外一个是 CSS 预处理,如 SASS / LESS / Stylus ,当然,这两个不用,直接使用 HTML / CSS 也是可以的,只不过可能效率会低一点,在本文中,选择使用如下两个: 这篇文章将不介绍它们的使用,请参考它们的文档。 Hexo 工作流程如果你已经看到了这篇文章,基本上你已经是一个 Hexo 用户了,但还是简单的介绍一下 Hexo 的流程:
主题的作用就是在 Hexo 生成文件的时候,提供对应的模板和资源。 主题的基本结构Hexo 对主题的基本要求,是需要有如下几个页面:
以上这些文件,是 Hexo 在生成 HTML 文件时要用到的,全部放在主题的
此外,还有些 JS / CSS / 图片/ favicon.ico 一类的文件,这类文件并不需要 Hexo 进行转换,直接就在 HTML 页面里引用了,所以全部放在主题的 使用 yeoman 生成基础代码现在开始项目之前,我都会搜索一下 yeoman 有没有库,生成 Hexo 主题就有 generator-hexo-theme 。如果还没有安装 yeoman ,那先用 npm 全局安装。 npm i -g yo
接着安装生成器的库: npm i -g generator-hexo-theme
然后到自己的博客目录之下,进入到 chmod 675 ./
运行这个命令一般需要管理员权限,请根据自己系统的情况加 yo hexo-theme
然后选择一些基本的配置,比如使用什么模板引擎,使用什么 CSS 预编译等,这里分别选择 Swig 和 Stylus。完成之后,主题目录下就会生成一些如下结构的文件: ├── _config.yml // 主题配置文件 ├── languages // 多语言文件夹 ├── layout │ ├── archive.swig // 存档页模板 │ ├── category.swig // 分类文章列表页模板 │ ├── includes // 各页面共享的模板 │ │ ├── layout.swig // 页面布局模板,其它的页面模板都是根据它扩展来的 │ │ ├── pagination.swig // 翻页按钮模板 │ │ └── recent-posts.swig // 文章列表模板 │ ├── index.swig // 首页模板 │ ├── page.swig // 页面详情页模板 │ ├── post.swig // 文章详情页模板 │ └── tag.swig // 标签文章列表页模板 └── source ├── css │ └── theme.styl // 主题自定义 CSS 文件 ├── favicon.ico └── js └── theme.js // 主题 JavaScript 文件
赶紧在 Hexo 的主配置文件中使用新主题,到博客根目录下找到 theme: test
赶紧 多语言支持
Hexo 支持多语言显示,在主题的 language: zh-CN # 或者多个配置文件 language: - zh-CN - en
像下面这样组织语言文件, archive_title: Archives category_title: Category tag_title: Tag
在模板里,当需要在页面中显示文字时,可以使用 Hexo 提供的帮助函数 {% if is_archive() %} {% set pageTitle = _p('archive_title') %} {% endif %} <h1>{{ page_title }}</h1> 这样,主题就可以轻松支持使用不同语言的博客主。 各个页面的布局
在上面生成的代码中,所有页面均使用同一个布局,全部扩展自 {% block body %}{% endblock %}
在其它的布局文件(除开 {% extends 'includes/layout.swig' %} {% block body %} {% include 'includes/recent-posts.swig' %} {% include 'includes/pagination.swig' %} {% endblock %}
这就相当于是使用 因此,如果你要不同页面使用不同的布局,那就需要你在各自的页面里自定义,或者在单独的布局文件中定义,再扩展。 数据的填充主题是供了页面的布局和样式,在生成 HTML 文件时,Hexo 会把特定的数据,传给 swig 模板,然后再由 swig 将数据填充到 HTML 文件之中。这些特定的数据,分为如下几类。 主配置文件数据
Hexo 的根目录中,有个 # 字符串 title: 不可能不确定 # 没有值 permalink_defaults: # 逻辑值 auto_spacing: true # 数字 since: 2010 # 数组 skip_render: - "books" - "books/*" # 对象 social: GitHub: https://github.com/stiekel Coding.NET: https://coding.net/u/Stiekel Twitter: https://twitter.com/SidCN 完整代码,请参见 _config.yml 。 主题配置文件
每个主题,还有单独的配置文件,用于配置与主题紧密相关的内容,格式与主配置文件一致。只不过变量名为 具体哪些数据放到主配置文件中,哪些数据放到主题配置文件,自由度其实很高,一般的,推荐与博客中的数据相关的,放主配置文件,如博客的名字、作者、菜单配置等,与主题相关的,放到主题配置文件,比如主题的脚本文件列表、样式文件列表等。当然,在编写主题的时候,也可以考虑对于某一个数据,既可以放在主配置文件中,也可以放在主题配置文件中,像这样: {% set menu = config.menu || theme.menu %} 要读取菜单配置时,任意哪个配置文件中有都可以,而且是优先使用主配置文件中的配置。 配置文件中数据的使用如果要在模板中使用某个具体的值,比如字符串、数字、逻辑变量或者对象的某个成员,可以在主题的模板文件 swig 中直接使用: <div class="title" onClick="openUserCard()">{{ config.title }}</div>
这就相当于把配置文件中的 <ul class="index-nav-link"> {% set menu = config.menu || theme.menu %} {% for key in Object.keys(menu) %} {% if menu[key] != '/' %} <li><a href="{{ url_for(menu[key]) }}">{{ key }}</a></li> {% endif %} {% endfor %} </ul>
常用功能的实现Hexo 提供了很多专门的变量及函数,用于在编写主题时使用。请参见 变量列表 和 帮助函数列表 。这里针对常用的一些功能做对应的介绍。 获取站点的所有的文章、页面、标签和分类
Hexo 为主题提供了一个变量
其中,
"data":{ "cipap3lwj0001fhpvlubaa0sp":{ "name":"乱七八糟", "_id":"cipap3lwj0001fhpvlubaa0sp" } } 不过实际在编写主题的时候,很少会直接用到这几个变量。 获取当前页面的数据
这个变量便是
比如,在我们生成的代码中,首页里使用 {% if site.posts.length > 0 %} {# ... #} {% for postItem in site.posts.toArray() %} {# ... #} {% endfor %} {% endif %}
如果你的博客已经有几篇文章得话,会发现文章虽然列出来了,但并不是按时间来排列的。所以几乎没什么用,但如果使用
再刷新一下首页,可以看到,文章只有几篇,并不是全部文章,且按时间倒序排列。 各种链接的处理获取某个页面的地址,有很多方法。包括:
我们来看看这几个值各自有何作用。打开 <p>{{ path | json }}</p> <p>{{ url | json }}</p> <p>{{ page.path | json }}</p> <p>{{ page.permalink | json }}</p> 然后启动博客,进入做任意一篇文章,可以在顶部看到三个字符串,类似于: "2016-06/Material-Design-Float-Action-Button.html" "http:///2016-06/Material-Design-Float-Action-Button.html" "2016-06/Material-Design-Float-Action-Button.html" "http:///2016-06/Material-Design-Float-Action-Button.html"
链接的具体样式,是主配置文件中 再来加一行: <p>{{ url_for(page.path) | json }}</p>
输出为: "/2016-06/Material-Design-Float-Action-Button.html"
这个结果就比较适合作为站内链接了。 在页面中加入 css 与 js 文件
Hexo 提供了两个帮助函数 当前页面类型的判断
由于有些代码在不同的页面都是共用的,所以有时候就需要根据不同的页面,做不同的显示。比如,一般会把 HTML 的
常用的字符串处理函数
另外几个,请参见 字符串处理函数 列表。 时间的处理
Hexo 提供了多个 时间处理函数
,不过一个 {% set thisYear = date(Date.now(), 'YYYY') %} HTML 中用法如下: <span id = "post-title-date">{{ date(postItem.date, 'M-D') }}</span>
由于是使用 Moment.js 来显示的时间,所以直接使用 Moment.js 的 时间格式 就行了,常用的如下:
生成标签云
Hexo 提供了帮助函数 以下是生成的一个标签云 HTML: <div class="tag-cloud-tags"> <a href="/tags/ASP-NET/" style="font-size: 17.33px; color: #929292">ASP.NET</a> <a href="/tags/Access/" style="font-size: 24px; color: #6f6f6f">Access</a> <a href="/tags/Android/" style="font-size: 17.33px; color: #929292">Android</a> </div>
这样便可以通知设置 生成文章的目录
对于长文章,目录还是非常实用的,Hexo 也提供了 生成的 HTML 是个有序列表,结构如下: <ol class="toc"> <li class="toc-item toc-level-3"> <a class="toc-link" href="#按钮的定位"> <span class="toc-text">按钮的定位</span> </a> </li> </ol>
只要使用了 if($("ol.toc").children().length <= 3) { $(".toc").hide(); } 这样当目录条数少于四条时,便自动隐藏目录。 页面的组织代码的复用上面我们曾经提到,对于一个主题,主要有首页、存档页、标签文章列表页、分类文章列表页、文章详情页、页面详情页这几个页面。这些页面的实现中,你会发现有大量的代码是可以共享的,比如:
所以,对于这部分的代码,一般都会设计成可复用的代码段,将这些代码段文章存放在 <div class="post-title-item"> {# postItem 为存有一个文章的所有信息的对象 #} {# postItem.title 为文章标题,如果没有标题,则直接截取文章内容 #} {# strip_html 是将 html 代码中提取可供普通人阅读的文字部分 #} {# trim 是去除前后空格 #} {% set postTitle = postItem.title || trim(strip_html(postItem.content)) %} <a href="{{ config.root }}{{ postItem.path }}"> {# 标题最多 80 个字符,超过得话,使用 truncate 来截取 #} {% if postTitle.length < 80 %} {{ postTitle }} {% else %} {{ truncate( postTitle, {length: 80}) }} {% endif %} </a> <p class="page-title-sub"> {# 显示时间 #} <span id = "post-title-date">{{ date(postItem.date, 'M-D') }}</span> {% if postItem.categories.length %} {# 组织分类的链接列表 #} <span id = "post-title-categories">{{ __('category_title') }} {% set i = 0 %} {% for cat in postItem.categories %} {% if i !== 0 %} {{'/'}} {% endif %} {% set i = i + 1 %} <a href="{{ url_for(cat.path) }}">{{ cat.name }}</a> {% endfor %} </span> {% endif %} {# 组织标签的链接列表 #} {% if postItem.tags.length %} <span id = "post-title-tags"> {{ __('tag_title') }} {% set i = 0 %} {% for tag in postItem.tags %} {% if i !== 0 %} {{'/'}} {% endif %} {% set i = i + 1 %} <a href="{{ url_for(tag.path) }}">{{ tag.name }}</a> {% endfor %} </span> {% endif %} {# 有些在文章的头部指定了照片,也显示出来 #} {% if postItem.photos %} <br> {% set i = 0 %} {% for photo in postItem.photos %} {% set i = i + 1 %} {% if i <= 3 %} {# 这里使用了 fancybox 的一些功能,具体后面了解 #} <a class="fancybox-thumb" rel="fancybox-thumb" href="{{photo}}"> <img src="{{photo}}" /> </a> {% endif %} {% endfor %} {% endif %} </p> </div>
这样,在需要使用的页面中,单个文章对象的常量名设置为 {# 遍历所有文章,注意,单个文章的对象为 `postItem` #} {% for postItem in site.posts.toArray() %} {% set isShow = false %} {# 确定一下某一个文章,是否包含当前这个标签 #} {% for tag in postItem.tags %} {% if tag.name === page.tag %} {% set isShow = true %} {% endif %} {% endfor %} {# 包含就显示 #} {% if isShow %} {% include 'includes/post-title-item.swig' %} {% endif %} {% endfor %}
以上代码在 首页
首页一般会包括一些链接和最近的几篇文章,使用的模板文章为
如果要显示最近的几篇文章的列表,可以使用 分类列表页与分类文章列表页分类列表页显示博客里的所有分类,分类文章列表页显示某个分类中的文章列表。
Hexo 并没有专门分类列表页的模板,那该如何处理呢?一般是写在页面模板中,即 {% extends 'includes/layout.swig' %} {% block body %} <article id="page"> {% set page_title = page.title %} {# 判断是否是分类列表页,如果是,显示对应内容 #} {% if 'categories' === page.type %} <h1 class="center-title">{{ page_title || __('category_title') }}</h1> {{ list_categories() }} {# 显示普通页面的内容 #} {% else %} <h1 class="">{{ page_title }}</h1> {% autoescape false %}{{page.content }}{% endautoescape %} {% endif %} </article> {% include 'includes/pagination.swig' %} {% endblock %}
要显示所有的分类,这里是使用了 hexo new page categories
然后在站点的 title: categories date: 2016-06-19 10:29:23 --- 将其内容修改为: title: 分类 date: 2016-06-19 10:29:23 type: "categories" comments: false ---
这样,Hexo 在生成页面时,判断到
而对于分类文章列表页,则需要在 标签列表页与标签文章列表页这个与分类的处理类似,不做重复介绍。 归档页
归档页使用的 文章列表显示比较简单,单说一点,如何按年分隔来显示文章列表,当然,按月分隔显示的操作也一样。
操作方法其实比较简单,先遍历 {# 先遍历一篇文章列表,找出每年各发了多少文章,以便显示 #} {% set howMuch = {} %} {% for postItem in page.posts.toArray() %} {% set thisYear = date(postItem.date, 'YYYY') %} {% if !howMuch[thisYear] %} {% set howMuch[thisYear] = 0 %} {% endif %} {% set howMuch[thisYear] = howMuch[thisYear] + 1 %} {% endfor %} {# 开始显示, lastYear 为上一篇文章的发表年份 #} {% set lastYear %} {% for postItem in page.posts.toArray() %} {# 当前文章的发表年份 #} {% set thisYear = date(postItem.date, 'YYYY') %} {# 如果当前文章年份与上一篇不一样,则显示年份 #} {% if thisYear !== lastYear %} {% set lastYear = thisYear %} <h3 class="archive-year-title">{{ thisYear }} {# 年份后面,显示一下这年共发表了多少篇文章 #} {% if howMuch[thisYear] %} <span class="posts-count"> {{ __('total_title') }} {{ howMuch[thisYear] }} {% if 1 === howMuch[thisYear] %} {{ __('post_title') }} {% else %} {{ __('posts_title') }} {% endif %} </span> {% else %} {{ __('not_post') }} {% endif %} </h3> {% endif %} {% include 'post-title-item.swig' %} {% endfor %} 图片与图片库
文章的图片放在 两个地方可能要显示图片,一个是文章或页面的详情中, 另外一个是在各个文章列表中。 代码的显示
代码段一般是使用 highlight
的样式,直接加载它的 css 就行。另外可能需要调整一下代码段的
除了代码段,在正文中之内还有一些代码文字,会使用 代码的字体,可以 www. 上找一组合适的,并且兼容 Windows / OS X 的字符,比如下面这个: code, table .line{
font-family: Consolas, Monaco, Courier New, Lucida Console;
}
主题的发布如果你想要更多的人看到你的主题,可以考虑发布到 Hexo 官方网站的主题列表中。需要经过几个步骤来实现。 主题测试官方提供了一个站点的示例,里面包含了多种需要考虑的可能情况。克隆 hexo-theme-unit-test ,然后在这个站点里安装你的主题。并确定主题可以成功的处理如下几种类型的文章:
发布
先 Fork 官方站点, hexojs/site
,然后克隆到本地,再编辑站点内 - name: Random description: A hexo theme with random fullscreen background image. link: https://github.com/stiekel/hexo-theme-random preview: http://hexo-theme-random.herokuapp.com/ tags: - responsive - one_column - background_image - random - iconfont - modal
然后再在 其实主题制作,主要是各种细节,按你想要的样子去实现吧。 |
|