搭建Hexo博客进阶篇---主题自定义(三)

本文续接 Hexo 文档讲解篇

Yelee主题介绍

更换主题

创建 Hexo 主题非常容易,您只要在 themes 文件夹内,新增一个任意名称的文件夹,并修改 _config.yml 内的 theme 设定,即可切换主题。一个主题可能会有以下的结构:

.
├── _config.yml      //这个是主题配置项文件
├── languages        //这个是语言文件
├── layout           //这个是模板文件
├── scripts           
└── source          

盛年不重来,一日难再晨。及时当勉励,岁月不待人

  1. Layout

     布局文件夹。用于存放主题的模板文件,决定了网站内容的呈现方式,Hexo 内建 Swig 模板引擎,您可以另外安装插件来获得 `EJS`、`Haml` 或 `Jade` 支持,Hexo 根据模板文件的扩展名来决定所使用的模板引擎,例如:
    layout.ejs   - 使用 EJS
    layout.swig  - 使用 Swig

    yelle主题采用的是EJS模板,EJS模板可以参考 ejs中文文档

  2. source

    资源文件夹,除了模板以外的 Asset,例如 CSS、JavaScript 文件等,都应该放在这个文件夹中。文件或文件夹开头名称为 _(下划线线)或隐藏的文件会被忽略。

    如果文件可以被渲染的话,会经过解析然后储存到 public 文件夹,否则会直接拷贝到 public 文件夹。

模板

模板决定了网站内容的呈现方式,每个主题至少都应包含一个 index 模板,以下是各页面相对应的模板名称:

模板用途回调
index首页
post文章index
page分页index
archive归档index
category分类归档archive
tag标签归档archive

布局

如果页面结构类似,例如两个模板都有页首(Header)和页脚(Footer),您可考虑通过「布局」让两个模板共享相同的结构。一个布局文件必须要能显示 body 变量的内容,如此一来模板的内容才会被显示,举例来说:

{% codeblock index,ejs %}
index
{% endcodeblock %}

{% codeblock layout.ejs %}
<!DOCTYPE html>
<html>
<body><%- body %></body>
</html>
{% endcodeblock %}

生成 :

<!DOCTYPE html>
<html>
<body>index</body>
</html>

每个模板都默认使用 layout 布局,您可在 front-matter 指定其他布局,或是设为 false 来关闭布局功能,您甚至可在布局中再使用其他布局来建立嵌套布局。

局部模板

局部模板让您在不同模板之间共享相同的组件,例如页首(Header)、页脚(Footer)或侧边栏(Sidebar)等,可利用局部模板功能分割为个别文件,让维护更加便利。举例来说:
{% codeblock partial/header.ejs %}
<h1 id="logo"><%= config.title %></h1>
{% endcodeblock %}

{% codeblock index.ejs %}
<%- partial('partial/header') %>
<div id="content">Home page</div>
{% endcodeblock %}

生成:

<h1 id="logo">My Site</h1>
<div id="content">Home page</div>

局部变量

您可以在局部模板中指定局部变量并使用。
{% codeblock partial/header.ejs %}
<h1 id="logo"><%= title></h1>
{% endcodeblock %}

{% codeblock index.ejs %}
<%- partial('partial/header', {title: 'Hello World'}) %>
<div id="content">Home page</div>
{% endcodeblock %}

生成:

<h1 id="logo">Hello World</h1>
<div id="content">Home page</div>

优化

如果您的主题太过于复杂,或是需要生成的文件量太过于庞大,可能会大幅降低性能,除了简化主题外,您可以考虑 Hexo 2.7 新增的局部缓存(Fragment Caching) 功能。

本功能借鉴于 Ruby on Rails,它储存局部内容,下次便能直接使用缓存内容,可以减少文件夹查询并使生成速度更快。

它可用于页首、页脚、侧边栏等文件不常变动的位置,举例来说:

<%- fragment_cache('header', function(){
  return '<header></header>';
});

如果您使用局部模板的话,可以更简单:

<%- partial('header', {}, {cache: true});

但是,如果您开启了 relative_link 参数的话,请勿使用局部缓存功能,因为相对链接在每个页面可能不同。

变量

这些变量很重要,因为将来自定义ejs,加入新的页面的时候,都需要了解这些变量

全局变量

变量描述
site网站变量
page针对该页面的内容以及 front-matter 所设定的变量。
config网站配置
theme主题配置。继承自网站配置。
_ (单下划线)Lodash 函数库
path当前页面的路径(不含根路径)
url当前页面的完整网址
env环境变量

网站变量

变量描述
site.posts所有文章
site.pages所有分页
site.categories所有分类
site.tags所有标签

页面变量

变量描述
site.posts所有文章
site.pages所有分页
site.categories所有分类
site.tags所有标签

A、页面

变量描述
page.title页面标题
page.date页面建立日期(Moment.js 对象)
page.updated页面更新日期(Moment.js 对象)
page.comments留言是否开启
page.layout布局名称
page.content页面的完整内容
page.excerpt页面摘要
page.more除了页面摘要的其余内容
page.source页面原始路径
page.full_source页面的完整原始路径
page.path页面网址(不含根路径)。我们通常在主题中使用 url_for(page.path)。
page.permalink页面的完整网址
page.prev上一个页面。如果此为第一个页面则为 null。
page.next下一个页面。如果此为最后一个页面则为 null。
page.raw文章的原始内容
page.photos文章的照片(用于相簿)
page.link文章的外部链接(用于链接文章)

B、文章

变量描述
page.published如果该文章已发布则为True
page.categories该文章的所有分类
page.tags该文章的所有标签

C、首页

变量描述
page.per_page每页显示的文章数量
page.total总文章数
page.current目前页数
page.current_url目前分页的网址
page.posts本页文章
page.prev上一页的页数。如果此页是第一页的话则为 0。
page.prev_link上一页的网址。如果此页是第一页的话则为 ''。
page.next下一页的页数。如果此页是最后一页的话则为 0。
page.next_link下一页的网址。如果此页是最后一页的话则为 ''。
page.path当前页面的路径(不含根目录)。我们通常在主题中使用 url_for(page.path)。

D、归档

变量描述
page.archive等于 true
page.year年份归档 (4位)
page.month月份归档 (没有前导零的2位数)

E、分类

变量描述
page.category分类名称

F、标签

变量描述
page.tag标签名称

辅助函数

辅助函数帮助您在模版中快速插入内容。辅助函数不能在源文件中使用。

网址

A、url_for

在路径前加上根路径,从 Hexo 2.7 开始您应该使用此函数而不是 config.root + path

<%- url_for(path) %>

<%- url_for(/2017/02/20/HexoBlog/) %>

B、relative_url

取得与 from 相对的 to 路径。

<%- relative_url(from, to) %>

C、gravatar

插入 Gravatar 图片。
如果你不指定 options 参数,将会应用默认参数。否则,你可以将其设置为一个数字,这个数字将会作为 Gravatar 的大小参数。最后,如果你设置它一个对象,它将会被转换为 Gravatar 的一个查询字符串参数。

<%- gravatar(email, [options]) %>;

示例:

<%- gravatar('a@abc.com') %>
// http://www.gravatar.com/avatar/b9b00e66c6b8a70f88c73cb6bdb06787
<%- gravatar('a@abc.com', 40) %>
// http://www.gravatar.com/avatar/b9b00e66c6b8a70f88c73cb6bdb06787?s=40
<%- gravatar('a@abc.com' {s: 40, d: 'http://example.com/image.png'}) %>
// http://www.gravatar.com/avatar/b9b00e66c6b8a70f88c73cb6bdb06787?s=40&d=http%3A%2F%2Fexample.com%2Fimage.png

<%- gravatar('a@abc.com') %>
<%- gravatar('a@abc.com', 40) %>
<%- gravatar('a@abc.com' {s: 40, d: 'http://example.com/image.png'}) %>

注意了没,这个不是在文中中使用的哦,这个是ejs模板语法,要在ejs模板文件中使用

HTML 标签

A、css

载入 CSS 文件。path 可以是数组或字符串,如果 path 开头不是 / 或任何协议,则会自动加上根路径;如果后面没有加上 .css 扩展名的话,也会自动加上。

<%- css(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">

B、js

载入 JavaScript 文件。path 可以是数组或字符串,如果 path 开头不是 / 或任何协议,则会自动加上根路径;如果后面没有加上 .js 扩展名的话,也会自动加上。

<%- js(path, ...) %>

示例:

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

C、link_to

插入链接。

<%- link_to(path, [text], [options]) %>
参数描述默认值
external在新视窗打开链接false
classClass 名称
idID

示例:

<%- link_to('http://www.google.com') %>
// <a href="http://www.google.com" title="http://www.google.com">http://www.google.com</a>
<%- link_to('http://www.google.com', 'Google') %>
// <a href="http://www.google.com" title="Google">Google</a>
<%- link_to('http://www.google.com', 'Google', {external: true}) %>
// <a href="http://www.google.com" title="Google" target="_blank" rel="external">Google</a>

D、mail_to

插入电子邮箱链接。

<%- mail_to(path, [text], [options]) %>
参数描述
classClass 名称
idID
subject邮件主题
cc抄送(CC)
bcc密送(BCC)
body邮件内容

示例:

<%- mail_to('a@abc.com') %>
// <a href="mailto:a@abc.com" title="a@abc.com">a@abc.com</a>
<%- mail_to('a@abc.com', 'Email') %>
// <a href="mailto:a@abc.com" title="Email">Email</a>

E、image_tag

插入图片。

<%- image_tag(path, [options]) %>
参数描述
alt图片的替代文字
classClass 名称
idID
width图片宽度
height图片高度

F、favicon_tag

插入favicon。

<%- favicon_tag(path) %>

G、feed_tag

插入 feed 链接。

<%- feed_tag(path, [options]) %>
参数描述默认值
titleFeed标题
typeFeed类型 atom

条件函数

A、is_current

检查 path 是否符合目前页面的网址。开启 strict 选项启用严格比对。

<%- is_current(path, [strict]) %>

B、is_home

检查目前是否为首页。

<%- is_home() %>

C、is_post

检查目前是否为文章。

<%- is_post() %>

D、is_archive

检查目前是否为存档页面。

<%- is_archive() %>

E、is_year

检查目前是否为年度归档页面。

<%- is_year() %>

F、is_month

检查目前是否为月度归档页面。

<%- is_month() %>

G、is_category

检查目前是否为分类归档页面。
如果给定一个字符串作为参数,将会检查目前是否为指定分类。

<%- is_category() %>
<%- is_category('hobby') %>

H、is_tag

检查目前是否为标签归档页面。
如果给定一个字符串作为参数,将会检查目前是否为指定标签。

<%- is_tag() %>
<%- is_tag('hobby') %>

字符串处理

A、trim

清除字符串开头和结尾的空格。

<%- trim(string) %>

B、strip_html

清除字符串中的 HTML 标签。

<%- strip_html(string) %>

示例:

<%- strip_html('It's not <b>important</b> anymore!') %>
// It's not important anymore!

C、titlecase

把字符串转换为正确的 Title case。

<%- titlecase(string) %>

示例:

<%- titlecase('this is an apple') %>
#   This is an Apple

D、markdown

使用 Markdown 解析字符串。

<%- markdown(str) %>

示例:

<%- markdown('make me **strong**') %>
// make me <strong>strong</strong>

E、render

解析字符串。

<%- render(str, engine, [options]) %>

F、word_wrap

使每行的字符串长度不超过 length。length 预设为 80。

<%- word_wrap(str, [length]) %>

示例:

<%- word_wrap('Once upon a time', 8) %>
// Once upon\n a time

G、truncate

移除超过 length 长度的字符串。

<%- truncate(text, length) %>

示例:

<%- truncate('Once upon a time in a world far far away', {length: 17}) %>
// Once upon a ti...
<%- truncate('Once upon a time in a world far far away', {length: 17, separator: ' '}) %>
// Once upon a...
<%- truncate('And they found that many people were sleeping better.', {length: 25, omission: '... (continued)'}) %>
// And they f... (continued)

模板

A、partial

载入其他模板文件,您可在 locals 设定区域变量。

<%- partial(layout, [locals], [options]) %>
参数描述默认值
cache缓存(使用 Fragment cache)false
only限制局部变量。在模板中只能使用 locals 中设定的变量。false

B、fragment_cache

局部缓存。它储存局部内容,下次使用时就能直接使用缓存。

<%- fragment_cache(id, fn);

示例:

<%- fragment_cache('header', function(){
  return '<header></header>';
}) %>

日期与时间

date

插入格式化的日期。date 可以是 UNIX 时间、ISO 字符串、Date 对象或 Moment.js 对象。format 默认为 date_format 配置信息。

<%- date(date, [format]) %>

示例:

<%- date(Date.now()) %>
// 2013-01-01
<%- date(Date.now(), 'YYYY/M/D') %>
// Jan 1 2013
date_xml

插入 XML 格式的日期。date 可以是 UNIX 时间、ISO 字符串、Date 对象或 Moment.js 对象。

<%- date_xml(date) %>

示例:

<%- date_xml(Date.now()) %>
// 2013-01-01T00:00:00.000Z
time

插入格式化的时间。date 可以是 UNIX 时间、ISO 字符串、Date 对象或 Moment.js 对象。format 默认为 time_format 配置信息。

<%- time(date, [format]) %>

示例:

<%- time(Date.now()) %>
// 13:05:12
<%- time(Date.now(), 'h:mm:ss a') %>
// 1:05:12 pm
full_date

插入格式化的日期和时间。date 可以是 UNIX 时间、ISO 字符串、Date 对象或 Moment.js 对象。format 默认为 date_format + time_format。

<%- full_date(date, [format]) %>

示例:

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

Moment.js 函数库。

列表

list_categories

插入分类列表。

<%- list_categories([options]) %>
参数描述默认值
orderby分类排列方式name
order分类排列顺序。1, asc 升序;-1, desc 降序。1
show_count显示每个分类的文章总数true
style分类列表的显示方式。使用 list 以无序列表(unordered list)方式显示。list
separator分类间的分隔符号。只有在 style 不是 list 时有用。,
depth要显示的分类层级。0 显示所有层级的分类;-1 和 0 很类似,但是显示不分层级;1 只显示第一层的分类。0
class分类列表的 class 名称。category
transform改变分类名称显示方法的函数
list_tags

插入标签列表。

<%- list_tags([options]) %>
参数描述默认值
orderby标签排列方式name
order标签排列顺序。1, asc 升序;-1, desc 降序。1
show_count显示每个标签的文章总数true
style标签列表的显示方式。使用 list 以无序列表(unordered list)方式显示。list
separator标签间的分隔符号。只有在 style 不是 list 时有用。
class标签列表的 class 名称。tag
transform改变标签名称显示方法的函数
amount要显示的标签数量(0 = 无限制)0
list_archives

插入归档列表。

<%- list_archives([options]) %>
参数描述默认值
type类型。此设定可为 yearly 或 monthly。monthly
order排列顺序。1, asc 升序;-1, desc 降序。1
show_count显示每个归档的文章总数true
format日期格式MMMM YYYY
style归档列表的显示方式。使用 list 以无序列表(unordered list)方式显示。list
separator归档间的分隔符号。只有在 style 不是 list 时有用。
class归档列表的 class 名称。 archive
transform改变归档名称显示方法的函数
list_posts

插入文章列表。

<%- list_posts([options]) %>
参数描述默认值
orderby文章排列方式date
order文章排列顺序。1, asc 升序;-1, desc 降序。-1
style文章列表的显示方式。使用 list 以无序列表(unordered list)方式显示。list
separator文章间的分隔符号。只有在 style 不是 list 时有用。
class文章列表的 class 名称。 post
amount要显示的文章数量(0 = 无限制)6
transform改变文章名称显示方法的函数
tagcloud

插入标签云。

<%- 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开始的颜色。您可使用十六进位值(#b700ff),rgba(rgba(183, 0, 255, 1)),hsla(hsla(283, 100%, 50%, 1))或 颜色关键字。此变量仅在 color 参数开启时才有用。
end_color结束的颜色。您可使用十六进位值(#b700ff),rgba(rgba(183, 0, 255, 1)),hsla(hsla(283, 100%, 50%, 1))或 颜色关键字。此变量仅在 color 参数开启时才有用。

其他

paginator

插入分页链接。

<%- paginator(options) %>
参数描述默认值
base基础网址/
format网址格式page/%d/
total分页总数1
current目前页数0
prev_text上一页链接的文字。仅在 prev_next 设定开启时才有用。Prev
next_text下一页链接的文字。仅在 prev_next 设定开启时才有用。Next
space空白文字 …
prev_next显示上一页和下一页的链接true
end_size显示于两侧的页数1
mid_size显示于中间的页数2
show_all显示所有页数。如果开启此参数的话,end_size 和 mid_size 就没用了。false
search_form

插入 Google 搜索框。

<%- search_form(options) %>
参数描述默认值
class表单的 class namesearch-form
text搜索提示文字Search
button显示搜索按钮。此参数可为布尔值(boolean)或字符串,当设定是字符串的时候,即为搜索按钮的文字。false
number_format

格式化数字。

<%- number_format(number, [options]) %>
参数描述默认值
precision数字精度。此选项可为 false 或非负整数。false
delimiter千位数分隔符号,
separator整数和小数之间的分隔符号,

示例:

<%- number_format(12345.67, {precision: 1}) %>
// 12,345.68
<%- number_format(12345.67, {precision: 4}) %>
// 12,345.6700
<%- number_format(12345.67, {precision: 0}) %>
// 12,345
<%- number_format(12345.67, {delimiter: ''}) %>
// 12345.67
<%- number_format(12345.67, {separator: '/'}) %>
// 12,345/67
open_graph

插入 open graph 资源。

<%- open_graph([options]) %>
参数描述默认值
title页面标题 (og:title)page.title
type页面类型 (og:type)blog
url页面网址 (og:url)url
image页面图片 (og:image)内容中的图片
site_name网站名称 (og:site_name)config.title
description页面描述 (og:desription)内容摘要或前 200 字
twitter_cardTwitter 卡片类型 (twitter:card)summary
twitter_idTwitter ID(twitter:creator)
twitter_siteTwitter 网站(twitter:site)
google_plusGoogle+个人资料链接
fb_adminsFacebook管理者 ID
fb_app_idFacebook应用程序 ID

last: toc

解析内容中的标题标签 (h1~h6) 并插入目录。

<%- toc(str, [options]) %>
参数描述默认值
classClass 名称toc
list_number显示编号true

示例:

<%- toc(page.content) %>

Hexo页面渲染流程

Yelee目录说明

看一下,Yelee主题的文件目录:

clipboard.png

根据这个目录我们来分析Hexo是如何工作的
我们都知道两个命名,一个是 hexo new page name ,一个是 hexo new post name

  • hexo new page name
    创建一个新页面 , 页面具体渲染模板在Theme主题下的layout文件夹下面

  • hexo new post name/hexo new post draft
    是创建一篇新文章,新文章保存在source/_posts目录下,如果是hexo new draft name,则是保存在source/_drafts目录下,那么根据scaffolds的模板,可以确定新建的文章或者草稿的基本配置

看下图:

clipboard.png 功能实现

下面看一下我们的,引擎模板文件:
入口模板

clipboard.png

具体实现

clipboard.png

想要自定义404吗?想要写一个个性化的about me吗?想要实现完全自定义吗?哈哈这个你还是得去学习 EJS

如何实现用D3-cloud来展现标签云

有没有感觉 Hexo 自带的 tag-cloud 标签云太丑陋了呢!作为一个前端程序员,对审美可是有着比较高的要求,
github 上面有 d3-cloud 这个项目,那么我们来玩一玩吧,把github上的d3-cloud项目在我们的hexo博客中使用哈!
hexo是静态博客,所以最后在网上看到的都是静态的内容,也就是说,我们的看到的标签云也是静态的已经生成好的内容,并不会随着刷新页面而重新计算生成另外样式的标签云。
当然d3-cloud这个项目,提供了浏览器端和node端运行的版本,见它的例子,我们可以在客户端运行,也可以在服务端作为 node运行。

具体操作流程

  1. 找到关于tagcloud模板文件以及JS和CSS
    我上面说的,应该很容易找到吧,我已经找到了呀

  2. 安装模块

    npm install canvas --save
    npm install d3-cloud --save
    npm install d3 --save
  3. 找到文件: 你的 blog项目 -> node_modules -> hexo ->plugins -> helper -> index.js

    var tagcloud = require('./tagcloud');
    helper.register('tagcloud', tagcloud);
    helper.register('tag_cloud', tagcloud);
    ​
    //修改为下面的代码:目的是不直接修改tagcloud.js,保留代码
    var tagcloud = require('./tagcloud');
    var tagcloudd3 = require('./tagcloudd3');
    helper.register('tagcloud', tagcloudd3);
    helper.register('tag_cloud', tagcloudd3);
  4. 新建文件tagcloudd3.js :位置在blog项目 -> node_modules -> hexo ->plugins -> helper -> tagcloudd3.js
    tagcloud3.js的内容如下:

    'use strict';
    ​    
    var Canvas = require("canvas");
    var cloud = require("d3-cloud");
    var d3 = require("d3");
    ​    
    var layout = cloud()//利用d3-cloud计算每个标签的位置
        .size([600, 400])
        .canvas(function() { return new Canvas(1, 1); })
        .padding(7)
        .rotate(function() { return ~~(Math.random() * 2) * 90; })
        .font("Impact")
        .fontSize(function(d) { return d.size; });
    var fill = d3.scale.category20();//利用d3的接口给每个标签颜色
    ​
    function tagcloudHelper(tags){
      /****与tagcloud.js一样,获得tags 开始***/
      if ((!tags || !tags.hasOwnProperty('length'))){
        tags = this.site.tags;
      }
    ​
      if (!tags || !tags.length) return '';
     
    var result = [];
    ​
    tags = tags.sort('name', 1);
    ​
    // Ignore tags with zero posts
    tags = tags.filter(function(tag){
    return tag.length;
    });
     /****与tagcloud.js一样,获得tags 结束***/
     
    //计算标签出现次数最大值,比如,博客中一共有两个标签,一个是hello,一个是world,hello出现2次,world 出现1次,那么maxsize就是2
    var maxsize = 1;
    ​
    tags.sort('length').forEach(function(tag){
    var length = tag.length;
    if(length > maxsize)
        maxsize = length;
    });
    ​
     //构建传入layout的words
    var arr = [],words;
    tags.forEach(function(tag){
     arr.push({"name": tag.name,"num" : tag.length});
    });
    words = arr.map(function(d) {
      var text = d.name.replace(/[^\x00-\xff]/g,"ab");//对中文的投机处理,用ab代替中文字符
      return {name:d.name, text: text, size : Math.log(d.num)/(Math.log(maxsize)-Math.log(1)) * 15 + 30};//size的计算取对数,是为了让标签之间的大小相对平均一些。因为博客侧重前端内容,所以某一些标签会比较多,标签最大最小次数的差距会比较大。
     
    });
    layout.words(words);
    layout.start();
    ​
    result.push('<svg width="600" height="400"><g transform="translate(300,200)">');
    words.forEach(function(word,i){
    ​
    result.push(
      '<text text-anchor="middle" fill="'+fill(i)+'" transform="translate('+word.x+','+word.y+')rotate('+
        word.rotate+')" style="font-size:'+word.size+'px;font-family:Impact">'+
        word.name+
      '</text>'
    );
    });
    result.push('</g></svg>');
     
    return result.join('');
     
    }
    module.exports = tagcloudHelper;
  5. 运行,就可以看到结果啦
    这个我并没有实际操作,原因很简单,最近比较忙,还有一个就是,node安装canvas实在是太麻烦了呀,

这里给出地址:

Yelee 主题配置

关于主题的配置,这篇文章讲的很详细啦,Yelee中文参考手册

关于Hexo,还有最后一篇文章,请看 :搭建Hexo博客进阶篇--API和一些小部件

参考资料

  1. Hexo官方网站 : https://hexo.io/

  2. 利用d3-cloud实现标签云 : http://www.cnblogs.com/lilyimage/p/5207697.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值