Hexo搭建说明

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解析,generatedeploy时会直接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获取 (关于modelHexo变量在后面章节中会介绍)

Read More

在文章正文中使用<!-- more -->进行分割,则正文解析后,分割的前后段部分会分别设置到excerptmore字段,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代码
  • 一些资源文件比如cssjs可以放置在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文件

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中的一些可配置项列举如下:

  1. Site

    • title : 网站的标题
    • subtitle : 网站的子标题
    • description : 网站的描述
    • author : 你的名字
    • language : 网站的语言
    • timezone : 网站的时区,默认电脑的设置,可以在here找到时区列表,例如America/New_York, Japan, and UTC.
  2. URL

    • url : 网站的url
    • root: 网站的根目录
    • permalink: 文件保存在public中得目录格式,默认为 :year/:month/:day/:title/
    • permalink_default: permalink每部分的默认值
  3. 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 来配置路径。(默认:[])
  4. 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:代码块设置
  5. Category&Tag

    • default_category:默认类别 (默认为:uncategorized)
    • category_map:分类别名
    • tag_map:标签别名
  6. Date/Time format (Hero使用Moment.js来处理数据)

    • date_format :日期格式(MMMM D YYYY)
    • time_format 时间格式(H:mm:ss)
  7. Pagination

    • per_page: 每页最大文章数,0隐藏页数 (10)
    • pagination: Pagination目录 (page)
  8. 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.ejsrss2.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>

多说

官网 | API

<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>

Google

官网 | Google 站内搜索博文

  • 在搜索引擎中配置自己的网站,上传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;
      }
      
  • 不嵌入到网站中(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>
      

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

  1. Hexo中的 database,model类是由 node module warehouse 定义的(具体可参考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
  2. 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
  3. 这些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

  1. 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')中的记录
  2. 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
  3. source和theme对象是通过Box调用执行对应的processor (具体可参看/node_modules/hexo/lib/box

    • event:processBefore
    • processor.pattern.match(path) 判断是否可处理
    • 可处理则调用processor.process方法
    • event:processAfter

PS:

  • /source/_post,/source/_draft
    • isRenderable:true => model('Post'),model('Category'),model('Tag'),model('PostTag'),model('PostCategory')
    • asset_dir/ => model('PostAssert')
  • /source/_data => do Render => model('Data')
  • /source下非_开头、非%、非~结尾的文件或文件夹
    • isRenderable:true => model('Page')
    • isRenderable:false => model('Assert')
  • /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') )
  • 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: 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
  • theme view render 布局文件和Generator返回的数据合并渲染出最终文件 (/node_modules/hexo/lib/hexo/theme/index.js)

2.hexo本身renderer处理:

  • htm,html to html [ use plain.js]
  • css to css [ use plain.js ]
  • js to js [ use plain.js ]
  • json to json [ use json.js ]
  • swig to html [ use swig.js ]
  • yml,yaml to json [ use yaml.js ]

3.hexo通过package.json加入的renderer:

  • hexo-renderer-ejs: ejs to html
  • hexo-renderer-marked: md,markdown,mkd,mkdn,mdwn,mdtxt,mdtext to html
  • hexo-renderer-stylus: styl,stylus to css

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部分:

  1. 不设置layout => 使用theme layout下的布局
    • 文章直接在source目录下 => 使用 page 布局
    • 文章在source目录下的某folder下 => 使用同folder名的布局
  2. layout: xxx => 使用theme layout下xxx布局
  3. layout: false => 不使用theme,直接为post render的结果
  4. 只要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对象
  • site.posts
    • 包含source下_posts,_drafts
    • 存放的是 db.model('Post').find(query)返回的Query对象
  • 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 当前页的URL
    • page.prev 前一页页码,如果为第一页,该值为0
    • page.next 后一页页码,如果为最后一页,则为0
    • page.prev_link 前一页URL,如果为第一页,则为''
    • page.next_link 后一页URL,如果为最后一页,则为''
    • page.posts 当前页的文章
  • 若使用了Archive (hexo-generator-archive),增加:
    • page.archive 为true
    • page.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 整数和小数之间的分隔符号 (默认为.)
<!-- 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