1. Hexo Start
1.1 安装
> npm install hexo-cli -g
> hexo version
> hexo help
1.2 搭建测试环境
> hexo init [project]
> cd [project]
> npm install
> hexo new [layout] <title>
1.3 启用命令
> hexo server # run localhost:4000/
> hexo clean # clean public/, db.json
> hexo g # generate public/xxx
> hexo d # deploy deploy/xxx
1.4 常用命令扩展
> npm install <plugin-name> --save #安装插件
> git clone <repository> themes/<theme-name> #下载主题
> hexo server -p xxx # --port set port at xxx
> hexo server --debug # console out (log level: debug,info,warn,error)
> hexo list <type> # list site information (type: page, post, route, tag)
> hexo render <file> # use renderer plugins render
> hexo publish <filename> # move _drafts/xxx to _post/xxx
> hexo <cmd> --config <configFile> # 指定全局配置文件
(eg: hexo server --config _config-test.yml)
PS:命令行可参考代码:node_modules\hexo\lib\plugins\console\index.js
2. 一些使用说明
Scaffold (文章脚手架)
在/scaffolds
下可配置对应layout的front-matter
,在使用命令新建或发布文章时会自动匹配结合front-matter
部分
hexo new [layout] <title>
hexo publish <title>
( 源码: node_modules\hexo\lib\plugins\console\new.js调用node_modules\hexo\lib\hexo\post.js的create,publish方法)
draft (草稿)
- 放在
/source/_drafts
下 - 可使用命令
hexo new draft <title>
创建草稿 - 通过命令
hexo publish <title>
可将_drafts下的文章移动到_posts
下- 会按配置的
new_post_name
命名文章 - 如果配置了
post_asset_folder: true
,对应的asset目录也会移动
- 会按配置的
- 当然这些也可以自己手动操作
- 草稿会被解析到
model('Post')
中,字段published为0 (默认情况下不会传递给布局页面进行渲染) _config.yml
中设置render_drafts: true
- 会传递给布局页面进行渲染
- 在generate或deploy时会自动将
_drafts/
下的文件解析生成到目标文件夹下 (但不会移动到/source/_posts
下)
assert (资源文件)
- 在
_config.yml
中配置post_asset_folder: true
- 在使用命令新建文章时,会自动在同一路径下创建一个同文件名的文件夹 可以将该文章的一些资源文件放在里面
- 例如:
使用
> hexo new post test
,则在/source/_posts
下,会创建文件2016-01-01-test.md
和文件夹2016-01-01-test/
,且改文件夹下的文件不会被renderer
解析,generate
或deploy
时会直接copy到目标目录下 (这里有日期,是因为我在_config.yml
中配置了new_post_name: :year-:month-:day-:title.md
) - 当然也可以自己创建
Skip Render
可在_config.yml
文件中配置skip_render
参数
skip_render:
- test1/*.html
- test2/*
- test3/**
skip_render
使用minimatch
,配置时可使用简易表达式- 这里的
skip_render
是匹配基于source_dir
下的文件的,一般为/source
- 注意是根目录下的source目录 (对于theme的source目录是无效的)
- 必须是
source_dir
下可被识别为model('Page')
的文件,即非_
开头、非~
结尾、非%
结尾的文件或文件夹下 skip_render
的文件虽然不会使用renderer
渲染,但依旧会被processor
解析入model('Page')
中
front-matter
是文件最上方以 ---
分隔的区域,用于指定文章的一些属性,内容格式遵循yml
,例如:
title: Hello World
date: 2016-01-29 10:57:44
tags: [test,hexo]
---
- 这块内容由node module
hexo-front-matter
处理 - 除了使用Hexo预定义的一些属性,还可以随意添加自定义的
- 这些信息都会根据layout解析进入对应的
model
记录中 - 在布局页面中可通过Hexo变量
page.xxx
获取 (关于model
和Hexo变量
在后面章节中会介绍)
Read More
在文章正文中使用<!-- more -->
进行分割,则正文解析后,分割的前后段部分会分别设置到excerpt
和more
字段,content
字段不变
Data (数据文件)
此功能会载入 source/_data
内的 YAML 或 JSON 文件到全局变量site.data
中
例如:
在 /source/_data
文件夹中新建 menu.yml
文件:
Home: /
Gallery: /gallery/
Archives: /archives/
在模板中使用这些资料:
{% for link in site.data.menu %}
{{ loop.key }}
{% endfor %}
EJS
布局文件可以使用普通多种模板文件,Hexo默认的使用的是ejs
页面中使用:
<!-- js逻辑代码-->
<% xxx %>
<!-- 输出内容(转换成字符串)-->
<%=xxx%>
<!-- 页面中输出内容(原样输出)-->
<%- xxx %>
单独js中使用举例:
var ejs = require('ejs');
var target = fs.readFileSync(path, 'utf8');
var locals={title:test};
var html = ejs.render(target, locals); // target为ejs模板文件,locals为传入参数
gulp中使用举例:
var gulp = require('gulp');
var rename=require('gulp-rename');
var ejs=require('gulp-ejs');
gulp.task("ejs",function(){
gulp.src("./templates/about.ejs")
.pipe(ejs({title:"About",menuLinks:menuLinks}))
.pipe(rename("about.html"))
.pipe(gulp.dest("../build"));
})
Theme
Hexo的theme放置在/themes
下,在_config.yml
中配置theme: xxx
,可下载自己喜欢的theme或自己编写theme
theme目录结构:
└── themes/ └── xxxx/ └── layout └── scripts └── source └──_config.yml
下载:
> cd project/themes > git clone xxxx
- theme的相关配置可写在theme下的
_config.yml
文件中,在模板中可通过theme.xxx
获取配置信息 - 若想针对此theme扩展hexo功能,可在theme的
scripts
文件夹下编写js代码 - 一些资源文件比如
css
,js
可以放置在theme的source
目录下,hexo会使用匹配的hexo-renderer-xxx
解析生成 - 扩展举例:Hexo默认使用Styl编写CSS样式,这里扩展使用sass
- 下载node module
hexo-renderer-sass
,使用命令npm install hexo-renderer-sass --save
(注意不要使用--save-dev
) - 可配置项:
node_sass: outputStyle: nested precision: 5 sourceComments: false
- 在theme的source目录下编写
.scss
文件
- 下载node module
Highlight
hexo中的默认配置:
highlight: {
enable: true,
auto_detect: true, // Maintain consistent with previous version.
line_number: true,
tab_replace: ''
}
注意:
Hexo默认是开启highlight功能的,在filter:before_post_render
时解析,若配置highlight的enable为false,则会设置options.highlight=null
,覆盖其他的配置(assign(...,options)
),所以即便在hexo-renderer-marked
中设置highlight,实际上也是无用的。
在前端使用highlight渲染:
- 关闭hexo的highlight功能,设置
enable: false
- 配置marked的
langPrefix: "hljs "
- 在需使用highlight的模板页面中:
<% if(page.layout=='post'){%> <%- css('highlight/demo/styles/tomorrow-night') %> <%}%> ... <%- js('highlight/highlight.pack') %> <script type="text/javascript"> hljs.initHighlightingOnLoad(); </script>
Marked
Hexo内置使用的是hexo-renderer-marked
解析markdown文章(内部调用node module marked
来解析)
可配置项举例:
marked:
gfm: true
pedantic: false
sanitize: false
tables: true
breaks: true
smartLists: true
smartypants: true
...
使用MathJax解析Latex
在模板页面中配置:
<script type="text/javascript" src="/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
/*
processEscapes: true,
//skipTags remove 'pre' entry
skipTags: ['script', 'noscript', 'style', 'textarea','code']
*/
}
});
</script>
如果想给公式添加css class(比如都加上has-jax
),可以增加如下配置:
<script type="text/x-mathjax-config">
MathJax.Hub.Queue(function() {
var all = MathJax.Hub.getAllJax(), i;
for (i=0; i < all.length; i += 1) {
all[i].SourceElement().parentNode.className += ' has-jax';
}
});
</script>
使用的Latex表达式可能会和Nunjucks
有冲突,hexo解析时可能会报错
(例如:在Latex中使用了两个连续的{
,且在其中使用了\xxx\
)
报错在post render 中进行标签插件的渲染时(node_modules/hexo/lib/hexo/post.js
的render函数中):
//在触发onRenderEnd的tagFilter函数时报错
return ctx.render.render({
text: data.content,
path: source,
engine: data.engine,
toString: true,
onRenderEnd: tagFilter
}, options);
Scripts
若要自己扩展Hexo,可在/scripts
或theme的/scripts
目录下编写js
比如,扩展一个命令,新建一个js文件在/scripts
文件夹下:
hexo.extend.console.register('test', 'Test Cmd', {
usage: '[name] [value]',
arguments: [
{name: 'name', desc: 'Setting name.'},
{name: 'value', desc: 'New value of a setting.'}
]
}, function(args){
console.log(args);
console.log(this.config);
});
执行命令:hexo test run 123
,可看到执行结果
Deploy
- 下载插件
npm install hexo-deployer-git --save
_config.yml
配置部署到github上(也可配置部署到其他地方):deploy: type: git repository: https://github.com/xxx/xxx.github.io branch: master
- 执行
hexo deploy
Migrate (迁移)
可参考官方文档
- 从Jekyll,Octopress,Middleman等静态博客迁移,只需按照文章的规则进行手动修改和copy
- RSS,WordPress,Joomla等可以使用相关插件
- 注意:如果使用导出的文件路径或网站来进行博客迁移,必须保证迁移使用的源是完整的(例如使用RSS,则RSS中必须包含所有文章信息),否则导入会不全
- 评论等配件功能的迁移,注意修改相关配置,具体参考各功能配件
Config (配置说明)
默认配置在/node_modules/hexo/lib/hexo/default_config.js
在/_config.yml
中的一些可配置项列举如下:
Site
- title : 网站的标题
- subtitle : 网站的子标题
- description : 网站的描述
- author : 你的名字
- language : 网站的语言
- timezone : 网站的时区,默认电脑的设置,可以在here找到时区列表,例如America/New_York, Japan, and UTC.
URL
- url : 网站的url
- root: 网站的根目录
- permalink: 文件保存在public中得目录格式,默认为 :year/:month/:day/:title/
- permalink_default: permalink每部分的默认值
Directory
- source_dir: 内容存放的源码文件夹目录 (默认:source)
- public_dir: 静态网页生成目录 (默认:public)
- tag_dir: 标签目录 (默认:tags))
- archive_dir: 存档目录 (默认:archives))
- category_dir: 类别目录 (默认:categories))
- code_dir: 引入的代码目录 (默认:download/code))
- i18n_dir: i18n目录(默认: :lang)
- skip_render: 跳过指定文件的渲染,您可使用 glob 来配置路径。(默认:[])
Writing
- new_post_name :新提交所使用的格式化文件名( :title.md)
- default_layout:默认格局 (post)
- titlecase :把标题转换成title case (false)
- external_link: 使用新标签打开一个连接(true)
- filename_case :转换文件名1小写;2大写 (0)
- render_drafts :显示草稿 (false)
- post_asset_folder: 是否开启Assert文件夹 (false)
- relative_link :是否创建一个文件夹关联根目录 (false)
- future:显示特性提交? (true)
- highlight:代码块设置
Category&Tag
- default_category:默认类别 (默认为:uncategorized)
- category_map:分类别名
- tag_map:标签别名
Date/Time format (Hero使用Moment.js来处理数据)
- date_format :日期格式(MMMM D YYYY)
- time_format 时间格式(H:mm:ss)
Pagination
- per_page: 每页最大文章数,0隐藏页数 (10)
- pagination: Pagination目录 (page)
Extensions
- theme :背景名,false隐藏背景
- deploy:部署设置
一些配置中可使用变量:
:year
:4位的年,比如2015:month
:2位的月,比如06:i_month
:去掉前面0的月,比如9代表9月:day
:两位的天,比如05代表5号:i_day
: 同理:title
:文件名:id
:文章ID:category
: 类别;如果文章没有指定,那么就是default_category字段设置的那个。:lang
:语言
一些generator配置举例:
- hexo-generator-archive
archive_generator: per_page: 10 yearly: true monthly: false daily: false
- hexo-generator-tag
tag_generator: per_page: 0
3. 应用扩展
tagCloud
Hexo的Helper功能已有内置的生成tagcloud的方法tagcloud([tags], [options])
可直接调用,比如:
<%- tagcloud({min_font: 12, max_font: 30, amount: 200, color: true, start_color: '#ccc', end_color: '#111'})%>
toc
Hexo的Helper功能已有内置的toc的方法toc(str, [options])
可直接调用,比如:
<%-toc(page.content, { "class": "nav", list_number: theme.toc.number })%>
feed
- 下载插件:
> npm install hexo-generator-feed --save
_config.yml
中配置:sitemap: path: sitemap.xml
模板文件中添加Link:
<a href="<%- url_for(site.data.about.rss)%>" target="_blank"> <span class="icon-rss"></span> </a>
- 可以修改
hexo-generator-feed
下的模板文件atom.ejs
或rss2.ejs
sitemap
- 下载插件:
> npm install hexo-generator-sitemap --save
- 配置:
feed: type: atom path: atom.xml #limit: 20
- 模板文件中添加Link:
<a href="<%- url_for(site.data.about.rss)%>" target="_blank"> <span class="icon-rss"></span> </a>
- 可以修改
hexo-generator-sitemap
下的模板文件sitemap.ejs
Comments
Disqus
官网 | Disqus进阶使用
// disqus默认将数据加载到id为'disqus_thread'的容器中,可配置disqus_container_id改变
<div id="disqus_thread">
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
</div>
<script type="text/javascript">
/* * *配置变量:可被添加在embed.js脚本之前的任何地方* * */
// Required - Replace example with your forum shortname (在Dsiqus上设置的论坛短名)
var disqus_shortname = 'example';
//Recomand - a unique URL for each page where Disqus is present(文章网址)
var disqus_url ='xxxx'
//Recomand - a unique identifier for each page where Disqus is present(可以配置为article.id)
var disqus_identifier ='xxxx';
// a unique title for each page where Disqus is present(可配置为article.title )
var disqus_title = 'xxx';
//可以在Settings > advanced页面或者使用我们的(分类)API来创建新分类
var disqus_category_id = '123456';
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
评论计数
<span class="disqus-comment-count" data-disqus-url="http://example.com/path-to-thread/"> <!-- Count will be inserted here --> </span>
或
<span class="disqus-comment-count" data-disqus-identifier="your_disqus_identifier"> <!-- Count will be inserted here --> </span>
多说
<script type="text/javascript">
var duoshuoQuery = {short_name:"xxx"};
(function() {
var ds = document.createElement('script');
ds.type = 'text/javascript';ds.async = true;
ds.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//static.duoshuo.com/embed.js';
ds.charset = 'UTF-8';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ds); })();
</script>
<div class="ds-thread"
data-thread-key="请将此处替换成文章在你的站点中的ID"
data-title="请替换成文章的标题"
data-url="请替换成文章的网址"
data-limit="单页显示评论数,取值范围:1~200"
data-order="排序方式,取值:asc(从旧到新),desc(从新到旧)"
...
></div>
使用示例:
var duoshuoQuery = {short_name:"xxx"};
<script type="text/javascript" async="" src="http://static.duoshuo.com/embed.js" charset="UTF-8"></script>
<div className="ds-thread"
data-thread-key="<%= page.path %>"
data-title="<%= page.title %>"
data-url="<%= page.permalink %>">
</div>
文章评论数:
<span class="ds-thread-count"
data-thread-key="文章在原站点中的id或其他唯一标识"
data-count-type="comments">
</span>
最新评论:
<div class="ds-recent-comments"
data-num-items="5"
data-show-avatars="1"
data-show-time="1"
data-show-title="1"
data-show-admin="1"
data-excerpt-length="70">
</div>
热评文章
<div class="ds-top-threads" data-range="daily" data-num-items="5"></div>
Search
站内搜索功能 (注意:尽量完善网站的sitemap)
Swiftype
官网 | 使用swiftype实现站内搜索 | 利用swiftype为hexo添加站内搜索
注意:配置搜索方式和搜索框等内容(在官网上配置)
<script type="text/javascript">
(function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
(w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
})(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');
_st('install','xxxx','2.0.0'); //xxxx为userID
</script>
比如:
在官网上配置了使用searchfield的selector为#hexoTest-search-field
,则在页面中可以这样写
<form>
<input type="text" id="hexoTest-search-field" />
<button type="submit">Search</button>
</form>
TinySou
微搜索 (与Swiftype设置类似)
<script>
var customRenderActFunction = function(item) {
var out = '<p class="title">' + item['document']['title'].split(" // ")[0] + '</p>';
return out;
};
var option = {
engineKey: 'xxxx', // 设置自己的 tinysou_Key
renderActFunction: customRenderActFunction
};
(function(w,d,t,u,n,s,e){
s = d.createElement(t);
s.src = u;
s.async = 1;
w[n] = function(r){
w[n].opts = r;
};
e = d.getElementsByTagName(t)[0];
e.parentNode.insertBefore(s, e);
})(window,document,'script','//tinysou-cdn.b0.upaiyun.com/ts.js','_ts');
_ts(option);
</script>
<form>
<input type="text" id="ts-search-input">
</form>
在搜索引擎中配置自己的网站,上传sitemap
搜索功能嵌入自己网站中
- 添加JS代码
<script> (function() { var cx = 'xxx'; // 生成的唯一id var gcse = document.createElement('script'); gcse.type = 'text/javascript'; gcse.async = true; gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//cse.google.com/cse.js?cx=' + cx; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(gcse, s); })(); </script>
页面中添加:
<!--搜索框-> <gcse:searchbox-only></gcse:searchbox-only> <!--搜索结果--> <gcse:searchresults-only></gcse:searchresults-only>
可自己添加Search样式,例如:
.gsc-resultsHeader { display: none !important; } .gsc-result .gs-title { height: 1.5em !important; } .gsc-webResult .gsc-result { padding: 0px 0 16px 0 !important; } .article-entry table { margin: 0px 0 !important; }
- 添加JS代码
不嵌入到网站中(link到搜索引擎页面进行搜索)
- Hexo的Helper已有此功能:
<div id="search-form-wrap"> <%- search_form() %> </div>
- 或自己添加
<form action="//google.com/search" method="get" accept-charset="UTF-8"> <input type="search" name="q" results="0" > <input type="hidden" name="sitesearch" value="xxx"> <!-- 这里value为自己site的url --> </form>
- Hexo的Helper已有此功能:
PS:百度站内搜索设置和此类似
Share
可以自己拼接分享URL来实现分享功能,这里介绍两种现有的分享功能
<div class="bdsharebuttonbox">
<a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间"></a>
<a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博"></a>
<a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博"></a>
<a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网"></a>
<a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信"></a>
<a href="#" class="bds_fbook" data-cmd="fbook" title="分享到Facebook"></a>
<a href="#" class="bds_twi" data-cmd="twi" title="分享到Twitter"></a>
<a href="#" class="bds_more" data-cmd="more"></a>
<a href="#" class="bds_count" data-cmd="count"></a>
</div>
<script>
window._bd_share_config={
"common":{
"bdSnsKey":{},
"bdText":"",
"bdMini":"1",
"bdMiniList":false,
"bdPic":"",
"bdStyle":"0",
"bdSize":"16"
},
"slide":{
"type":"slide",
"bdImg":"3",
"bdPos":"left",
"bdTop":"250"
}
};
with(document)0[
(getElementsByTagName('head')[0]||body)
.appendChild(createElement('script'))
.src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)
];
</script>
<div class="ds-share-inline">
<ul class="ds-share-icons-16">
<li data-toggle="ds-share-icons-more"><a class="ds-more" href="javascript:void(0);">分享到:</a></li>
<li><a class="ds-weibo" href="javascript:void(0);" data-service="weibo">微博</a></li>
<li><a class="ds-qzone" href="javascript:void(0);" data-service="qzone">QQ空间</a></li>
<li><a class="ds-qqt" href="javascript:void(0);" data-service="qqt">腾讯微博</a></li>
<li><a class="ds-wechat" href="javascript:void(0);" data-service="wechat">微信</a></li>
</ul>
<div class="ds-share-icons-more">
</div>
</div>
<script>
//JS代码(同多说评论)
</script>
Analytics
Google Analytics 官网 | Doc | 博文
- 在Google Analytics官网上注册,配置(网站域名),获取JS代码
- 将JS代码Copy到Hexo的模板文件中
一些Theme已集成了Google Analytics,比如 landscape (放置在
<head>
中):<script type="text/javascript"> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'xxxx', 'auto'); // xxx为注册配置后生成的唯一id ga('send', 'pageview'); </script>
4. 源码概要
可参考Hexo官方API
4.1 基础目录
├── _config.yml
├── package.json
├── scaffolds/
├── scripts/
├── source/
| ├── _drafts
| └── _posts
└── themes/
└── landscape/
└── layout
└── scripts
└── source
└──_config.yml
4.2 Hexo Core
主要在 /node_modules/hexo/lib/hexo
下:
index.js 核心: (默认配置文件default_config.js)
return loadDatabase(this).then(function(){
return Promise.all([
//调用processor
self.source.process(),
self.theme.process()
]);
}).then(function(){
return self._generate({cache: true});
}).nodeify(callback);
- => Load Database (build models)
- => Processor ( insert models)
- => Post Render (parse article main content )
- => Generator (expand model datas)
- => Theme Renderer (combine data and template to output)
4.3 Hexo model
Hexo中的
database
,model
类是由 node modulewarehouse
定义的(具体可参考warehouse的源码)- 在Hexo中创建database和注册models对象的代码如下 (index.js中)
this.database = new Database({ version: dbVersion, path: pathFn.join(base, 'db.json') }); registerModels(this);
- 在
filter:before_exit
时save database对象到file:/db.json
- 可在
/db.json
中查看解析的models对象,也可自行扩展导入到其他DB中 - 注意:model的Schema中定义为CacheString类型的字段不会
save
到/db.json
- 在Hexo中创建database和注册models对象的代码如下 (index.js中)
Hexo的主要model为(定义在
/node_modules/hexo/lib/models
下):Page
- Schema包含:title,date,updated,comments,layout,_content,source,path,raw,content,excerpt
- virtual path (即Reference): permalink,full_source
- Schema中的content,excerpt,more字段为CacheString类型
Post
- Schema包含page中所有properties,再加slug,photos,link,published
- virtual path包含page中所有,再加 path,asset_dir,tags,categories
Tag
- Schema包含:name
- virtual path: slug,path,permalink,posts,link,
Category
- Schema包含:name,parent
- virtual path: slug,path,permalink,posts,length
Data
- Schema包含:_id,data
Assert
- Schema包含:_id,path,modified
这些models会被设置到
/node_modules/hexo/lib/hexo/locals.js
中定义的Locals
对象中locals.set
:posts
,pages
,categories
,tags
,data
(会对model进行处理后再设置到locals,具体参看index.js下的_bindLocals方法)- 例如:若
_config.yml
设置了render_drafts: true
,则通过locals获取posts
时会自动过滤掉草稿,即published
字段为true
的记录
- 例如:若
在布局页面渲染时会将
Locals
传入,这样就可在布局页面中使用这些变量例如:后面介绍的Hexo Variables中的全局对象
site
,是通过如下代码设置的//after do filter:before_generate self.locals.invalidate(); siteLocals = self.locals.toObject(); Locals.prototype.site = siteLocals; //Run Generators (每个generator.call(self,siteLocals) ) ...
- Locals.prototype.toObject 返回 {key,item} (item是存放在locals的对象)
- 还有其他Hexo Variables也是在`index.js`中设置到Locals
4.4 Hexo Processor
self.source.process()
解析File(/source下的文件),更新对应Model: (调用/node_modules/hexo/lib/plugins/processor
下regist的processor)- asset.js:
- 处理非临时(
%
,~
结尾)和隐藏(_
开头)文件 - 解析
front-matter
更新model('Page')
中对应记录的属性(若为不可渲染文件,则更新model('Asset')
) - 若无对应记录,则插入一条新记录
- 处理非临时(
- post.js:
- 处理
_posts/
,_drafts/
下的非临时和隐藏文件 - 解析基本同上,只是操作的是
model('Post')
中的记录
- 处理
- data.js:
- 处理
_data/
下文件 - render后更新
model('Data')
中的记录
- 处理
- asset.js:
self.theme.process()
解析File(/themes/[themeName]下文件),更新相关配置: (调用/node_modules/hexo/lib/theme/processors
下的processor)- config.js:
- 处理
_config.xxx
文件 - render后更新box.config
- 处理
- i18n.js:
- 处理
languages/
下文件 - render后更新box.i18n
- 处理
- source.js:
- 处理
source/
下文件 - 更新
model('Assert')
中对应记录的属性
- 处理
- view.js:
- 处理
layout/
下文件 - 更新box.view
- 处理
- config.js:
source和theme对象是通过
Box
调用执行对应的processor (具体可参看/node_modules/hexo/lib/box
)- event:
processBefore
processor.pattern.match(path)
判断是否可处理- 可处理则调用
processor.process
方法 - event:
processAfter
- event:
PS:
/source/_post
,/source/_draft
- isRenderable:true =>
model('Post')
,model('Category')
,model('Tag')
,model('PostTag')
,model('PostCategory')
asset_dir/
=>model('PostAssert')
- isRenderable:true =>
/source/_data
=> do Render =>model('Data')
/source
下非_
开头、非%
、非~
结尾的文件或文件夹- isRenderable:true =>
model('Page')
- isRenderable:false =>
model('Assert')
- isRenderable:true =>
/themes/[themeName]/source
下非_
开头、非%
、非~
结尾的文件或文件夹 =>model('Assert')
4.5 Hexo Generator
1. 过程:
- filter:
before_generate
(event: generateBefore)- render_post,call
/node_modules/hexo/lib/hexo/post.js
(处理model('Post')
,model('page')
)
- render_post,call
- Run Generators
- 每个Generator返回一个key-value或数组对象
- 都要包含path和data字段,可包含其他自定义扩展字段
- 即
{path:xxx,data:xxx,...}
或[{path:xxx,data:xxx,...},...]
- Add Routes
- filter:
template_locals
(i18n) - Theme Render (合并render布局文件和上面Generator返回的数据 )
- filter:
- filter:
after_generate
(event: generateAfter)
2. hexo本身regist generator plugin: (会更新对应model的记录)
- asset.js: 处理
model('Assert')
和model('PostAsset')
的记录 - page:js: 处理
model('Page')
的记录 - post.js: 处理
model('Post')
的记录
3. hexo通过package.json加入的generator plugin:
- hexo-generator-index
- hexo-generator-archive
- hexo-generator-tag
- hexo-generator-category
PS:对于这些plugin,可在_config.yml中通过xxx_generator配置一些参数 【具体参看对应generator plugin的源码】
4.6 Hexo Renderer
1.处理:
- render post 调用匹配的render解析文章 (
/node_modules/hexo/lib/hexo/post.js
)- filter:
before_post_render
- backtick_code_block
- titlecase
- Skip rendering swig file
- Render (!isSwig)
- get matched renderer
- call rendererer (更新对应model记录的content字段)
- onRenderEnd (tag render)
- filter:
after_render:xxx
- isSwig -> tag.render
- filter:
after_post_render
- excerpt (处理
<!-- more -->
,填充model的excerpt和more属性内容) - external_link
- excerpt (处理
- filter:
- theme view render 布局文件和Generator返回的数据合并渲染出最终文件 (
/node_modules/hexo/lib/hexo/theme/index.js
)
2.hexo本身renderer处理:
htm,html
tohtml
[ use plain.js]css
tocss
[ use plain.js ]js
tojs
[ use plain.js ]json
tojson
[ use json.js ]swig
tohtml
[ use swig.js ]yml,yaml
tojson
[ use yaml.js ]
3.hexo通过package.json加入的renderer:
- hexo-renderer-ejs:
ejs
tohtml
- hexo-renderer-marked:
md,markdown,mkd,mkdn,mdwn,mdtxt,mdtext
tohtml
- hexo-renderer-stylus:
styl,stylus
tocss
4.7 Hexo Theme
1. 目录结构
/themes/[themeName]/
下:
/layout
:- 用于存放模版文件(eg: ejs,jade,swig等)
- 一般有 layout,page,post,tag,category,archive布局文件
- 可在布局文件中调用hexo的全局变量
- site(site.pages,site.posts,site.categories,site.tags,site.data.xxx)
- page ( page.title,page.date,page.layout,page.content,page.tags,...)
- config (/_config.yml中定义的)
- theme (theme下_config.yml中定义的)
- path,url,env
- _ lodash函数
/scripts
:- 用于存放扩展hexo的js代码
- 可直接使用hexo.xxx调用hexo的功能
/source
:- 用于存放资源文件(eg:css,js)
- hexo会根据文件后缀调用register的renderer处理
- 执行generate或deploy会全部生成到目标目录下
2. Config (注意优先级)
Locals.prototype.theme = _.extend({}, config, theme.config, config.theme_config);
修改theme:将_config.yml
中theme字段值设为主题名即可
3. Layout
- 每个模板都默认使用
layout
布局 - 可在文章的前置申明中指定其他布局
- 若不需要
layout
布局,则在文章的front-matter
部分设置layout:false
配置source下文章的front-matter
部分:
- 不设置
layout
=> 使用theme layout下的布局- 文章直接在source目录下 => 使用 page 布局
- 文章在source目录下的某folder下 => 使用同folder名的布局
layout: xxx
=> 使用theme layout下xxx布局layout: false
=> 不使用theme,直接为post render的结果- 只要
layout
未设置为false,theme layout下有layout模板都会和其他布局结合使用
4. 举例:某theme的layout目录下
<!-- layout.ejs -->
<html>
<body><%- body %></body>
</html>
<!-- index.ejs -->
<h1>Welcome!</h1>
<!-- page.ejs -->
<h1><%=page.title%><h1>
<p><%-page.content%></p>
<!-- post.ejs -->
<h1><%=page.title%><h1>
<%- list_tags(page.tags)%>
<p><%-page.content%></p>
<!-- about.ejs -->
<h1>About</h1>
<p><%-page.content%></p>
...
文章 | front-matter 中布局设置 |
theme下布局 |
---|---|---|
/source/about.md |
不设置layout |
page.ejs+layout.ejs |
/source/about.md |
layout: false |
不使用布局,直接render的结果 |
/source/about.md |
layout: about |
about.ejs+layout.ejs |
/source/about/about.md |
不设置layout |
about.ejs+layout.ejs |
/source/about/about.md |
layout: false |
不使用布局,直接render的结果 |
/source/about/about.md |
layout: page |
page.ejs+layout.ejs |
5. Hexo Variables
以下这些变量,来自`Locals`,在theme的布局文件render时传递进去(具体可参考/node_modules/hexo/lib/theme/view.js
)
layoutView.render(layoutLocals, callback);
5.1. config 配置变量
- /_config.yml =>
config.xxx
网站配置 - /themes/[themeName]/_config.yml =>
theme.xxx
主题配置,继承自网站配置
5.2 site 网站变量
- /source/_data/xxx.yml =>
site.data.xxx
- 存放的是通过遍历
db.model('Data')
生成的Key-value
对象(即{}对象)
- 存放的是通过遍历
site.pages
- 包含source下非
_posts
,_drafts
,_data
- 存放的是
db.model('Page').find(query)
返回的Query
对象
- 包含source下非
site.posts
- 包含source下
_posts
,_drafts
- 存放的是
db.model('Post').find(query)
返回的Query
对象
- 包含source下
site.tags
- 存放的是
db.model('Tag')
返回的Model
对象 - Schema字段:name
- virtual字段:slug,path,permalink,posts,length
- 存放的是
site.categories
- 存放的是
db.model('Categories')
返回的Model
对象 - Schema字段:name,parent
- virtual字段:slug,path,permalink,posts,length
- 存放的是
5.3 page 页面变量
针对该页面的内容以及 front-matter
所设定的变量
- 一般包含:
page.title
文章标题page.date
文章建立日期page.updated
文章更新日期page.comments
留言是否开启page.layout
布局名称page.source
文章在项目source/下的路径+文件名page.slug
page.path
文章网址(不含根路径),通常在主题中使用url_for(page.path)page.raw
文章原始所有内容page._content
文章正文原始内容page.content
文章正文解析后的内容page.excerpt
文章摘要 (正文<!-- more -->
前面的解析内容)page.more
除了摘要的其他内容 (正文<!-- more -->
后面的解析内容)- 引用类型:
page.permalink
文章永久网址page.full_source
文章完整原始路径
- 其他定义在
front-matter
中的字段信息
- post页面,新增:
page.published
文章非草稿为true- 引用类型:
page.path
page.asset_dir
page.tags
page.categories
- 若使用了Pagination (参考node module:
hexo-pagination
),增加:page.per_page
每一页显示的文章数page.total
文章数量page.current
当前页码page.current_url
当前页的URLpage.prev
前一页页码,如果为第一页,该值为0page.next
后一页页码,如果为最后一页,则为0page.prev_link
前一页URL,如果为第一页,则为''
page.next_link
后一页URL,如果为最后一页,则为''
page.posts
当前页的文章
- 若使用了Archive (
hexo-generator-archive
),增加:page.archive
为truepage.year
归档年份(4位)page.month
归档月份(1~12)page.day
归档日(1~31)
- 若使用了Category (
hexo-generator-category
),增加:page.category
分类名称
- 若使用了Index (
hexo-generator-index
),增加:page.__index
为true
5.4 其他
path
当前页面的路径(不含根路径)url
当前页面的完整网址env
环境变量_
Lodash 函数库
6. Hexo Helper
源码在/node_modules/hexo/lib/plugins/helper
下,可在theme的布局文件中直接使用
6.1 URL
url_for
返回一个带root路径的url,eg:<%= url_for(path) %>
relative_url
返回from相对的to路径,eg:<%- relative_url(from, to) %>
gravatar
插入Gravatar图片,eg:<%- gravatar(email, [size])%>
6.2 HTML
partial(name, locals, options)
引入局部模版文件,例如:<!--partial/header.ejs--> <h1 id="logo"><%= config.title %></h1> <!--index.ejs--> <%- partial('partial/header') %> <div id="content">Home page</div> <!-- 生成后: --> <h1 id="logo">My Site</h1> <div id="content">Home page</div>
css(path,...)
,js(path,...)
path开头不是/或任何协议,则会自动加上根路径;没有扩展名则会自动加上
<%- css('style.css') %> // <link rel="stylesheet" href="/style.css" type="text/css"> <%- css(['style.css', 'screen.css']) %> // <link rel="stylesheet" href="/style.css" type="text/css"> // <link rel="stylesheet" href="/screen.css" type="text/css"> <%- js('script.js') %> // <script type="text/javascript" src="/script.js"></script> <%- js(['script.js', 'gallery.js']) %> // <script type="text/javascript" src="/script.js"></script> // <script type="text/javascript" src="/gallery.js"></script>
link_to(path,[text],[options])
,mail_to(path,[text],[options])
<%- link_to(‘http://www.google.com‘, ‘Google’, {external: true, class: “link”}) %> // Google <%- mail_to(‘a@abc.com’, ‘Email’) %> // Email
image_tag(path,[options])
,favicon_tag(path)
,feed_tag(path,[options])
6.3.Condition
参考 is.js
is_current()
is_post()
is_page()
is_archive()
is_year()
- ...
6.4 String & Date & Number
- String:
trim
,strip_html
,titlecase
word_wrap
分割字符串,使每行长度不超过length
truncate(text, length)
移除超过length
长度的字符串
- Date:
date(date,[format])
,date_xml(date)
,time(date,[format])
,full_date(date,[format])
date
可以是 UNIX 时间、ISO 字符串、Date 对象或 [Moment.js] 对象。
- Number
number_format(number,[options])
options
:- precision 数字精度 (false 或非负整数,默认为
false
) - delimiter 千位数分隔符号 (默认为
,
) - separator 整数和小数之间的分隔符号 (默认为
.
)
- precision 数字精度 (false 或非负整数,默认为
<!-- String -->
<%- strip_html('It's not <b>important</b> anymore!') %>
// It's not important anymore!
<%- titlecase('this is an apple') %>
// This is an Apple
<%- word_wrap('Once upon a time', 8) %>
// Once upon\n a time
<%- truncate('Once upon a time in a world far far away', 16) %>
// Once upon a time
<!-- Date -->
<%- date(Date.now()) %>
// Jan 1, 2015
<%- date(Date.now(), 'YYYY/M/D') %>
// 2015/1/1
<%- date_xml(Date.now()) %>
// 2015-01-01T00:00:00.000Z
<%- time(Date.now()) %>
// 13:05:12
<%- time(Date.now(), 'h:mm:ss a') %>
// 1:05:12 pm
<%- full_date(new Date()) %>
// Jan 1, 2013 0:00:00
<%- full_date(new Date(), 'dddd, MMMM Do YYYY, h:mm:ss a') %>
// Tuesday, January 1st 2013, 12:00:00 am
6.5 Parse
markdown
使用node module
markdown
解析字符串<%- markdown('make me **strong**') %> // make me <strong>strong</strong>
- render
- 使用hexo注册的renderer解析字符串
render(str, engine, [options])
6.6 List
list_categories([categories],[options])
,list_tags([tags],[options])
options
相同配置:- orderby :默认按name
- order :1, asc 升序;-1, desc 降序,默认为1
- show_count :显示每个标签的文章总数 ,默认true
- style :显示方式。默认为list(无序列表li)
- separator: 分隔符号。只有在 style 不是 list 时有用
- class :默认为category或tag
- transform :修正title显示的函数
- list_categories 特有的
options
:- depth:显示的分类层级 (0 显示所有层级的分类;-1 显示不分层级;1 只显示第一层的分类)
- list_tags 特有的
options
:- amount: 要显示的标签数量(0 = 无限制) amount | 要显示的标签数量(0 = 无限制) | 0
list_archives([options])
,list_posts([options])
options
相同配置:- order
- style
- separator
- class
- transform
- class (默认为archive或post)
- list_archives 特有的
options
:- type:可为 yearly 或 monthly (默认为monthly)
- show_count:显示每个归档的文章总数 (默认为true)
- format:日期格式
- list_posts 特有的
options
:- orderby:文章排列方式 (默认按 date)
- amount:显示的文章数量(0 = 无限制)
6.7 Special
tagcloud([tags], [options])
参数 描述 默认值 min_font 最小字体尺寸 10 max_font 最大字体尺寸 20 unit 字体尺寸的单位 px amount 标签总量 40 orderby 标签排列方式 name order 标签排列顺序。1, sac 升序;-1, desc 降序 1 color 使用颜色 false start_color 开始的颜色 / end_color 结束的颜色 / paginator(options)
插入分页链接参数 描述 默认值 base 基础网址 / format 网址格式 page/%d/ total 分页总数 1 current 目前页数 0 prev_text 上一页链接的文字 Prev next_text 下一页链接的文字 Next space 空白文字 / prev_next 显示上一页和下一页的链接 true end_size 显示于两侧的页数 1 mid_size 显示于中间的页数 2 show_all 显示所有页数 false search_form(options)
插入 Google 搜索框参数 描述 默认值 class 表单的 class name search-form text 搜索提示文字 Search button 显示搜索按钮。此参数可为布尔值(boolean)或字符串,当设定是字符串的时候,即为搜索按钮的文字。 false toc(str, [options])
解析内容中的标题标签 (h1~h6) 并插入目录参数 描述 默认值 class Class 名称 toc list_number 显示编号 true
6.8 其他
请参看源码
Hexo标签插件
源码在/node_modules/hexo/lib/plugins/tag
下
References
- Hexo 文档
- Hexo 博文