本博客基于Hexo框架与Butterfly主题。

安装Hexo框架与Butterfly主题

安装Hexo的命令行,这里选择全局安装,因为后面需要多次使用到

1
npm install -g hexo-cli

安装好Hexo的CLI后接下来就是初始化你的站点啦,在合适的位置建立博客站点文件夹,名字随意,我用blog文件夹

1
2
3
hexo init <folder>
cd <folder>
npm install

PS:这里会使用npm安装需要的包,鉴于国内网络环境,自行安装cnpm或者yarn工具

到这基本的Hexo博客框架已经搭建好了,可以用npx hexo g && npx hexo s命令启动一个本地server来查看网站啦

接下来安装Butterfly主题

1
npm install hexo-theme-butterfly

网上有的教程使用git clone直接将主题clone到theme文件夹下,并且随后的美化、优化环节会去修改主题源码,这样后面就无法直接升级主题了,需要对修改进行合并,所以这了不使用常用的方法。有一点需要注意npm安装方法需要Hexo版本5.0.0以上,并且不会在themes目录下生成文件。

配置文件说明

由于采用了npm的安装方式,无法直接修改主题文件,所以不熟悉的人可能在这卡很久。Hexo的配置文件为_config.yml,Butterfly的配置文件为_config.butterfly.yml,这两个文件都需要放在博客根目录。Hexo的配置在官网文档可以查看,而Butterfly的详细配置就很隐蔽了,官网也并不是很容易找到,其实Butterfly作者有写默认的配置模板node_modules/hexo-theme-butterfly/_config.yml,需要什么可以直接去里面找但是不要修改

常规配置

_config.yml

此文件是Hexo的配置文件,会自动生成。标题等的按个人情况填写,不明白的查阅官网文档即可,建议以下三个字段按如下填写

1
2
3
4
language: zh-CN
timezone: Asia/Shanghai

theme: butterfly

_config.butterfly.yml

这就是Butterfly主题的配置文件了,需要自己手动创建。我只配置了如下基本字段

  • favicon: 网站图标
  • index_img: 首页大图,可以设置为false来关闭

这里的图片文件路径可以是url也可以是文件路径

markdown渲染引擎

this section updated at 2022-7-30

官方推荐的markdown-it插件是hexo-renderer-markdown-it,使用下来发现这个的toc是无法跳转的,所以用以下的第三方render

1
yarn add @upupming/hexo-renderer-markdown-it-plus

关于这个问题提交了discussion给butterfly

侧边栏配置

侧边栏的配置位于_config.butterfly.ymlaside字段,可以把需要的写入自己新建的主题配置文件完成修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
aside:
enable: true
hide: false
button: true
mobile: true # display on mobile
position: right # left or right
card_author:
enable: true
description:
button: # 作者卡片下按钮的配置
enable: true
icon: fab fa-github
text: Follow Me
link: https://github.com/xxxxxx
card_announcement: # 公告
enable: true
content: This is my Blog
card_recent_post:
enable: true
limit: 5 # if set 0 will show all
sort: date # date or updated
sort_order: # Don't modify the setting unless you know how it works
card_categories:
enable: true
limit: 8 # if set 0 will show all
expand: none # none/true/false
sort_order: # Don't modify the setting unless you know how it works
card_tags:
enable: true
limit: 40 # if set 0 will show all
color: false
sort_order: # Don't modify the setting unless you know how it works
card_archives:
enable: true
type: monthly # yearly or monthly
format: MMMM YYYY # eg: YYYY年MM月
order: -1 # Sort of order. 1, asc for ascending; -1, desc for descending
limit: 8 # if set 0 will show all
sort_order: # Don't modify the setting unless you know how it works
card_webinfo:
enable: true
post_count: true
last_push_date: true
sort_order: # Don't modify the setting unless you know how it works

社交媒体配置

侧边栏的配置位于_config.butterfly.ymlsocial字段,可以添加多个

1
2
3
social:
fas fa-envelope: mailto:xxxx@mail.com || Email
fas fa-envelope: mailto:xxxx@mail.com || Email

作者头像

侧边栏的配置位于_config.butterfly.ymlavatar字段

1
2
3
avatar:
img: https://aa.com/bb.jpeg # 图片路径
effect: false # 是否旋转

页脚设置

侧边栏的配置位于_config.butterfly.ymlfooter字段,使用shields.io生成的logo作为页脚上的标志,并关闭版权显示

1
2
3
4
5
6
7
footer:
owner:
enable: true
since: 2021
custom_text: <p><a style="margin-inline:5px"target="_blank" href="https://hexo.io/"><img no-lazy src="https://img.shields.io/badge/Frame-Hexo-blue?style=flat&logo=hexo" title="博客框架为 Hexo" alt="HEXO"></a><a style="margin-inline:5px"target="_blank" href="https://butterfly.js.org/"><img no-lazy src="https://img.shields.io/badge/Theme-Butterfly-6513df?style=flat&logo=bitdefender" title="主题采用 Butterfly" alt="Butterfly"></a><a style="margin-inline:5px"target="_blank" href="https://www.jsdelivr.com/"><img no-lazy src="https://img.shields.io/badge/CDN-jsDelivr-orange?style=flat&logo=jsDelivr" title="本站使用 Jsdelivr 为静态资源提供CDN加速" alt="Jsdelivr"></a><a style="margin-inline:5px"target="_blank" href="https://github.com/"><img no-lazy src="https://img.shields.io/badge/Source-Github-d021d6?style=flat&logo=GitHub" title="本站项目由 GitHub 托管" alt="GitHub"></a><a style="margin-inline:5px"target="_blank"href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img no-lazy src="https://img.shields.io/badge/Copyright-BY--NC--SA%204.0-d42328?style=flat&logo=Claris" alt="img" title="本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可"></a></p>
copyright: false # Copyright of theme and framework

注入js、css文件

Butterfly主题支持将js脚本文件或者css样式文件注入到网页的header或者footer部分,配置位于_config.butterfly.ymlinject字段,样例如下

1
2
3
4
5
6
7
8
inject:
head:
- <link rel="stylesheet" href="/xxx.css">
- <link rel="stylesheet" href="/xxx.css">

bottom:
- <script src="xxxx"></script>
- <script src="xxxx"></script>

以下是本博客的一个实际注入

1
2
3
inject:
head:
- <link rel="stylesheet" href="/blog/css/footer.css" media="defer" onload="this.media='all'">

注意路径使用的是绝对路径,未测试相对路径是否可用

页脚透明

source/css路径下新建footer.css样式文件,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 页脚透明 */
#footer {
background: rgba(255,255,255,.15);
color: #000;
border-top-right-radius: 20px;
border-top-left-radius: 20px;
backdrop-filter: saturate(100%) blur(5px)
}

#footer::before {
background: rgba(255,255,255,.15)
}

#footer #footer-wrap {
color: var(--font-color)
}

#footer #footer-wrap a {
color: var(--font-color)
}

在主题配置文件中注入该样式文件

卡片、文章样式与黑暗模式

配置文件中并未直接提供对黑暗模式样式的修改的,直接修改卡片、文章等的样式无法直接适配到黑暗模式。本人对css的知识比较薄弱,google并查看源码后找到了以下实现方式,依旧在css文件夹下新建一个样式文件(background.css)并进行注入。实现方式就是设置元素的背景,使用rgba格式颜色最后一个参数是透明度,样式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
:root {
--light_bg_color: rgba(255, 255, 255, 0.6);
--dark_bg_color: rgba(18, 18, 18, 0.8);
--light_page_color: rgba(255, 255, 255, 0.5);
--dark_page_color: rgba(18, 18, 18, 0.8);
}

/* 文章 */
[data-theme="dark"] .post>.layout>#post,#recent-posts>.recent-post-item,.read-mode .layout>#post {
background: var(--dark_bg_color);
}

/* aside card */
[data-theme="dark"] #aside-content .card-widget {
background: var(--dark_bg_color);
}

/* aside card */
[data-theme="light"] #aside-content .card-widget {
background: var(--light_bg_color);
}


[data-theme="light"] #recent-posts>.recent-post-item,.post>.layout>#post,.read-mode .layout>#post {
background: var(--light_bg_color);
}

在调试时发现Hexo是通过改变data-theme属性的值来切换黑暗模式的,并且类似[data-theme="light"]的写法就可以在不同值时对应不同的样式。这里有个坑就是[data-theme="light"]需要和css选择器写在同一行,否则会使得有的样式不生效。魔改过程中这个博客给我了很大帮助,表示感谢

全局背景

网页背景修改首先需要在主题配置文件中添加background: "#efefef",只有添加后渲染出来的网页才有#web_bg这个元素,网页背景也是对这个元素样式进行修改。修改过程参考了这篇博客很多,表示感谢~~。此部分样式内容放在background.css

1
2
3
4
#web_bg {
background-color: #0093E9;
background-image: linear-gradient(160deg, #0093E9 0%, #80D0C7 100%);
}

我选择蓝色渐变作为背景,这个网站可以生成渐变背景的css样式代码,当然你也可以选择图片作为背景

algolia搜索配置

实现站点搜索有两种形式,一个是local_search一个是在线的搜索如algolia。本站点使用algolia提供的搜索。
algolia有免费套餐可以使用,额度是一个月10k请求,一般情况下也够了。注册过程就不细写了,可以参考其他大佬的教程,完成注册并创建一个index后即可在API Keys里得到三个key,分别是Application ID、Search-Only API Key以及Admin API Key。安装hexo的插件,这里推荐使用这个插件而不是hexo-algolia插件

1
npm install hexo-algoliasearch

插件配置可以从github仓库查找到,fields部分配置可以在文档中查找。以下贴的配置需要放置于 _config.yml文件内,起到插件配置的作用。其中的fields部分是本博客实际使用的字段,不是很全但是基本涵盖了常用的查找范围

1
2
3
4
5
6
7
8
9
10
algolia:
appId: "Z7A3XW4R2I" # Application ID
apiKey: "12db1ad54372045549ef465881c17e743" # Search-Only API Key
adminApiKey: "40321c7c207e7f73b63a19aa24c4761b" # Admin API Key
chunkSize: 5000
indexName: "blog" # index
fields:
- path
- title
- content:strip

插件配置完毕后还需要在Butterfly主题配置文件中使能搜索插件,否则博客页面上搜索按钮都不会出现,这点很重要!以下是_config.butterfly.yml文件中的配置

1
2
3
4
algolia_search:
enable: true
hits:
per_page: 6

当配置完毕后,生成站点后使用hexo algolia即可生成索引。

图片的懒加载

hexo已经有插件实现了图片的lazy load,所以仅需要简单的安装配置插件即可使用

1
npm install hexo-lazyload-image

该插件文档见此

安装插件后需要在Hexo配置文件中添加如下字段

1
2
3
4
lazyload:
enable: true
onlypost: false # 是否只对文章的图片做懒加载
loadingImg: # eg ./images/loading.gif

其中的loadingImg是加载过程中的图,可自定义,默认图还是挺丑的

而对于某些你不想懒加载的图片,比如图标一类的,只需要在其元素属性上加入no-lazy字段即可

jsDelivr加速(CDN)

选择将文件放置在本人的GitHub仓库上,通过jsDelivr免费的CDN进行加速,使用该CDN加速GitHub内的文件无需注册,比较方便

1
https://cdn.jsdelivr.net/gh/<github name>/<repo name>/.../<file name>

只需要将上述路径替换成自己的仓库路径即可

gulp压缩

对于生成的css、js、html、图片等文件,可以使用gulp自动化工具进行压缩以加快网站加载速度,本文参考Butterfly官方教程,安装gulp本体以及插件

1
2
npm install -g gulp-cli
npm install --save-dev gulp-htmlclean gulp-html-minifier-terser gulp-clean-css gulp-terser gulp-uglify gulp-babel @babel/core @babel/preset-env

在网站根目录下创建gulpfile.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
const gulp = require('gulp')
const cleanCSS = require('gulp-clean-css')
const htmlmin = require('gulp-html-minifier-terser')
const htmlclean = require('gulp-htmlclean')
const imagemin = require('gulp-imagemin')
// gulp-tester (如果使用 gulp-tester,把下面的//去掉)
// const terser = require('gulp-terser');

// babel (如果不是使用bebel,把下面兩行註釋掉)
const uglify = require('gulp-uglify')
const babel = require('gulp-babel')

// minify js - babel( 如果不是使用bebel,把下面註釋掉)
gulp.task('compress', () =>
gulp.src(['./public/**/*.js', '!./public/**/*.min.js'])
.pipe(babel({
presets: ['@babel/preset-env']
}))
.pipe(uglify().on('error', function (e) {
console.log(e)
}))
.pipe(gulp.dest('./public'))
)

// minify js - gulp-tester (如果使用 gulp-tester,把下面前面的//去掉)
// gulp.task('compress', () =>
// gulp.src(['./public/**/*.js', '!./public/**/*.min.js'])
// .pipe(terser())
// .pipe(gulp.dest('./public'))
// )


// css
gulp.task('minify-css', () => {
return gulp.src('./public/**/*.css')
.pipe(cleanCSS())
.pipe(gulp.dest('./public'))
})

// 壓縮 public 目錄內 html
gulp.task('minify-html', () => {
return gulp.src('./public/**/*.html')
.pipe(htmlclean())
.pipe(htmlmin({
removeComments: true, // 清除 HTML 註釋
collapseWhitespace: true, // 壓縮 HTML
collapseBooleanAttributes: true, // 省略布爾屬性的值 <input checked="true"/> ==> <input />
removeEmptyAttributes: true, // 刪除所有空格作屬性值 <input id="" /> ==> <input />
removeScriptTypeAttributes: true, // 刪除 <script> 的 type="text/javascript"
removeStyleLinkTypeAttributes: true, // 刪除 <style> 和 <link> 的 type="text/css"
minifyJS: true, // 壓縮頁面 JS
minifyCSS: true, // 壓縮頁面 CSS
minifyURLs: true
}))
.pipe(gulp.dest('./public'))
})

// 執行 gulp 命令時執行的任務
gulp.task('default', gulp.parallel(
'compress', 'minify-css', 'minify-html'

我这里删除了最小化图片的部分,因为图片都使用了CDN加速

使用gulp即可运行该task

github action生成与部署

部署于Github Pages

因为不想在笔记本上生成网站再传输到自己的服务器或者GitHub上,所以使用GitHub Action进行网站的生成。首先你需要有个GitHub账号~~

新建一个仓库,分支Hexo上存储博客源文件,分支public用来存放生成的站点文件,同时直接开启GitHub Pages来部署博客。

上述过程中,安装的hexo-algoliasearchgulp-cli插件需要本地安装,并且在package.json中的scripts下添加"algolia": "hexo algolia""gulp": "gulp"两项

1
2
3
4
5
6
7
8
"scripts": {
"build": "hexo generate",
"clean": "hexo clean",
"deploy": "hexo deploy",
"server": "hexo server",
"algolia": "hexo algolia",
"gulp": "gulp"
}

如此添加后,就可以直接使用npm run algolianpm run gulp来执行命令,避免包的全局安装,最大化利用缓存

GitHub Action参考Hexo官方的部署示例进行少部分修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
name: deploy_hexo

on:
push:
branches:
- hexo # default branch

jobs:
deploy_hexo:
runs-on: ubuntu-latest
env:
ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
ALGOLIA_ADMIN_API_KEY: ${{ secrets.ALGOLIA_ADMIN_API_KEY }}
ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME }}
steps:
- uses: actions/checkout@v2
- name: Use Node.js 16
uses: actions/setup-node@v2
with:
node-version: "16"
- name: Cache NPM dependencies
uses: actions/cache@v2
with:
path: node_modules
key: ${{ runner.OS }}-hexo-npm-cache
restore-keys: |
${{ runner.OS }}-hexo-npm-cache
- name: Install Dependencies
run: npm install
- name: Build
run: npm run clean && npm run build && npm run algolia && npm run gulp
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
publish_branch: public # deploying branch
user_name: 'github-actions[bot]'
user_email: 'github-actions[bot]@clemon.noreply.github.com'

其中需要注意的是,由于启用GitHub Page后仓库不能是私有的,所以hexo-algoliasearch所需要的三个key不适合直接放在配置文件中,而该插件支持从环境变量中直接获取key,所以将配置文件中的appId、apiKey、adminApiKey字段删除,在Action的配置中声明三个环境变量ALGOLIA_APP_IDALGOLIA_API_KEYALGOLIA_ADMIN_API_KEY,本文将indexName也使用环境变量进行了注入。而这些key储存在仓库的secrets里,没有公开访问权限。

私有部署

本人强迫症并且考虑到Github服务器访问问题,所以选择私有部署并且设置Github仓库为私有

基于Caddy的WebHook插件

铁头娃用新不用旧,直接上Caddy2,基本流程参考了这篇博客,使用的WebHook插件也是来自这位大佬,表示感谢🙏

贴一下我的博客部分Caddyfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
your.domain.name {
tls xxxxxx@gmail.com
encode zstd gzip

root blog
file_server

log {
output file /caddy/logs/blog.log
}

route /webhook {
webhook {
repo https://github.com/gh_username/blog_repo.git
path blog
branch public
secret xxxxxxx
}
}
}

简单说明下:

  • rootpath需要对应,这里使用相对路径Caddy会自动使用\var作为根目录
  • 在Github仓库的Setting->Webhooks添加一个Webhook,URL就是https://your.domain.name/webhookContent type选择application/jsonsecret自己设置一个并填到Caddyfile内的secret位置,触发事件选择仅push就可以了
  • repo需要带有.git后缀

这里使用的Caddy需要编译带插件的,可以手动在官网下也可以自己编译,我贴一下我的Dockerfile

1
2
3
4
5
6
7
8
9
FROM caddy:builder-alpine AS builder

RUN go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct \
&& xcaddy build --with github.com/WingLim/caddy-webhook


FROM caddy:alpine

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

部署使用和docker-compose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3'
services:
caddy:
build:
context: .
dockerfile: Dockerfile.caddy
restart: always
networks:
- proxy-bridge
ports:
- 80:80
- 443:443
- 6800:6800
- 2015:2015
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
- ./caddy/data:/data/caddy
- ./log/caddy:/caddy/logs

networks:
proxy-bridge:

但是使用过程中发现存在问题,插件可以收到仓库更新的事件并去更新本地文件,但是会一直卡在updating。。。

基于rsync与Caddy

Caddy的WebHook不好用也可以用其他同步方式不是嘛,rsync就是个不错的选择。简而言之就是Github Action生成好public的文件后,直接用rsync将文件部署到自己的目标服务器即可。
首先在目标服务器创建rsync的用户并设置密码,防止安全问题

1
2
sudo useradd -m rsync
sudo passwd rsync

创建一对公钥、私钥

1
ssh-keygen

将公钥上传到目标服务器

1
ssh-copy-id -i ./id_rsa_rsync.pub rsync@domain.name

在目标服务器上创建文件夹作为目标部署文件夹

1
2
mkdir /var/rsync
sudo chmod -R rsync.rsync /var/rsync

将私钥保存到Github仓库的Secrets中,名字为HEXO_DEPLOY_KEY,Github Action文件添加一个Step

1
2
3
4
5
6
7
8
9
- name: Deploy By Rsync
uses: burnett01/rsync-deployments@5.1
with:
switches: -avzr --delete
path: public/
remote_path: /var/rsync/blog
remote_host: domain.name
remote_user: rsync
remote_key: ${{ secrets.HEXO_DEPLOY_KEY }}

caddyfile只是很简单的启动一个fileserver,docker-compose里只是多只读挂载/var/rsync/blog目录进去就好了

  • rsync用户不能禁止登陆,否则同步失败
  • 采用这种方式,public分支可以删除
  • 目标服务器建议禁止密码登陆

字体压缩

参考了font-spider,但是有很多问题,待解决后更新

访客统计

截止这篇教程,Butterfly已经继承了不蒜子的访问计数功能,默认开启无需配置。

评论系统

Butterfly主题支持Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo评论系统。

这里选用Valine,这个不是类似GitHub issues的系统,并且LeanCloud在国内也有服务,所以访问速度什么的都还可以。

配置过程参考了这篇文章,在此表示感谢。

注册LeanCloud

LeanCloud中国官网去注册就可以了,需要使用支付宝进行实名认证。注册好了以后找到类似界面创建一个应用

image-20210823143610800

应用选择开发版,虽然有限制但是一个小小的博客肯定是够了。创建好了就会跳转到如下界面,点击齿轮图标进入设置

image-20210823143832068

然后需要绑定域名,这个域名需要是备案完成的域名,绑定过程会检查备案。海外版似乎不需要备案。

image-20210823144208161

SSL就选择自动好了,省点事。这里的域名不是博客的域名!!看文档应该是调用LeanCloud Api时用的CNAME域名。这里不支持绑定裸域名,建议设置成类似blog-comment-api.domain.cn的域名。然后需要去域名服务商的云解析添加一条CNAME记录,按着操作就好了,静待绑定完毕,其中部署证书可能需要较长时间。

  • 绑定完毕之后不要删除或暂停这条CNAME记录,否则LeanCloud Api会无法调用(评论功能失效)
  • 上面推荐的文章里说这里的域名是博客域名,似乎是不对的。。。

好了之后就会有如下提示

image-20210823144545525

然后在应用凭证里找到AppIDAppKey,后面配置要用

image-20210823144756784

以后博客的评论信息会储存在LeanCloud数据库里

image-20210823150809147

想要删除评论的话来这里就好了(控评doge)

配置

由于Butterfly已经支持了,所以配置很简单,直接贴_config.butterfly.yml相关部分。

开启评论系统

1
2
3
4
# 评论系统
comments:
use:
- Valine

comments字段可支持两个评论系统,但是没想到什么场景需要使用,所以这里就只写一个Valine

配置valine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# valine
valine:
appId: <LeanCloud AppId>
appKey: <LeanCLoud AppKey>
pageSize: 10 # comment list page size
avatar: wavatar # gravatar style https://valine.js.org/#/avatar
lang: zh-CN # i18n: zh-CN/zh-TW/en/ja
placeholder: 你有什么想说呢~ # valine comment input placeholder (like: Please leave your footprints)
guest_info: nick,mail # valine comment header info (nick/mail/link)
requiredFields: mail # required fields (nick/mail)
recordIP: false # Record reviewer IP
serverURLs: https://blog-comment-api.domain.cn
# bg: # valine background
# emojiCDN: # emoji CDN
  • valine下的字段大多数都可以在valine配置项里查到
  • appIdappKey换成你自己的LeanCloud里的id和key
  • guest_info就是评论者可填写的信息字段(可选nick/mail/link),对应requiredFields就是评论者必须填写的字段
  • serverURLs在LeanCloud绑定的Api域名,valine访问LeanCloud Api需要使用
  • 想关闭某些文章的评论功能,在文章头部添加comments: false就行了

字数统计

安装插件

1
yarn add hexo-wordcount

修改主题配置文件_config.butterfly.yml

1
2
3
4
5
wordcount:
enable: true
post_wordcount: true
min2read: true
total_wordcount: true