# Hexo 使用手册
# 1. Hexo 安装流程
# 1. 基础环境
- 安装 NodeJs
- 安装 hexo-cli : 全局安装
npm install hexo-cli -g
# 2. 初始化项目
- 初始化项目文件夹 :
hexo init blog - 安装 node 依赖:
cd blog && npm install - 启动项目:
hexo server -p port指定本地启动端口,默认为 4000 - 测试指令:
hexo clean && hexo generate && hexo server, 简写:hexo clean && hexo g && hexo s
# 2. Hexo 主题
# 1. shoka 主题安装
hexo-theme-shoka主题安装流程
安装 Hexo
hexo-cli:npm install hexo-cli -g --registry https://registry.npmmirror.com- 创建一个新目录,执行命令
hexo init - 切换到刚刚创建的新目录,将
./_config.yml里面内容清空,并添加下面的内容
# Site | |
title: Hexo | |
subtitle: 'Subtitle' | |
description: 'Description' | |
keywords: | |
author: Author Name | |
language: en | |
timezone: '' | |
# URL | |
## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project' | |
url: https://www.reversesacle.com | |
root: / | |
permalink: :title/ # edit for Theme.shoka | |
permalink_defaults: | |
pretty_urls: | |
trailing_index: true # Set to false to remove trailing 'index.html' from permalinks | |
trailing_html: true # Set to false to remove trailing '.html' from permalinks | |
# Directory | |
source_dir: source | |
public_dir: public | |
tag_dir: tags | |
archive_dir: archives | |
category_dir: categories | |
code_dir: downloads/code | |
i18n_dir: :lang | |
skip_render: | |
# Writing | |
new_post_name: :title.md # File name of new posts | |
default_layout: post | |
titlecase: false # Transform title into titlecase | |
external_link: | |
enable: true # Open external links in new tab | |
field: site # Apply to the whole site | |
exclude: '' | |
filename_case: 0 | |
render_drafts: false | |
post_asset_folder: false | |
relative_link: false | |
future: true | |
highlight: | |
enable: false | |
line_number: true | |
auto_detect: false | |
tab_replace: '' | |
wrap: true | |
hljs: false | |
prismjs: | |
enable: false | |
preprocess: true | |
line_number: true | |
tab_replace: '' | |
# Home page setting | |
# path: Root path for your blogs index page. (default = '') | |
# per_page: Posts displayed per page. (0 = disable pagination) | |
# order_by: Posts order. (Order by date descending by default) | |
index_generator: | |
path: '' | |
per_page: 5 | |
order_by: -date | |
# Category & Tag | |
default_category: uncategorized | |
category_map: | |
tag_map: | |
# Metadata elements | |
## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta | |
meta_generator: true | |
# Date / Time format | |
## Hexo uses Moment.js to parse and display date | |
## You can customize the date format as defined in | |
## https://momentjs.com/docs/#/displaying/format/ | |
date_format: YYYY-MM-DD | |
time_format: HH:mm:ss | |
## updated_option supports 'mtime', 'date', 'empty' | |
updated_option: 'mtime' | |
# Pagination | |
## Set per_page to 0 to disable pagination | |
per_page: 10 | |
pagination_dir: page | |
# Include / Exclude file(s) | |
## include:/exclude: options only apply to the 'source/' folder | |
include: | |
exclude: | |
ignore: | |
# Extensions | |
## Plugins: https://hexo.io/plugins/ | |
## Themes: https://hexo.io/themes/ | |
theme: shoka | |
# Deployment | |
## Docs: https://hexo.io/docs/one-command-deployment | |
deploy: | |
type: git | |
repo: https://github.com/xxx/xxx.github.io.git # 替换为你的仓库地址 | |
branch: main # 或 master,取决于你的 GitHub 仓库设置 |
安装 hexo-theme-shoka 主题
-
安装
hexo-theme-shoka插件git clone https://github.com/amehime/hexo-theme-shoka.git ./themes/shoka
-
安装
hexo-renderer-multi-markdown-it插件npm un hexo-renderer-marked --savenpm install hexo-renderer-multi-markdown-it --ignore-scripts --save --registry https://registry.npmmirror.com
-
安装
hexo-autoprefixer插件npm install hexo-autoprefixer --save --registry https://registry.npmmirror.com
安装hexo-symbols-count-time插件npm install hexo-symbols-count-time --save --registry https://registry.npmmirror.com
-
安装
hexo-feed插件npm install hexo-generator-feed --save-dev --registry https://registry.npmmirror.com
-
安装
hexo-algoliasearch插件npm install hexo-algoliasearch --save --registry https://registry.npmmirror.com
-
安装
hexo-deployer-git插件npm install hexo-deployer-git --save --registry https://registry.npmmirror.com
-
切换到项目的根目录,打开
./_config.yml文件,在文件末添加下面内容
markdown: | |
render: # Render setting | |
html: false # Filter HTML lable | |
xhtmlOut: true # Use '/' to close the sigle lable(<br />) | |
breaks: true # Change the '\n' in text to <br> | |
linkify: true # Change the text form of link to real link | |
typographer: | |
quotes: '“”‘’' | |
plugins: # markdown-it plugins setting | |
- plugin: | |
name: markdown-it-toc-and-anchor | |
enable: true | |
options: # No need to change the below setting, it is default | |
tocClassName: 'toc' | |
anchorClassName: 'anchor' | |
- plugin: | |
name: markdown-it-multimd-table | |
enable: true | |
options: | |
multiline: true | |
rowspan: true | |
headerless: true | |
- plugin: | |
name: ./markdown-it-furigana | |
enable: true | |
options: | |
fallbackParens: "()" | |
- plugin: | |
name: ./markdown-it-spoiler | |
enable: true | |
options: | |
title: "You Know too much" | |
# autoprefixer | |
autoprefixer: | |
exclude: | |
- '*.min.css' | |
# algolia | |
algolia: | |
appId: # Your appId | |
apiKey: # Your apiKey | |
adminApiKey: # Your adminApiKey | |
chunkSize: 5000 | |
indexName: #"shoka" | |
fields: | |
- title # default | |
- path # default | |
- categories # Optional setting | |
- content:strip:truncate,0,2000 | |
- gallery | |
- photos | |
- tags | |
# feed | |
feed: | |
limit: 20 | |
order_by: "-date" | |
tag_dir: false | |
category_dir: false | |
rss: | |
enable: true | |
template: "themes/shoka/layout/_alternate/rss.ejs" | |
output: "rss.xml" | |
atom: | |
enable: true | |
template: "themes/shoka/layout/_alternate/atom.ejs" | |
output: "atom.xml" | |
jsonFeed: | |
enable: true | |
template: "themes/shoka/layout/_alternate/json.ejs" | |
output: "feed.json" |
- 在项目根目录中打开
CMD, 执行hexo clean && hexo g && hexo s启动 - 浏览器访问:
http://localhost:4000
# 2. 插件处理
Algolia 搜索
-
注册 Algolia 账号,官网地址
-
官网完成注册后,进入 welcome 页面→点击 API keys,在 All API Keys 页面处点击 New API key 并填写相关信息
-
在
_config.yml文件中添加对应配置信息
algolia: | |
appId: "Application ID对应码" | |
apiKey: "API Keys页面的All API Keys中新建的API key的对应码" | |
adminApiKey: "Admin API Key对应码" | |
chunkSize: 5000 | |
indexName: "项目索引名称, Indices部分" | |
fields: | |
- title | |
- path | |
- categories | |
- content: strip:truncate,0,4000 | |
- gallery | |
- photos | |
- tags |
- 生成索引信息:在 hexo 根目录右键点击
Git Bash Here,输入hexo clean && hexo g && hexo algolia即可 - (注意) 每当有新文章发布,都需输入
hexo g与hexo algolia指令
Waline评论
-
按照 Reversesacle 大佬的文档进行配置和修改
-
shoka 默认的评论插件
valine由于兼容和LeanCloud不再提供项目部署服务等问题,以及 所以手动切换到 waline -
waline 官方文档,按照文档步骤将后端服务部署到
Vercel中,完成创建数据库和域名绑定 -
修改文件:
./themes/shoka/_config.yml, 删除原有的 valine 配置并添加 waline 配置信息,
# 删除掉 valine 相关的所有内容,并添加下面的内容 | |
# Comments | |
# For more information: https://github.com/amehime/MiniValine | |
waline: | |
# Waline 的服务端地址,刚刚在 Vercel 记录的 DOMAINS 链接或自绑定的域名 | |
serverURL: 'https://xxx.vercel.app' | |
comment: true # 文章评论数统计,填入字符串时会作为 CSS 选择器 | |
pageview: true # 文章浏览量统计,填入字符串时会作为 CSS 选择器 | |
lang: 'zh-CN' # 多语言支持 | |
dark: 'auto' # 暗黑模式适配,true | false | 'auto' | |
commentSorting: 'latest' # 评论列表排序方式。可选值: 'latest', 'oldest', 'hottest' | |
login: 'disable' # 登录模式状态,'enable' | 'disable' | 'force' | |
wordLimit: 0 # 评论字数限制。填入单个数字时为最大字数限制。设置为 0 时无限制 | |
pageSize: 10 # 评论列表分页,每页条数 | |
imageUploader: false # 自定义图片上传方法。 | |
highlighter: true # 代码高亮 | |
texRenderer: false # 自定义 tex 渲染 | |
search: false # 自定义搜索功能 | |
noCopyright: true # 是否隐藏页脚版权信息 | |
recaptchaV3Key: false # Google 提供的验证码服务 | |
turnstileKey: false # Cloudflare 提供的验证码服务 | |
reaction: false # 为文章增加表情互动功能 | |
meta: ['nick', 'mail', 'link'] # 评论者相关属性。可选值: 'nick', 'mail', 'link' | |
requiredMeta: ['nick', 'mail'] # 设置必填项,默认匿名 ([]) | |
locale: { | |
placeholder: "欢迎评论" | |
} # https://waline.js.org/cookbook/customize/locale.html | |
emoji: [ | |
https://unpkg.com/@waline/emojis@1.2.0/alus | |
] # 表情 | |
# 滑动页面到最后面,找到 vendors 区域,按下面条件修改 | |
将 | |
comment: css/comment.css | |
修改为 | |
waline_crop: css/comment.css | |
waline: combine/npm/@waline/client@3.1.3/dist/waline.min.css,npm/@waline/client@3.1.3/dist/waline-meta.min.css | |
将 | |
valine: gh/amehime/MiniValine@4.2.2-beta10/dist/MiniValine.min.js | |
修改为 | |
waline: npm/@waline/client@3.1.3/dist/waline.umd.min.js |
- 修改文件:
./themes/shoka/layout/_macro/comment.njk, 删除所有内容,添加下面的内容
// 由于 hexo 渲染识别问题,需要将下方内容中的 \% 替换为 \ 再添加到 comment.njk 文件中 | |
{\% macro render() %} | |
{\%- if page.comment !== false %} | |
<div class="wrap" id="comments"> | |
{\%- if theme.waline.serverURL %} | |
<div id="waline-comment"></div> | |
{\%- endif %} | |
</div> | |
{\%- endif %} | |
{\% endmacro %} |
-
修改文件:
./themes/shoka/layout/_macro/widgets.njk28 行左右<ul class="leancloud-recent-comment"></ul>==><ul id="waline-recent"></ul> -
修改文件:
./themes/shoka/layout/_partials/layout.njk111 行左右valine: {{ page.valine|safedump if page.valine else "true" }},{%- endif %}==>waline: {{ page.waline|safedump if page.waline else "true" }},{%- endif %} -
修改文件:
./themes/shoka/layout/_partials/post/footer.njk第 13 行左,将{%- if ~ 到 {%- endif %}区域,更改为下面内容:{%- if theme.valine.appId and theme.valine.appKey and theme.valine.visitor %}==>{%- if theme.waline.serverURL and theme.waline.pageview %}<span id=标签修改为<span class="item" data-path="{{ '/' + (page.path | replace('index.html', '')) }}" title="{{ __('post.views') }}"><span class="leancloud-visitors-count"></span>==><span class="waline-pageview-count"></span>
-
修改文件:
./themes/shoka/scripts/generaters/script.js, 第 23 行到 38 行左右,修改为下面的内容
js: { | |
waline: theme.vendors.js.waline, | |
chart: theme.vendors.js.chart, | |
copy_tex: theme.vendors.js.copy_tex, | |
fancybox: theme.vendors.js.fancybox | |
}, | |
css: { | |
waline_crop: theme.css + "/comment.css", | |
waline: theme.vendors.css.waline, | |
katex: theme.vendors.css.katex, | |
mermaid: theme.css + "/mermaid.css", | |
fancybox: theme.vendors.css.fancybox | |
}, | |
loader: theme.loader, | |
search : null, | |
waline: theme.waline, | |
quicklink: { | |
timeout : theme.quicklink.timeout, | |
priority: theme.quicklink.priority | |
} |
- 修改文件:
./themes/shoka/source/css/comment.styl修改第 5 行为下面内容
@import "_common/components/third-party/waline"; |
- 修改文件:
./themes/shoka/source/js/_app/utils.js第 45 行左右的vendorCSS函数后添加下面内容
const vendorCss_body = function(type) { | |
var name = 'css-' + type; | |
if(undefined != document.querySelector('body link' + '.' + name)){ | |
return; | |
} | |
var new_link = document.body.createChild('link', { | |
rel: 'stylesheet', | |
href: assetUrl("css", type) | |
}); | |
new_link.setAttribute('class',name); | |
}; |
- 修改文件:
./themes/shoka/source/js/_app/page.js第 408 行左右的loadComments函数,修改函数内部的vendorCss('valine');,更改为下面内容
// 有两处都需要修改 | |
vendorCss_body('waline_crop'); |
- 修改文件:
./themes/shoka/source/js/_app/pjax.js
// 第 49 行左右的 `siteRefresh` 函数,在其前面添加下面的两个函数 | |
const recent_comment_create = function(comments) { | |
var waline_recent = $('#waline-recent'); | |
if(waline_recent.hasChildNodes()) | |
{ | |
var parent_label = waline_recent.parentNode; | |
parent_label.removeChild(waline_recent); | |
parent_label.appendChild(waline_recent.cloneNode(false)); | |
} | |
var len = comments.length; | |
if(0 == len){ return; } | |
var getDate = function(timestamp) { | |
var date = new Date(timestamp); | |
var year = date.getFullYear(); | |
var month = date.getMonth() + 1; | |
var day = date.getDate(); | |
month = month < 10 ? '0' + month : month; | |
day = day < 10 ? '0' + day : day; | |
return year + '-' + month + '-' + day; | |
}; | |
var getText = function(new_text) { | |
new_text = new_text.replace(/\n/g, ''); | |
var place_text = ''; | |
var text_size = 100; | |
var len = new_text.length; | |
var smallest = [ | |
'r','g','t','j', | |
'f','i','v','y', | |
'x','z',',','.', | |
'!',';',':','"', | |
'(',')','-',' ' | |
]; | |
var regex = /[a-z]/; | |
var regex2 = /[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/; | |
var regex3 = /[A-Z]/; | |
for(var i = 0,count = 0;i < len;) | |
{ | |
if('<' == new_text[i]) | |
{ | |
while(i < len && '>' != new_text[i++]); | |
continue; | |
} | |
if(count >= text_size) | |
{ | |
if(i < len){ place_text += '...'; } | |
break; | |
} | |
var c = new_text[i++]; | |
if(c in smallest){ count += 1; } | |
else if(regex.test(c) || regex2.test(c)){ count += 2; } | |
else if(regex3.test(c)){ count += 2.5; } | |
else{ count += 3.5; } | |
place_text += c; | |
} | |
return place_text; | |
}; | |
var i = 0; | |
var li_label = document.createElement('li'); | |
li_label.setAttribute('class','item'); | |
var a_label = document.createElement('a'); | |
a_label.setAttribute('href',comments[i].url); | |
a_label.setAttribute('data-pjax-state',''); | |
var breadcrumb_label = document.createElement('span'); | |
breadcrumb_label.style.marginBottom = '-5.3px'; | |
breadcrumb_label.innerText = comments[i].nick + ' @ ' + getDate(comments[i].time); | |
var text_span = document.createElement('span'); | |
text_span.innerText = getText(comments[i].comment); | |
waline_recent = $('#waline-recent'); | |
a_label.appendChild(breadcrumb_label); | |
a_label.appendChild(text_span); | |
li_label.appendChild(a_label); | |
waline_recent.append(li_label); | |
for(++i;i < len;++i) | |
{ | |
var new_li_label = li_label.cloneNode(false); | |
var new_a_label = a_label.cloneNode(false); | |
new_a_label.setAttribute('href',comments[i].url); | |
var new_breadcrumb_label = breadcrumb_label.cloneNode(false); | |
new_breadcrumb_label.innerText = comments[i].nick + ' @ ' + getDate(comments[i].time); | |
var new_text_span = text_span.cloneNode(false); | |
new_text_span.innerText = getText(comments[i].comment); | |
new_a_label.appendChild(new_breadcrumb_label); | |
new_a_label.appendChild(new_text_span); | |
new_li_label.appendChild(new_a_label); | |
waline_recent.append(new_li_label); | |
} | |
}; | |
const waline_create = function() { | |
if(CONFIG.waline.serverURL) | |
{ | |
vendorCss_body('waline'); | |
var getScript = function(options) { | |
var name = 'script-waline'; | |
var old_script = document.querySelector('body script' + '.' + name); | |
if(old_script){ document.body.removeChild(old_script); } | |
var script = document.createElement('script'); | |
script.defer = true; | |
script.crossOrigin = 'anonymous'; | |
script.setAttribute('class','script-waline'); | |
Object.keys(options).forEach(function(key) { | |
script[key] = options[key]; | |
}); | |
document.body.appendChild(script); | |
}; | |
getScript({ | |
src: assetUrl('js','waline'), | |
onload: function() { | |
var options = Object.assign({}, CONFIG.waline); | |
options = Object.assign(options, LOCAL.waline||{}); | |
Waline.RecentComments({ | |
serverURL: options.serverURL, | |
count: 10, | |
}).then(({ comments }) => { recent_comment_create(comments.data); }); | |
if($('#waline-comment')) | |
{ | |
options.el = '#waline-comment'; | |
Waline.init(options); | |
setTimeout(function(){ | |
positionInit(1); | |
postFancybox('#waline-comment'); | |
}, 1000); | |
} | |
} | |
}); | |
} | |
}; | |
// 之后将 `siteRefresh` 函数中 | |
// 第 58 行到 72 行左右的 `vendorJs ('valine',function (){}` 部分删除, | |
// 并添加下面内容 | |
waline_create() |
- 删除文件:
./themes/shoka/source/css/_common/components/third-party/valine.styl - 创建新文件:
./themes/shoka/source/css/_common/components/third-party/waline.styl, 将下面内容复制到文件中并保存
:root { | |
--waline-border-color: initial; | |
--waline-border: initial; | |
--waline-font-size: initial; | |
} | |
[data-theme="dark"]:root { | |
--waline-info-bg-color: var(--grey-3); | |
} | |
[data-waline] { | |
font-size: unset; | |
text-align: left; | |
} | |
[data-waline] * { | |
box-sizing: unset; | |
line-height: unset; | |
} | |
[data-waline] p { | |
color: unset; | |
} | |
[data-waline] pre { | |
overflow: unset; | |
padding: unset; | |
line-height: unset; | |
margin: unset; | |
padding: unset; | |
border-radius: unset; | |
background: unset; | |
font-size: unset; | |
} | |
[data-waline] code { | |
padding: .1em .2em; | |
} | |
[data-waline] pre code { | |
padding: unset; | |
background: unset; | |
color: unset; | |
white-space: unset; | |
word-break: unset; | |
} | |
#comments { | |
animation: none; | |
opacity: 0; | |
} | |
#waline-comment | |
{ | |
.wl-comment .wl-footer .upload, | |
.wl-comment .wl-footer .wl-gif-popup, | |
.wl-comment .wl-footer .wl-text-number, | |
.wl-meta-head .wl-sort, | |
.wl-cards .wl-like, | |
.wl-cards .wl-addr { | |
display: none; | |
} | |
input, | |
textarea { | |
border: none; | |
outline: none; | |
background: transparent; | |
font-size: $font-size-smallest; | |
transition: all .25s ease; | |
} | |
a { | |
color: var(--color-aqua); | |
border-bottom: initial; | |
padding-bottom: initial; | |
&:hover { | |
color: var(--primary-color); | |
} | |
} | |
li { | |
list-style: none; | |
} | |
.text-center { | |
text-align: center; | |
} | |
.text-right { | |
text-align: right; | |
} | |
.float-right { | |
float: right !important; | |
} | |
.pd5 { | |
padding: 5px; | |
} | |
.pd10 { | |
padding: 10px; | |
} | |
.wl-comment { | |
shadow-box(); | |
margin-bottom: .625rem; | |
position: relative; | |
padding: .625rem; | |
.wl-panel { | |
background: initial; | |
margin: unset; | |
} | |
.wl-header { | |
display: flex; | |
padding: .3em .6em; | |
.wl-header-item { | |
flex: 1 1 27%; | |
width: 27%; | |
input { | |
padding: .625rem .3125rem; | |
width: 100%; | |
border-bottom: .0625rem dashed var(--grey-4); | |
color: var(--text-color); | |
&:focus { | |
border-bottom-color: var(--primary-color); | |
} | |
} | |
label { | |
position: absolute; | |
margin-left: -10px; | |
color: var(--grey-6); | |
} | |
.wl-nick, | |
.wl-mail { | |
padding-left: 35px; | |
} | |
.wl-link { | |
padding-left: 60px; | |
} | |
} | |
} | |
.wl-editor { | |
width: 97%; | |
height: 123px; | |
min-height: 8.75em; | |
line-height: 1.75; | |
resize: vertical; | |
margin-left: 10px; | |
} | |
.wl-footer { | |
padding: .625rem 0; | |
display: flex; | |
.wl-action { | |
color: var(--grey-6); | |
transition: all .25s ease; | |
&:first-child { | |
line-height: 1.3; | |
} | |
} | |
.wl-btn { | |
display: inline-block; | |
cursor: pointer; | |
touch-action: manipulation; | |
text-align: center; | |
text-decoration: none; | |
vertical-align: middle; | |
white-space: nowrap; | |
border-radius: 0.3rem; | |
border: 0.0625rem solid var(--grey-3); | |
color: var(--grey-6); | |
font-size: $font-size-small; | |
font-weight: 400; | |
line-height: 1.5; | |
background: 0 0; | |
margin-bottom: 0; | |
min-height: 1em; | |
padding: 0.5em 1.25em; | |
-webkit-user-select: none; | |
-moz-user-select: none; | |
-ms-user-select: none; | |
user-select: none; | |
outline: 0; | |
will-change: auto; | |
transition: all .2s ease-in-out 0s; | |
&:hover { | |
background: var(--primary-color); | |
color: var(--grey-0); | |
} | |
} | |
} | |
.wl-actions { | |
margin-bottom: -20px; | |
.wl-action { | |
font-size: $font-size-small; | |
&:first-child svg { | |
margin-bottom: -7px; | |
} | |
&:last-child { | |
float: right; | |
} | |
&:hover { | |
color: var(--primary-color); | |
} | |
} | |
} | |
} | |
.wl-cards { | |
.wl-meta { | |
margin-top: -5px; | |
margin-left: -1px; | |
>span { | |
font-size: $font-size-smallest; | |
padding: 0.5px 2px; | |
line-height: 1.15; | |
padding-top: 0; | |
} | |
} | |
.wl-comment-actions button { | |
&:hover { | |
color: var(--primary-color); | |
} | |
} | |
.wl-reply-wrapper .wl-close { | |
.wl-close-icon { | |
width: 40px; | |
height: 40px; | |
color: var(--grey-1); | |
} | |
} | |
.wl-quote { | |
border-bottom: 0.0625rem dashed var(--grey-3); | |
.wl-card { | |
border-bottom: 0.0625rem dashed var(--grey-3); | |
} | |
div:last-child .wl-card { | |
border-bottom: none; | |
} | |
} | |
} | |
.wl-meta>.wl-browser::before, | |
.wl-meta>.wl-os::before { | |
margin-bottom: -0.8px; | |
} | |
.wl-content pre, | |
.wl-content pre[class*=language-] { | |
overflow: unset; | |
margin: unset; | |
padding: unset; | |
border-radius: unset; | |
background: unset; | |
line-height: unset; | |
} | |
.wl-content pre code, | |
.wl-content pre[class*=language-] code { | |
padding: unset; | |
border-radius: unset; | |
background: unset; | |
color: unset; | |
direction: unset; | |
} | |
.wl-content pre { | |
position: relative; | |
overflow: hidden; | |
border-radius: 0.5rem; | |
box-shadow: 0 0.3125rem 0.625rem -0.125rem var(--grey-9-a1); | |
background: var(--grey-2); | |
color: var(--grey-7); | |
line-height: 1.6; | |
margin: 1.25rem auto; | |
display: block; | |
margin-block-start: 1em; | |
margin-block-end: 1em; | |
margin-inline-end: 40px; | |
white-space: pre; | |
border-spacing: 0; | |
width: 100%; | |
border-collapse: separate; | |
text-indent: initial; | |
overflow-x: scroll; | |
overflow-y: hidden; | |
code { | |
vertical-align: middle; | |
border-color: inherit; | |
} | |
span { | |
margin-left: 5px; | |
} | |
} | |
.wl-loading { | |
} | |
} | |
#waline-recent { | |
.item { | |
counter-increment: counter-post; | |
&::before{ | |
content: counter(counter-post); | |
} | |
} | |
} | |
.waline-pageview-count { | |
margin-left: 3px; | |
} |
# 3. 页面配置
标签页标题
- 文件位置:
./themes/shoka/languages
# show 浏览页为当前页,hide 浏览页为其他页面所展示的内容 | |
favicon: | |
show: (*^▽^*) | |
hide: o(╥﹏╥)o |
页内 (show): (●´3`●)やれやれだぜ
页外 (hide): (´Д`)大変だ!
顶部导航
- 配置文件:
./themes/shoka/_config.yml文件中的menu元素 - 中文映射:
./themes/shoka/languages目录中对应语言文件中的menu元素
#./themes/shoka/_config.yml | |
menu: | |
home: / || home | |
about: /about/ || user | |
posts: | |
default: / || feather | |
archives: /archives/ || list-alt | |
categories: /categories/ || th | |
tags: /tags/ || tags | |
friend-links: /friend-links/ || heart | |
books: | |
default: / || clipboard | |
deeplearning-books: /books/deeplearning-books/ || fedora | |
computer-books: /books/computer-books/ || opera | |
phone-books: /books/phone-books/ || android | |
#./themes/shoka/language/zh-CN.yml (简中) | |
menu: | |
home: 首页 | |
posts: 文章 | |
archives: 归档 | |
categories: 分类 | |
tags: 标签 | |
about: 关于 | |
search: 搜索 | |
friend-links: 友链 | |
books: 书架 | |
deeplearning-books: 人工智能 | |
computer-books: PC端技能 | |
phone-books: MD端技能 |
加载动画
- CSS 样式文件:
./themes/shoka/source/css/_common/components/third-party/loading.styl
// 保留下面这几行,其余删除后写入新样式 | |
#loading { | |
@extend $fix-fullscreen; | |
background-color: var(--grey-1); | |
if(!hexo-config('loader.start')) { | |
display: none; | |
} | |
} | |
// 填入自定义的CSS样式代码 |
- HTML 文件:
./themes/shoka/layout/_partials/layout.njk
<body itemscope itemtype="https://schema.org/WebPage"> | |
<div id="loading"> | |
</div> | |
<div id="container"> |
# 3. Hexo 常用命令
- 新建文章
hexo new "文章名称" # windows | |
$ sudo hexo new "文章名称" # linux |
- 删除文章
# 删除项目根目录下的 db.json 文件 | |
# 删除单篇文章:搜索对应文章标题 将 title... 相关文件和.html 文件一并删除 | |
# 依次执行 hexo clean, hexo g, hexo d 即可 |
-
清理缓存
-
清理缓存文件 db.json 和 已生成的静态文件 public
-
对站点的修改配置不生效时,执行以下命令
-
hexo clean # windows | |
$ sudo hexo clean # linux |
- 生成静态文件
hexo generate # windows | |
$ sudo hexo clean # linux | |
hexo g # 简写 |
- 生成索引
# 前置条件:安装 hexo-algolia 插件 | |
npm install --save hexo-algolia | |
# 将索引信息上传到 Algolia 中的对应 index | |
hexo algolia |
- 启动服务器
hexo server # windows | |
$ sudo hexo server # linux | |
hexo s # 简写 |
- 部署网站
# 前置条件:安装 hexo-deployer-git 依赖 | |
npm install hexo-deployer-git --save --registry https://registry.npmmirror.com | |
# 在_config.yml 文件中添加如下配置 | |
deploy: | |
type: git | |
repo: https://github.com/xxx/xxx.github.io.git # 替换为自己的仓库地址 | |
branch: main # 仓库分支 | |
# 生成静态文件,部署到远程仓库 | |
hexo deploy # windows | |
$ sudo hexo deploy # linux | |
hexo d # 简写 |
# 4. MarkDown 文章配置
# 1. front-matter 标签
# 文章顶部区域 hexo 内置标签属性 | |
title: 文章的标题。 | |
date: 文章的发布日期,格式通常是YYYY-MM-DD。 | |
tags: 文章的标签,可以是一个或多个。 | |
categories: 文章的分类,同样可以是一个或多个。 | |
updated: 文章的最后更新日期,格式与date相同。如果不指定,date字段的值会被用作updated。 | |
comments: 是否开启评论,通常设置为true或false。 | |
permalink: 自定义文章的永久链接。 | |
photos: 用于展示图片列表的字段,常用于带有图片集的文章。 | |
mathjax: 启用MathJax支持数学公式的渲染,设置为true。 | |
description: 文章描述,用于SEO优化。 | |
keywords: 文章关键词,用于SEO优化。 | |
sticky: 文章是否置顶, 多文章时时间逆序排序 | |
layout: post | page | draft | |
# 案例 | |
title: Hexo-Shoka主题功能介绍补充点 #文章名字 | |
date: 2021-11-28 01:04:31 #日期 | |
tags: 主题 #文章内容的标签,单个标签 | |
#tags: # 多个标签 | |
# - tag1 | |
# - tag2 | |
#cover: https://../2021.jpg #封面图片 | |
#sticky: true # 是否置顶 | |
#type: "about" # 当新建一个页面时需配置此参数 | |
#categories: [xxx,xxx] # 该文件路径 | |
#keywords: "文章内容的描述" | |
#disableNunjucks: true # 启用时禁用 Nunjucks 标签 |
- 核心内置标签
| 标签名 | 类型 | 说明 | 示例 |
|---|---|---|---|
title |
String | 文章标题。如果不填,默认使用文件名。 | title: 我的第一篇博客 |
date |
Date | 创建时间。格式: YYYY-MM-DD HH:mm:ss 。 |
date: 2026-03-20 10:00:00 |
updated |
Date | 更新时间。用于显示 “最后修改时间”。 | updated: 2026-03-21 15:30:00 |
tags |
Array | 标签。可以是单个字符串或数组。 | tags: [Hexo, 教程] 或 tags: Hexo |
categories |
Array | 分类。支持多级分类(嵌套数组)。 | categories: [技术, Hexo] |
layout |
String | 布局模板。指定使用哪个 .njk/.ejs 文件渲染。默认是 post 或 page 。 |
layout: photo (需主题支持) |
permalink |
String | 永久链接。覆盖全局配置,自定义该文章的 URL。 | permalink: my-custom-url |
comments |
Boolean | 是否开启评论。 false 可关闭该文章的评论区。 |
comments: false |
toc |
Boolean | 是否显示目录。部分主题支持,用于控制文章内目录。 | toc: true |
draft |
Boolean | 是否为草稿。 true 时执行 hexo s 可见,但 hexo g 不会生成。 |
draft: true |
photos |
Array | 相册图片。主要用于 photo 布局,展示图片墙。 |
photos: [img1.jpg, img2.jpg] |
- 常见主题扩展标签 (Theme Specific)
| 标签名 | 说明 | 示例 |
|---|---|---|
cover / banner |
文章顶部封面图路径。 | cover: /img/cover.jpg |
top / sticky |
置顶文章。数字越大越靠前。 | top: 10 |
copyright |
自定义该文章的版权协议(覆盖全局)。 | copyright: cc_by_nc_sa |
mathjax / math |
强制在该文章开启数学公式渲染。 | mathjax: true |
mermaid |
开启 Mermaid 流程图支持。 | mermaid: true |
aside |
控制侧边栏显示 ( true / false )。 |
aside: false |
highlight_theme |
单独设置该文章的代码高亮主题。 | highlight_theme: mac-light |
ai摘要 (新) |
部分新主题支持 AI 生成的摘要字段。 | ai_summary: "这是AI生成的摘要..." |
series |
文章系列名称,用于生成系列导航。 | series: Hexo 入门教程 |
series_index |
文章在系列中的序号。 | series_index: 3 |
reward |
是否开启打赏 (true/false) | |
edit_link |
自定义文章的 "编辑此页" 链接 |
- 插件扩展标签 (Plugin Specific)
| 插件名 | 新增标签 | 说明 |
|---|---|---|
| hexo-abbrlink | abbrLink |
自定义短链接哈希值。 |
| hexo-generator-searchdb | (无) | 通常自动抓取,无需配置。 |
| hexo-related-popular-posts | popularPosts |
控制相关文章显示。 |
| hexo-filter-nofollow | nofollow |
控制外部链接属性。 |
| hexo-generator-ai-summary | ai_summary |
存储 AI 生成的摘要内容。 |
# 2. 常用语法标签
以下介绍可以使用的 markdown 或者标签代码。
功能大部分基于 hexo-renderer-multi-markdown-it 渲染器,尤其是代码块的显示,与默认渲染器不兼容。
# 5. 常见问题合集
# 1. hexo-symbols-count-time 依赖
hexo 8.1.1 中一些内置对象的属性结构出现变更,导致插件无法正确得到结果,模板中渲染结果会变成 NANM || NAN
- 文章总字数统计
symbolsCountTotal(site)
// 源码部分 | |
function getSymbols(post) { | |
return post.length; | |
}; | |
function getSymbolsTotal(site) { | |
let symbolsResultCount = 0; | |
site.posts.forEach(post => { | |
symbolsResultCount += getSymbols(post); | |
}); | |
return symbolsResultCount; | |
}; | |
module.exports.symbolsCountTotal = function(site) { | |
let symbolsResultTotal = getSymbolsTotal(site); | |
return symbolsResultTotal < 1000000 | |
? Math.round(symbolsResultTotal / 1000) + 'k' // < 999k => 111k | |
: Math.round(symbolsResultTotal / 100000) / 10 + 'm'; // > 999k => 1.1m | |
}; |
- 阅读所需时长
symbolsTimeTotal(site, awl, wpm, suffix)
// 源码部分 | |
module.exports.symbolsTimeTotal = function(site, awl = config.awl, wpm = config.wpm, suffix = config.suffix) { | |
let minutes = Math.round(getSymbolsTotal(site) / (awl * wpm)); | |
return getFormatTime(minutes, suffix); | |
}; |
-
异常原因
- hexo 新版本的
site.posts对象结构发生变化
posts:{
data:[Document],
length: number,
}data: {
_content: '', // 文章内容
path: [Getter],
permalink: [Getter],
full_source: [Getter],
asset_dir: [Getter],
tags: [Getter],
categories: [Getter],
prev: [Circular *3],
next: [_Document],
__post: true
} - hexo 新版本的
-
解决方法
- 修正
\项目主文件\node_modules\hexo-symbols-count-time\lib\helper.js中对应的getSymbols以及getSymbolsTotal方法中的异常属性
// 修正后的结果 | |
function getSymbols(post) { | |
return post._content.length; | |
}; | |
function getSymbolsTotal(site) { | |
let symbolsResultCount = 0; | |
site.posts.data.forEach(post => { | |
symbolsResultCount += getSymbols(post); | |
}); | |
return symbolsResultCount; | |
}; |
# 2. code 代码块儿渲染异常
- 异常原因: hexo 新版本的默认高亮渲染器禁用方法变更,导致主题的自定义渲染器没有正确生效
- 解决方法:
# 旧版本禁用方式 | |
highlight: | |
enable: false | |
prismjs: | |
enable: false | |
# 新版本变更 | |
syntax_highlighter: false | |
prismjs: | |
enable: false |