博客搭建日志
🔝重要内容置顶
注意每次备份前,都需要commit一下~(回忆前面讲的内容,如果不commit就只是在本地有缓存,没有上传到缓存区)
➜ dlog git:(master) ✗ git add themes/butterfly41 ➜ dlog git:(master) ✗ git add . ➜ dlog git:(master) ✗ git commit -m '211023版本提交' ➜ dlog git:(master) ✗ git push backup_github master
后面的
hexo
是远程仓库名,master
是hexo
这个远程仓库的默认分支名,这些名字都可以自己个性化命名,它们都不是代码中的关键字!将备份下载到本地的代码:(要预先给电脑配置好SSH哦~)
➜ dlog git:(master) ✗git clone https://gitee.com/dhndzwxj/hexo-backup.git hexo-backup
如何将仓库拷贝到本地?(以Windows版本为例)
cd Desktop git clone git@github.com:dhndzwxj/hexo-backup.git juncker
当然,进行这一切的前提是提前在使用的电脑上设置好SSH key,方法详见本文章3.1节。
下载主题:
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
【完美解决】Hexo博客出现“Cannot GET/xxx”错误
npm audit fix
1. 搭建环境
首先搭建nodejs
和git
环境。这两个环境相当于博客搭建的基础设施,没有这两个东西的支持,就没法对自己的博客实现结构化管理。虽然这一步看起来非常枯燥无聊,却是实现后续所有功能的关键步骤!经过实验,下载这两个环境的时候,一定要关闭电脑的安全中心当中的“病毒和威胁防护”,否则直接被电脑当病毒杀灭了。
1.1 Windows
第一步,点击nodejs进行下载nodejs。
![]() |
![]() |
按照提示一步一步进行安装操作即可(时间比较长,请保持耐心~)。下一步是Windows平台配置环境。
高级系统设置——环境变量(配置环境变量的目的是,以后git
和npm
这两个命令可以在任何目录下使用,变成了全局的指令。不配置的话会出错!)
新增系统变量:
变量名填:NODE_PATH
变量值填:(文件路径)

还需要在Path变量名中修改nodejs文件默认路径

第二步,git环境搭建
到Git官网下载,下载地址,一步一步进行安装。如下图(仅展示需要提示的部分,基本上选择默认选项即可):
![]() |
![]() |
![]() |
1.2 Mac
苹果电脑上的安装相对比较简单。
下载nodejs:node-v16.11.1.pkg
下载git:git-2.33.0-intel.dmg
检查安装的版本信息:
# 首先检查是否安装了git和node.js,终端输入一下命令,
node -v #是否出现安装版本信息,出现说明已经安装了
git --version #同上述情况
# 如果没有安装,则进行安装,都可以通过直接下载安装测序进行安装,这里不演示了。
如果已经安装好了上述的软件,那么可以安装hexo,然后等待安装成功即可。
1.3 Nas
略,可参考基于黑群晖 NAS 搭建 Hexo 博客系统。
2. 搭建博客站点
因为npm
的服务器在海外,所以有时候用这个指令安装东西的话速度比较慢。好在国内有淘宝镜像cnpm
,功能和原装的npm
一模一样,只需在命令行中输入:
npm config set registry https://registry.npm.taobao.org
npm install -g cnpm --registry=https://registry.npm.taobao.org #(Windows)
sudo npm install -g cnpm --registry=https://registry.npm.taobao.org #(MacBook)
以后直接用cnpm替换npm即可!如有可选更新,cnpm也可以进行升级:
npm -g i cnpm #更新cnpm
npm install -g npm@[版本号] #更新npm
sudo npm install -g npm@[版本号] #更新npm(MacBook)
2.1 Windows
第一步是安装Hexo环境:
npm install -g hexo-cli
第二步是新建一个存放博客的专属文件夹,然后在这个文件夹下进行操作:
# 在你的家目录下创建一个blog文件夹
mkdir dlog
# 进入目录
cd dlog
# 初始化目录
hexo init
#开启本地服务
hexo s
出现以下信息,说明你可以本地访问博客系统,在浏览器输入http://localhost:4000
这个网址,就可以看到博客首页。

2.2 Mac
Mac上的步骤不能说一模一样的,反正也是八九不离十了。
sudo npm install -g hexo-cli
# 在你的家目录下创建一个blog文件夹
mkdir blog
# 进入目录
cd blog
# 初始化目录
hexo init
#开启本地服务
hexo s
2.3 部署hexo博客
以下步骤对Windows和Mac而言,没有任何区别。
首先需要安装hexo-deployer-git
插件,这样我们才能把生成的public
文件夹部署到Github或者其他代码托管平台。
npm install hexo-deployer-git --save
下面你需要打开根目录,然后在这个目录下找到站点配置文件_config.yml
(如下图左)。并且打开这个文件,进行编辑。
![]() |
![]() |
在站点配置文件内(如上图右)找到deploy
这一行代码,按照以下的格式修改即可。(下面是本人的个性化设置,各个参数是什么意思,容我细细道来)
deploy:
type: git
repo: git@github.com:dhndzwxj/dhndzwxj.github.io.git
branch: master
- deploy: 部署的意思,一个关键词
- type:部署的类型,这里选择的是git,就是上面我们已经安装的那个!
- repo:仓库的意思,我们所有的本地文件都要上传到这里!我们要在github上面申请自己的账号,建造自己的仓库~(具体怎么做见后文!)
- branch:仓库的分支的意思,一个仓库里面可以有多个分支,就像是一个大院里面有很多间厢房一样。具体的含义后面解释。
2.4 Nas
略,可参考基于黑群晖 NAS 搭建 Hexo 博客系统。
3. 博客代码部署
3.1 Github
对我们而言,Github可能只是一个存数据的仓库而已。但对于大多数编程爱好者而言,这是一个巨大的资源宝库,是全球编程爱好者交流思想的平台。它的官方网址是:Github。
第一步要在Github上申请一个账号。点击网站右上角的Sign up
,然后在下一个页面用自己的邮箱进行注册,输入一个比较复杂的密码,输入自己的个性化用户名,然后选择是否接受更新(输入y表示同意,n表示不同意),然后进行人机验证,最后在邮箱接收验证码。
![]() |
![]() |
后面就是一些个性化的设置,不关键,此处略去。如果想跳过这些烦人的步骤,直接把网页拉倒最底下,点一下skip personalization即可。
接下来进入github个人首页,如下图所示。

下面我们就要建立“仓库”用以储存我们的博客网站了!点击如上图中左边的“create repository”(如果你已经有仓库了,那么酒店左上角的“New”,或者点右上角“+”,里面有个“New repository”),进入下面的页面。这个页面里面一些设置要特别注意了!仓库的名字(repository name)一定要和自己的名字(owner)一模一样!而且一定要写成yourname.github.io
的格式。不然后面没办法生成相应的github页面。

这样仓库就建好了。每一个仓库都有自己的地址,如下图:

一般而言,最重要的地址是SSH地址,将其复制下来,粘贴到博客站点配置文件blog/_config.yml
就可以了。这里解释了上面deploy
中repo
的参数的含义!
deploy:
type: git
repo: git@github.com:dhndzwxj/dhndzwxj.github.io.git
branch: master
下一步要做的呢,就是在本地的文件和github仓库之间建立联系,其实两行代码也就解决了:
git config --global user.name "dhndzwxj"
git config --global user.email dhndzwxj@ruc.edu.cn
注意啊,上面的user.name和user.email一定替换成自己的githubID和注册的邮箱~
接下来还有个问题!我们目的是把本地的文件上传到云端的github,但是每次访问github,它都会要求我们输入密码,否则就没有权限(也是出于安全的目的)。怎么省掉这个麻烦呢?方法就是创建SSH(别问我这是啥,照着下面的说法做就是了)!
(1)检查是否有SSH key
登陆github
,点击头像位置处 Settings ——> SSH and GPG keys
,查看是否有SSH keys
。如果有,直接跳到第(3)步;如果没有,则继续。

(2)新建 SSH key,在CMD中输入如下代码,注意大小写,后面的邮箱替换成自己注册github的邮箱:
ssh-keygen -t rsa -C "dhndzwxj@ruc.edu.cn"
ssh-keygen -t ed25519 -C "dhndzwxj@ruc.edu.cn"
然后会出现:
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/电脑名/.ssh/id_rsa):
直接回车就可以。然后会出现:
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
要求你输入密码,这个密码会在你提交项目时使用,如果为空的话提交项目时则不用输入。这个设置是防止别人往你的项目里提交内容,但就我个人的使用体验而言,还是不设置的好,否则的话每次上传都需要输入密码,很麻烦。
注意:输入密码的时候没有*字样的,直接输入就好。然后会出现:
BASH
Your identification has been saved in /c/Users/电脑名/.ssh/id_rsa.
Your public key has been saved in /c/Users/电脑名/.ssh/id_rsa.pub.
The key fingerprint is:
65:69:······02:4b emailname@email.com
The key's randomart image is:
+--[ RSA 2048]----+
| |
| . o . |
| . o o = o |
| . o * = o |
| E o + o . |
| . o. . . |
| .. |
+-----------------+
至此,密钥已经成功生成。
(3)接下来在github上添加SSH key:
- 打开本地文件:id_rsa.pub(文件路径可以在上一步SSH生成成功后看到路径,比如我的是
/Users/电脑名/.ssh/id_rsa.pub
),可以将这个文件在编辑器中打开,然后全选复制。- 这一步需要注意,打开后直接复制可能会损坏原来的字段格式,推荐使用命令行进行复制。mac上可以使用这个代码:
cat ~/.ssh/id_rsa.pub
。windows电脑就使用这个代码:clip < C:/Users/Administrator/.ssh/id_rsa.pub
。 - 登陆github,点击头像位置处 Settings ——> SSH and GPG keys ——> New SSH key,点击新建SSH key。
- 这一步需要注意,打开后直接复制可能会损坏原来的字段格式,推荐使用命令行进行复制。mac上可以使用这个代码:
- 将 ① 中复制的内容粘贴在key文本框里,title可以不用填(或者自己起一个名字也可以)。
(4)测试设置是否成功:
ssh -T git@github.com
如果出现以下内容:Hi dhndzwxj! You've successfully authenticated, but GitHub does not provide shell access.
。或者kex_exchange_identification: Connection closed by remote host
。可以尝试访问该网站:https://docs.github.com/cn/authentication/troubleshooting-ssh/error-permission-denied-publickey
npm un hexo-deployer-git
npm i hexojs/hexo-deployer-git
ssh -T GITHUB-dhndzwxj@github.com
(5)然后就可以部署你的博客到github啦。这之后,输入网址yourname.github.io
,就能访问自己的网站了。
hexo cl && hexo g && hexo d
如果这一步报错,可能是因为没有安装hexo-deployer-git
插件,安装即可:
cnpm install hexo-deployer-git --save
代码解读:第(6)步实现了三个步骤:
-
hexo cl
是hexo clean
的简写。意思是清除本地的缓存,实际上就是把博客文件夹下的public
文件夹删除掉了。这个public
是基于本地的文件生成的、用于上传到仓库或者其他网站服务器上的文件夹,可以理解为本地文件上网的中转站、交通工具,删掉了也不影响本地的内容。 -
hexo g
是hexo generate
的简写,意思是生成public文件夹。 -
hexo d
是hexo deploy
的简写,意思是将生成的public文件夹部署到网上,我们这里是部署到github上面。为了顺利部署,我们前面也提到过,要在站点文件夹下_config.yml
文件中修改一些内容,如下:
deploy:
type: git
repo: git@github.com:dhndzwxj/dhndzwxj.github.io.git
branch: master
上面的repo
后面要换成自己仓库的SSH,SSH在如下的位置:

3.1a 部署意外情况
不过hexo d
还可能遇到其他一些情况:Please make sure you have the correct access rights and the repository exists
。解决方法是:
到这里问题就很明确了,是DNS解析出问题了,导致github.com域名被解析成了localhost的ip地址,就自然连不上GitHub了。
Windows下执行ipconfig /flushdns
清楚DNS缓存后也没用,最后修改/etc/hosts
文件,增加一条github.com的域名映射搞定。
140.82.113.4 github.com
设置http proxy
git config --global http.proxy socks5://127.0.0.1:7890
git config --local http.postBuffer 524288000
#如果是缓存不够,设置500M缓存
git config --global sendpack.sideband false
如果要取消代理:
git config --global --unset http.proxy
git config --global --unset httpx.proxy
client_loop: send disconnect: Broken pipeiB
I solved the same problem by editing the file ~/.ssh/config
to have:
Host *
User dhndzwxj@ruc.edu.cn
Hostname ssh.github.com
IdentityFile ~/.ssh/id_rsa
ServerAliveInterval 20
TCPKeepAlive no
Port 22
如果还有ip地址问题,就删掉/.ssh/known_hosts
。
github 的ip地址为。
ping github.com
PING github.com (140.82.113.4): 56 data bytes
!!!!!!!!!!!!!!!!!!!!
删除.ssh文件夹下的known_hosts
git 设置和取消代理
# 设置ss
git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'
# 设置代理
git config --global https.proxy http://127.0.0.1:1080
git config --global https.proxy https://127.0.0.1:1080
# 取消代理
git config --global --unset http.proxy
git config --global --unset https.proxy
还可能遇见这种问题:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
(commit or discard the untracked or modified content in submodules)
删掉博客目录下的.git
文件夹(一般为隐藏状态)即可。不过值得注意的是,这个文件夹被删掉之后,所有的远程“代号需要重新设置”:
git remote add [代号] [仓库名]
3.2 Gitee
别名“码云”,服务器在国内。优点是下载速度比Github快了一个数量级,缺点是一旦博客中有某些“敏感词”就无法上线自己的博客。它的官方网址是:Gitee。后续的过程基本上重复了上一步在Github中所作的一切,因此不再赘述。
不过gitee的站点发布和github不太一样。需要在最后一步开启网站服务。不过我的博客经常被gitee检查出敏感词汇,因此一直没有上线成功,就不展示了哈~

找到.git下面的config文件,通过vi命令进行修改,笔者起初文件内容如下:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:secbr/shiro.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
修改之后变为:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
// github的仓库地址
url = git@github.com:secbr/shiro.git
// gitee的仓库地址
url = git@gitee.com:secbro/shiro.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
4. 撰写博客
4.1 博客文件夹
我们打开自己的博客根目录,跟着我一个个了解里面的这些文件(夹)都是干什么的:

_config.yml
:俗称站点配置文件,很多与博客网站的格式、内容相关的设置都需要在里面改。node_modules
:存储Hexo插件的文件,可以实现各种扩展功能。一般不需要管。package.json
:别问我,我也不知道干嘛的。scaffolds
:模板文件夹,里面的post.md
文件可以设置每一篇博客的模板。具体用起来就知道能干嘛了。source
:非常重要。所有的个人文件都在里面!themes
:主题文件夹,可以从Hexo主题官网或者网上大神的Github主页下载各种各样美观的主题,让自己的网站变得逼格高端的关键!
接下来重点介绍source
文件夹。新建的博客中,source
文件夹下默认只有一个子文件夹——_posts
。我们写的博客都放在这个子文件夹里面。我们还可以在source
里面新建各种子文件夹满足自己的个性化需求,对初学者而言,我们先把精力放在主线任务上,然后再来搞这些细节。
4.2 新建博客文件
博客文件格式为.md
(Markdown格式文件,建议使用typora软件进行编辑)。我们一定要用代码建立新的博客 :
hexo n 文件名

如上图,左边的是scafford
文件夹下的模板文件,右边是我们刚刚新建的markdown文件。我们可以看出模板文件是干嘛用的了吧!之所以要用代码新建博客文件,是因为这种方式生成的文件有前面的头文件(Front Matter),这些内容对于后面生成网页很有用!默认的模板文件中并没有这么丰富的内容,这些新添加的东西都是我写博客一年来积累的符合个人习惯的设置,不必邯郸学步。
写好内容后,在命令行一键三连:
hexo cl && hexo g && hexo s
然后随便打开一个浏览器,在网址栏输入localhost:4000/
,就能发现自己的网站更新了!不过这只是在本地进行了更新,要想部署到网上(Github上),输入如下代码:
hexo d
然后在浏览器地址栏输入https://yourname.github.io
,或者yourname.github.io
就能在网上浏览自己的博客了!
以上,我们的博客网站1.0版本就搭建完成了,如果没有更多的需求,做到这里基本上就可以了。如果有更多的要求,还需要进一步的精耕细作!
4.3 Typora-Markdown语法
如果想要系统学习相关的编码知识,需要研究以下两套代码:
Markdown语法:菜鸟教程
Markdown是支持html和html5的语法的:菜鸟教程
不过出于快速上手的目的,下面我分享一些设置和模板,方便新学者能快速投入使用。
首先要对Typora软件进行一些设置。按Ctrl+,
打开其设置界面。
(1)由于个人喜欢使用LaTeX语法,因此要在Markdown
标签中打开内联公式:
(2)此外如果在Typora中插入图片,我的做法是用QQ截图(或者其他截图工具)然后直接粘贴到Typora里。这样做一般都会成功,生成的图片格式如下:

-
插入图片代码的基本格式为

。其中“图片描述”部分可以省略不写。图片路径是我自己设置过的:打开Typora的设置,在“图像”标签下,自定义的图片插入路径:(如果不设置,我都不知道这插入的图片存到我电脑的什么奇怪位置去了!) -
我的图片和文章的存储结构为:
-
source
- _posts
- images
-
下一张图的
../images/${filename}
是在macOS系统下设置的。不过在windows系统下这么设置就出现了问题,要改成/../../images/${filename}
。【在相对路径的语法中,.
代表文件所在的目录,..
代表上一级目录,${filename}
代表文件名。】 -
/../../source/images/${filename}
的含义是,在_post
的同目录(source
)下,创建一个images
子文件夹(如果文件夹已经存在就不另建了),然后在images
下,以当前工作的markdown文件名新建子子文件夹,于是这篇博文中的所有图片都保存在这个新的子子文件夹下了。 -
当然,我不推荐用上面的方法插入图片,因为没办法设置图片的大小、位置等信息。我一般用html标签语言插入图片,代码如下:
<div align='center'>
<img src='图片路径' width=450px>
<p align='center' style='font-size:15px;font-family:kaiti;color:red'>图片标题 </p>
</div>
-
<div></div>
是html中表示代码块的标签,相似的还有<span></span>
。只不过前者用于行间,后者用于行内。它包裹住所有的内容都会独立出现为一个大模块,里面的align='center'
是设置整个模块的位置。 -
<img>
是设置图片插入的标签(这个标签没有</img>
配合),src
写的是图片路径参数,width
是图片宽度参数,px
是宽度单位。其实还有高度的参数height
,只不过我一般宽高只设置一个。还可以加入参数float='left'
,效果就像是word里面的“四周环绕型”,不推荐。 -
<p></p>
是图片周边的文字内容。align
前面已经介绍,style
下可以设置文字的字号(font-size
)、字体(font-family
)、颜色(color
)等,彼此之间用英文的分号间隔。(注意:这些属性都是可选的,也可以一个style都不写,那样就随系统默认格式。)
(3)行间文字的设置,和上面介绍的图片周边的文字基本一样,只不过这里一般使用<span></span>
标签。格式如下:
<span style='color:blue;background:yellow;font-size:18px;font-family:hei'>测试字体</span>
(4)如果要插入代码块,把自己的代码存在文章中,在英文输入法下按三下键盘上的这个键(下图),然后回车,代码环境就出现了:

(5)插入表格:段落——表格——插入表格
。
(6)无序列表
就是输入减号-
然后按下空格,按下回车。如下:
- 一级列表
- 二级列表
生成二级列表的方法是按键盘的Tab
键,返回上一级列表的方法是按键盘的Shift+Tab
键。
(7)有序列表
就是输入1.然后按下空格,按下回车,如下:
(8)文中尾注
马克思[^2]是全世界无产阶级和劳动人民的伟大导师。
[^2]: 卡尔·马克思,全名卡尔·海因里希·马克思(德语:Karl Heinrich Marx,1818年5月5日-1883年3月14日),马克思主义的创始人之一。……
注意:在typora中写文章,能用html代码就别用markdown语法,否则生成网页的时候非常容易出现问题。比如对于加粗,建议使用标签
<b></b>
。而不是markdown的**内容**
。
5. 插件
使用插件可以让我们的博客功能更为强大,也能满足自己的一些特殊需要。下面列举一些事例,如有其他的要求,可以自行上网搜索、学习。
5.1 永久链接
在做优化之前,hexo-next文章链接默认的生成规则是::year/:month/:day/:title
,是按照年、月、日、标题来生成的。这样,如果文章标题是中文的话,URL链接是也会是中文,特别容易出现编码错乱的问题。那能不能生成唯一不变的URL链接呢?答案是可以的,已经有人给我们实现了。这就是我们要说的hexo-abbrlink
插件:
cnpm install hexo-abbrlink --save
修改博客站点配置文件_config.yml,将带有permalink:
的一行改为:
permalink: /:abbrlink.html # 此处可以自己设置
abbrlink:
alg: crc32 #算法: crc16(default) and crc32
rep: hex #进制: dec(default) and hex
5.2 LaTeX
推荐这个插件:https://github.com/hexojs/hexo-math
npm i hexo-math --save
有几个坑:
- 行间公式的换行,
\\
是远远不够的,要写成\\\tag\\
;- 上标符号
^
后面接了一个内容后要紧接着空格,不然会出错;例如:^\star =
,^{ \star}
- 下标符号
_
和后面的内容一定要有间隔,例如:x_ 1
,\mathscr{R}_ {++}
- 数学公式中,二阶导数的两个“撇”最好用代码
^{ \prime\prime}
来表示。- 数学公式中。减号’-'前后一定不要什么内容都没有,不然会被渲染为markdown语法中的item!
- latex中的公式环境eqnarray会在html中自动生成公式的tag,因此最好使用{eqnarray*}环境——或者添加
\notag
Hexo默认使用marked.js
去解析我们写的markdown
,比如一些符号,_
代表斜体,会被处理为标签,比如x_i
在开始被渲染的时候,处理为xi
,比如init会被处理成init。
因此,我们需要采取更换Markdown引擎的方式。
npm i hexo-renderer-mathjax --save
npm uninstall hexo-renderer-marked --save
npm install hexo-renderer-kramed --save
执行上面的命令即可,先卸载原来的渲染引擎,再安装新的。
之后,现在站点配置文件下添加如下内容:
# _config.yml
math:
katex:
css: 'https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css'
options:
throwOnError: false
mathjax:
css: 'https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css'
options:
conversion:
display: false
tex:
svg:
要在主题配置文件中开启mathjax
开关,把mathjax
默认的false
修改为true
,具体如下:
# MathJax Support
mathjax:
enable: true
per_page: false
别着急,这样还不够,还需要在文章的Front-matter
里打开mathjax
开关,如下:
---
title: index.html
date: 2016-12-28 21:01:30
tags:
mathjax: true
--
5.3 行内公式bug
对于Typora软件而言,按Crtl+/
(macOS按command+/
)。可以进入源代码模式,这样就能看清楚那部分代码除了问题了!这样修改效率很高!(如下图)

5.4 脚注
hexo-renderer-markdown-it-plus
插件(目前在使用,这个插件和hexo-renderer-kramed有冲突)
原生的主题是不支持网页脚注的,非常不方便。几经查找、探索,终于找到合适的方法,点击脚注即可实现跳转。
首先卸载原来的 markdown 渲染插件(我这里原来是 marked,这是 Hexo 提供的默认渲染插件),然后把 hexo-renderer-markdown-it-plus
装上:
cnpm un hexo-renderer-marked --save
cnpm i hexo-renderer-markdown-it-plus --save
然后在博客站点根目录下的_config.yml
中进行相应的配置。简单的配置方法只包括设置 markdown 格式,这里就不详述了。高级的配置方法中包含很多可选项:
markdown:
# 渲染设置
render:
# 置为true时,html内容保持不变;置为false时,html内容将被转义成普通字符串
html: true
# 是否生成与XHTML完全兼容的标签(虽然我不懂是什么意思)
xhtmlOut: false
# 置为true时,每个换行符都被渲染成一个<br>(即Hexo的默认表现);置为false时,只有空行才会被渲染为<br>(GFM的默认表现)
breaks: true
# 是否自动识别链接并把它渲染成链接
linkify: true
# 是否自动识别印刷格式(意思是把(c)渲染为©这样的)
typographer: true
# 如果typographer被设置为true,则该选项用于设置将dumb quotes("")自动替换为smart quotes
quotes: '“”‘’'
# 设置所需插件
plugins:
- markdown-it-abbr
- markdown-it-footnote
- markdown-it-ins
- markdown-it-sub
- markdown-it-sup
# 锚点设置(因为我没有尝试相关内容,所以就不翻译相关说明了)
anchors:
level: 2
collisionSuffix: 'v'
permalink: true
permalinkClass: header-anchor
permalinkSymbol: § #¶
这里设置了文中章节、脚注等内容的格式。
5.5 文章加密功能
有些文章的内容,我们不希望别人能看到,Hexo提供了加密文章的插件:
cnpm install --save hexo-blog-encrypt
然后在你的文章的头部(Front Matter)添加上对应的字段,如 password, abstract, message。(这时候文章模板blog/scaffod/post.md
的作用就体现出来了,这些内容加在模板里,以后生成的每一篇文章都能选择是否进行加密)
如果password:
后面空着,就是无密码;我发现如果密码内容加上英文输入法下的引号,引号也不会被算作密码内容。
---
title: 文章加密
date: 2019-01-04T22:20:13.000Z
category: 教程
tags:
- 博客
- Hexo
keywords: 博客文章密码
password: TloveY #是该博客加密使用的密码
abstract: 密码:TloveY #是该博客的摘要,会显示在博客的列表页
message: 输入密码,查看文章 #这个是博客查看时,密码输入框上面的描述性文字
---
不过加密文件的toc
无法上下拉,如果网页目录特别长就很不方便。原因是插件hexo-blog-encrypt
给toc
添加了display inline
(如下图),导致没办法滚动。

未加密的应该是这样的:

尝试修改插件:
找到需要修改的文件:node_modules/hexo-blog-encrypt/lib/hbe.js
。然后注释这一段代码:
// document.getElementById('hexo-blog-encrypt').style.display = 'inline';
把这两句代码中的display = 'inline'
,改为display = ''
。
// TOC part
var tocDiv = document.getElementById("toc-div");
if (tocDiv) {
tocDiv.style.display = '';
}
var tocDivs = document.getElementsByClassName('toc-div-class');
if (tocDivs && tocDivs.length > 0) {
for (var idx = 0; idx < tocDivs.length; idx++) {
tocDivs[idx].style.display = '';
}
}
然后就成功了!
自主修改hbe.js
文件完整版:
解决加密文章toc无法滚动的问题
//node_modules/hexo-blog-encrypt/lib/hbe.js
(() => {
'use strict';
const cryptoObj = window.crypto || window.msCrypto;
const storage = window.localStorage;
const storageName = 'hexo-blog-encrypt:#' + window.location.pathname;
const keySalt = textToArray('hexo-blog-encrypt的作者们都是大帅比!');
const ivSalt = textToArray('hexo-blog-encrypt是地表最强Hexo加密插件!');
// As we can't detect the wrong password with AES-CBC,
// so adding an empty div and check it when decrption.
const knownPrefix = "<hbe-prefix></hbe-prefix>";
const mainElement = document.getElementById('hexo-blog-encrypt');
const wrongPassMessage = mainElement.dataset['wpm'];
const wrongHashMessage = mainElement.dataset['whm'];
const dataElement = mainElement.getElementsByTagName('script')['hbeData'];
const encryptedData = dataElement.innerText;
const HmacDigist = dataElement.dataset['hmacdigest'];
function hexToArray(s) {
return new Uint8Array(s.match(/[\da-f]{2}/gi).map((h => {
return parseInt(h, 16);
})));
}
function textToArray(s) {
var i = s.length;
var n = 0;
var ba = new Array()
for (var j = 0; j < i;) {
var c = s.codePointAt(j);
if (c < 128) {
ba[n++] = c;
j++;
} else if ((c > 127) && (c < 2048)) {
ba[n++] = (c >> 6) | 192;
ba[n++] = (c & 63) | 128;
j++;
} else if ((c > 2047) && (c < 65536)) {
ba[n++] = (c >> 12) | 224;
ba[n++] = ((c >> 6) & 63) | 128;
ba[n++] = (c & 63) | 128;
j++;
} else {
ba[n++] = (c >> 18) | 240;
ba[n++] = ((c >> 12) & 63) | 128;
ba[n++] = ((c >> 6) & 63) | 128;
ba[n++] = (c & 63) | 128;
j += 2;
}
}
return new Uint8Array(ba);
}
function arrayBufferToHex(arrayBuffer) {
if (typeof arrayBuffer !== 'object' || arrayBuffer === null || typeof arrayBuffer.byteLength !== 'number') {
throw new TypeError('Expected input to be an ArrayBuffer')
}
var view = new Uint8Array(arrayBuffer)
var result = ''
var value
for (var i = 0; i < view.length; i++) {
value = view[i].toString(16)
result += (value.length === 1 ? '0' + value : value)
}
return result
}
async function getExecutableScript(oldElem) {
let out = document.createElement('script');
const attList = ['type', 'text', 'src', 'crossorigin', 'defer', 'referrerpolicy'];
attList.forEach((att) => {
if (oldElem[att])
out[att] = oldElem[att];
})
return out;
}
async function convertHTMLToElement(content) {
let out = document.createElement('div');
out.innerHTML = content;
out.querySelectorAll('script').forEach(async (elem) => {
elem.replaceWith(await getExecutableScript(elem));
});
return out;
}
function getKeyMaterial(password) {
let encoder = new TextEncoder();
return cryptoObj.subtle.importKey(
'raw',
encoder.encode(password),
{
'name': 'PBKDF2',
},
false,
[
'deriveKey',
'deriveBits',
]
);
}
function getHmacKey(keyMaterial) {
return cryptoObj.subtle.deriveKey({
'name': 'PBKDF2',
'hash': 'SHA-256',
'salt': keySalt.buffer,
'iterations': 1024
}, keyMaterial, {
'name': 'HMAC',
'hash': 'SHA-256',
'length': 256,
}, true, [
'verify',
]);
}
function getDecryptKey(keyMaterial) {
return cryptoObj.subtle.deriveKey({
'name': 'PBKDF2',
'hash': 'SHA-256',
'salt': keySalt.buffer,
'iterations': 1024,
}, keyMaterial, {
'name': 'AES-CBC',
'length': 256,
}, true, [
'decrypt',
]);
}
function getIv(keyMaterial) {
return cryptoObj.subtle.deriveBits({
'name': 'PBKDF2',
'hash': 'SHA-256',
'salt': ivSalt.buffer,
'iterations': 512,
}, keyMaterial, 16 * 8);
}
async function verifyContent(key, content) {
const encoder = new TextEncoder();
const encoded = encoder.encode(content);
let signature = hexToArray(HmacDigist);
const result = await cryptoObj.subtle.verify({
'name': 'HMAC',
'hash': 'SHA-256',
}, key, signature, encoded);
console.log(`Verification result: ${result}`);
if (!result) {
alert(wrongHashMessage);
console.log(`${wrongHashMessage}, got `, signature, ` but proved wrong.`);
}
return result;
}
async function decrypt(decryptKey, iv, hmacKey) {
let typedArray = hexToArray(encryptedData);
const result = await cryptoObj.subtle.decrypt({
'name': 'AES-CBC',
'iv': iv,
}, decryptKey, typedArray.buffer).then(async (result) => {
const decoder = new TextDecoder();
const decoded = decoder.decode(result);
// check the prefix, if not then we can sure here is wrong password.
if (!decoded.startsWith(knownPrefix)) {
throw "Decode successfully but not start with KnownPrefix.";
}
const hideButton = document.createElement('button');
hideButton.textContent = 'Encrypt again';
hideButton.type = 'button';
hideButton.classList.add("hbe-button");
hideButton.addEventListener('click', () => {
window.localStorage.removeItem(storageName);
window.location.reload();
});
// document.getElementById('hexo-blog-encrypt').style.display = 'inline';
document.getElementById('hexo-blog-encrypt').innerHTML = '';
document.getElementById('hexo-blog-encrypt').appendChild(await convertHTMLToElement(decoded));
document.getElementById('hexo-blog-encrypt').appendChild(hideButton);
// support html5 lazyload functionality.
document.querySelectorAll('img').forEach((elem) => {
if (elem.getAttribute("data-src") && !elem.src) {
elem.src = elem.getAttribute('data-src');
}
});
// support theme-next refresh
window.NexT && NexT.boot && typeof NexT.boot.refresh === 'function' && NexT.boot.refresh();
// TOC part
var tocDiv = document.getElementById("toc-div");
if (tocDiv) {
tocDiv.style.display = '';
}
var tocDivs = document.getElementsByClassName('toc-div-class');
if (tocDivs && tocDivs.length > 0) {
for (var idx = 0; idx < tocDivs.length; idx++) {
tocDivs[idx].style.display = '';
}
}
return await verifyContent(hmacKey, decoded);
}).catch((e) => {
alert(wrongPassMessage);
console.log(e);
return false;
});
return result;
}
function hbeLoader() {
const oldStorageData = JSON.parse(storage.getItem(storageName));
if (oldStorageData) {
console.log(`Password got from localStorage(${storageName}): `, oldStorageData);
const sIv = hexToArray(oldStorageData.iv).buffer;
const sDk = oldStorageData.dk;
const sHmk = oldStorageData.hmk;
cryptoObj.subtle.importKey('jwk', sDk, {
'name': 'AES-CBC',
'length': 256,
}, true, [
'decrypt',
]).then((dkCK) => {
cryptoObj.subtle.importKey('jwk', sHmk, {
'name': 'HMAC',
'hash': 'SHA-256',
'length': 256,
}, true, [
'verify',
]).then((hmkCK) => {
decrypt(dkCK, sIv, hmkCK).then((result) => {
if (!result) {
storage.removeItem(storageName);
}
});
});
});
}
mainElement.addEventListener('keydown', async (event) => {
if (event.isComposing || event.keyCode === 13) {
const password = document.getElementById('hbePass').value;
const keyMaterial = await getKeyMaterial(password);
const hmacKey = await getHmacKey(keyMaterial);
const decryptKey = await getDecryptKey(keyMaterial);
const iv = await getIv(keyMaterial);
decrypt(decryptKey, iv, hmacKey).then((result) => {
console.log(`Decrypt result: ${result}`);
if (result) {
cryptoObj.subtle.exportKey('jwk', decryptKey).then((dk) => {
cryptoObj.subtle.exportKey('jwk', hmacKey).then((hmk) => {
const newStorageData = {
'dk': dk,
'iv': arrayBufferToHex(iv),
'hmk': hmk,
};
storage.setItem(storageName, JSON.stringify(newStorageData));
});
});
}
});
}
});
}
hbeLoader();
})();
5.6 上传本地pdf并在线预览
安装插件
cnpm i hexo-pdf -S
如果是网上的pdf,参考格式:
{% pdf http://www.u.arizona.edu/~compitel/marston.pdf %}
如果直接在想添加的页面中 (md 文件) 添加pdf,就以网页标签的格式写代码:
<embed width='700' height='650' fullscreen='yes' src='http://www.u.arizona.edu/~compitel/marston.pdf'>点击打开pdf文件</embed>
对于本地的pdf文件,在搭建好的hexo博客的根目录的blog/source
文件夹下新建一个文件夹,如local_file
(用来存pdf文件。参考格式:
{% pdf '/local_file/test.pdf' %}
如果要让pdf文件在一个新的页面打开,参考下面的代码:
<a href='/local_file/test.pdf' target='_blank'>点击这里在新的网页打开pdf文件</a>
注意,要先执行hexo d
命令将本地的pdf文件上传到github
上,然后pdf文件才能显示在网站上!这里我们就不难发现,github的仓库充当的是远程服务器的作用!
参考:https://github.com/superalsrk/hexo-pdf
5.7 引用本地文件的链接
其实不只是pdf文件,其他文件也可以上传到github上,并且在文章中直接引用的。可以在根目录下创建自己的文件夹,比如说my_folder
,然后在这个文件夹下创建一个文档test.txt
。那么通过以下代码就可以直接插入到网站上面了:
<a href='/my_folder/test.txt' target='_blank'>test文件</a>
区别不过是,如果引用的是pdf文件,就能直接打开一个新页面并进行文件预览。如果是其他格式的文件,浏览器默认为我们把文件下载到本地。
5.8 归档计数
npm install --save hexo-generator-index
npm install --save hexo-generator-archive
然后在博客站点配置文件blog/_config.yml
中更改:
index_generator:
path: ''
per_page: 10
order_by: -date
# 归档页面
archive_generator:
per_page: 50
yearly: true
monthly: true
#效果,首页每间隔10篇文章就分页,归档页每间隔50篇文章才分页。
5.9 mermaid
这个博主介绍了一些标签外挂功能,可以按需索取:
mermaid
标签不允许嵌套于一些隐藏属性的标签外挂,例如:tag-hide
内的标签外挂和tabs
标签外挂,这会导致mermaid
图示无法正常显示,使用时请留意。请不要压缩
html
代码,不然会导致mermaid
显示异常
使用mermaid
标签可以绘制Flowchart
(流程图)、Sequence diagram
(时序图 )、Class Diagram
(类別图)、State Diagram
(状态图)、Gantt
(甘特图)和Pie Chart
(圆形图),具体可以查看mermaid文檔。
由于网页不支持直接显示Markdown
中的mermaid
语法,需要安装一个插件:
cnpm install --save hexo-filter-mermaid-diagrams
然后设置站点根目录:
# mermaid chart
mermaid: ## mermaid url https://github.com/knsv/mermaid
enable: true # default true
version: "7.1.2" # default v7.1.2
options: # find more api options from https://github.com/knsv/mermaid/blob/master/src/mermaidAPI.js
#startOnload: true // default true
最后一步需要对js
文件进行修改,需要修改的文件为footer.swig
或者footer.pug
或者footer.ejs
或者
//footer.swig
{% if theme.mermaid.enable %}
<script src='https://unpkg.com/mermaid@{{ theme.mermaid.version }}/dist/mermaid.min.js'></script>
<script>
if (window.mermaid) {
mermaid.initialize({{ JSON.stringify(theme.mermaid.options) }});
}
</script>
{% endif %}
//- footer.pug
if theme.mermaid.enable == true
script(type='text/javascript', id='maid-script' mermaidoptioins=theme.mermaid.options src='https://unpkg.com/mermaid@'+ theme.mermaid.version + '/dist/mermaid.min.js' + '?v=' + theme.version)
script.
if (window.mermaid) {
var options = JSON.parse(document.getElementById('maid-script').getAttribute('mermaidoptioins'));
mermaid.initialize(options);
}
<!-- footer.ejs -->
<% if (theme.mermaid.enable) { %>
<script src='https://unpkg.com/mermaid@<%= theme.mermaid.version %>/dist/mermaid.min.js'></script>
<script>
if (window.mermaid) {
mermaid.initialize({theme: 'forest'});
}
</script>
<% } %>
例子
pie title 日常支出 "衣" : 100 "食" : 200 "住" : 200 "行" : 200
flowchart LR 1 --> 2 x--x 3 --- 4 <--> 5
stateDiagram-v2 [*] --> Still Still --> [*] Still --> Moving Moving --> Still Moving --> Crash Crash --> [*]
5.10 插入视频
首先安装插件:
cnpm i hexo-tag-aplayer --save
cnpm i hexo-tag-dplayer --save
然后写代码:
<p align='center'>
<video height="400" autoplay="autoplay" controls="controls" allowfullscreen="true">
<source src="/local_video/各省GDP排行榜.mp4" type="video/mp4">
</video>
</p>
对于B站视频而言,在网页端获取分享链接,然后直接粘贴进markdown文件即可(如下图)。

当然这种方式生成的视频无法控制其大小和位置,因此我这么写:
<p align='center'>
<iframe src="//player.bilibili.com/player.html?aid=799951124&bvid=BV1ey4y1b7GA&cid=320466524&page=1" style="width:100%; height:600px;" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
</p>
效果如下图:
至于来自其他视频网站的资源如何挂在自己的博客上,需要自己慢慢摸索了,不一一列举。
Safari上出现问题的原因可以参考这篇文章:修复video标签在safari中无法播放mp4视频的问题。
6 butterfly主题
注意:不要把个人需要的文件/图片放在主题
source
文件夹里,因为在升级主题的过程中,可能会把文件覆盖删除了。在Hexo根目录的
source
文件夹里,创建一个文件夹来放置个人文件/图片。引用文件时直接写为/文件夹名称/文件名
。这个技巧在配置主题文件时尤其要注意!
6.1 主题下载
这是从大神的github仓库里把主题克隆到我们的博客网站目录blog/themes/butterfly
下:
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly
这个主题的模板引擎为pug
格式的文件,因此需要安装对应的支持程序:(至于模板引擎文件是啥,可参考后面——不知道也无所谓)
cnpm install hexo-renderer-pug hexo-renderer-stylus --save
这里我经常会遇到一个问题:我的错误是 Cannot GET /,因此在public目录下寻找index.html
是否存在。如果不存在,执行下面代码对其进行修复:
npm audit fix
查看是否少了什么组件,通过cnpm install hexo-xxx-xxx
安装即可。
然后在博客站点配置文件blog/_config.yml
中更改:
theme: butterfly
注意,themes后面的参数,要和主题所在的文件夹名一致!
6.1a 网站主题的更新
此部分仅对于已经配置好的用户参考。如果您是第一次使用本主题,请直接前往6.2。
由于大多数主题都会定期更新,但是把自己原来的个性化修改内容重新在新版本上体现,又会浪费大量时间。这里总结一个相对节约时间、清晰明了的办法:利用github自带的compare功能。
首先fork更新的主题仓库,这样自己的github上就有了这个新的repository,然后把这个仓库clone到本地,然后命名为Jerryc227
。
git clone git@github.com:dhndzwxj/hexo-theme-butterfly.git
然后将这个本地文件夹与原作者的仓库建立联系
git remote add upstream git@github.com:jerryc127/hexo-theme-butterfly.git
接下来将自己的旧版本的主题文件拷贝并替换掉Jerryc227
的原有内容,然后“三连”。
git add .
git commit -m '主题更新'
git push origin master
这样自己fork掉的仓库的内容就成功更新了。最后将自己本地的修改同原作者的仓库做比较:

6-2
6.2 网站背景
默认显示白色,可设置图片或者颜色。
修改主题配置文件blog/themes/butterfly/_config.yml
#圖片格式 url(http://xxxxxx.com/xxx.jpg)
#顏色(HEX值/RGB值/顔色單詞/漸變色)
#留空 不顯示背景
background:
留意: 如果你的网站根目录不是/
,使用本地图片时,需加上你的根目录。
例如:网站是https://yoursite.com/blog
,引用一张img/xx.png
图片,则设置background
为 url(/blog/img/xx.png)
6.3 字数统计
npm install hexo-wordcount --save
修改主题配置文件:
wordcount:
enable: true
post_wordcount: true
min2read: true
total_wordcount: true
如需调整右侧卡片网站信息内项目的数据,在文件/butterfly/layout/includes/widgets/card_webinfo.pug
中操作。
6.3a 文章meta信息
post_meta:
page: # Home Page
...
post:
...
tags: true # true or false 文章頁是否顯示標籤
我发现即使将主题配置文件中如下tags设置为true,依然没有显示标签。检查后发现是缺少对应代码,在大概38行的位置添加如下代码:
//- \blog\themes\butterfly\layout\includes\header\post-info.pug
if (theme.post_meta.post.tags && page.tags.data.length > 0)
span.post-meta-tags
span.post-meta-separator |
each item, index in page.tags.data
i.fas.fa-tag
a(href=url_for(item.path)).article-meta__tags #[=item.name]
注意该if
要和前面的if (theme.post_meta.post.categories && page.categories.data.length > 0)
缩进相同。
小结:各个特殊符号的含义
#
:例如#post-meta
,生成的HTML代码为<div id='post-meta'>
。(不过会另起一行).
:例如.meta-firstline
,生成的HTML代码为<div class='meta-firstline'>
。.
:span.post-meta-author=page.author
,生成的HTML代码为<span class='post-meta-author'>page.author代表的内容</span>
。
若要把第一行的meta信息整体修改,下面是现成的代码[大概第8行](对于author,我在其网页标签添加了id,<div id="post-meta-author">
)
//- \blog\themes\butterfly\layout\includes\header\post-info.pug
.meta-firstline
if (theme.post_meta.post.author)
i.fa-solid.fa-user
span.post-meta-author=page.author
if (theme.post_meta.post.date_type)
span.post-meta-date
if (theme.post_meta.post.author)
span.post-meta-separator |
if (theme.post_meta.post.date_type === 'both')
i.far.fa-calendar-alt.fa-fw.post-meta-icon
span.post-meta-label= _p('post.created')
time.post-meta-date-created(datetime=date_xml(page.date) title=_p('post.created') + ' ' + full_date(page.date))=date(page.date, config.date_format)
span.post-meta-separator |
i.fas.fa-history.fa-fw.post-meta-icon
span.post-meta-label= _p('post.updated')
time.post-meta-date-updated(datetime=date_xml(page.updated) title=_p('post.updated') + ' ' + full_date(page.updated))=date(page.updated, config.date_format)
else
- let data_type_update = theme.post_meta.post.date_type === 'updated'
- let date_type = data_type_update ? 'updated' : 'date'
- let date_icon = data_type_update ? 'fas fa-history' :'far fa-calendar-alt'
- let date_title = data_type_update ? _p('post.updated') : _p('post.created')
i.fa-fw.post-meta-icon(class=date_icon)
span.post-meta-label= date_title
time(datetime=date_xml(page[date_type]) title=date_title + ' ' + full_date(page[date_type]))=date(page[date_type], config.date_format)
if (theme.post_meta.post.categories && page.categories.data.length > 0)
span.post-meta-categories
if (theme.post_meta.post.date_type)
span.post-meta-separator |
each item, index in page.categories.data
i.fas.fa-inbox.fa-fw.post-meta-icon
a(href=url_for(item.path)).post-meta-categories #[=item.name]
if (index < page.categories.data.length - 1)
i.fas.fa-angle-right.post-meta-separator
也在网站首页修改展示文章的meta[大概在第26行添加]
//- \blog\themes\butterfly\layout\includes\mixins\post-ui.pug
if (theme.post_meta.page.author)
span.post-meta-author
i.fa-solid.fa-user
span.post-meta-author=article.author
span.article-meta-separator |
6.4 主页和文章页面侧边栏
需要在文件/butterfly/layout/includes/widgets/index.pug
中操作。我这里是去掉了文章页面的“最新文章”部分,于是我在读懂这段代码后,把“最新文章”和“广告”对应的两条代码删除了。此外,我也想让头像成为sticky_layout
(随着网页滑动跟着走的效果),我的代码改成了下面的样子:
//- /butterfly/layout/includes/widgets/index.pug
#aside-content.aside-content
//- post
if is_post()
//- 如果是文章页面,下面对应的else应该指的是主页面
- const tocStyle = page.toc_style_simple
- const tocStyleVal = tocStyle === true || tocStyle === false ? tocStyle : theme.toc.style_simple
if showToc && tocStyleVal
.sticky_layout
include ./card_post_toc.pug
else
!=partial('includes/widget/card_author', {}, {cache: true})
!=partial('includes/widget/card_announcement', {}, {cache: true})
!=partial('includes/widget/card_top_self', {}, {cache: true})
.sticky_layout
if showToc
include ./card_post_toc.pug
!=partial('includes/widget/card_recent_post', {}, {cache: true})
!=partial('includes/widget/card_ad', {}, {cache: true})
else
//- page
!=partial('includes/widget/card_author', {}, {cache: true})
!=partial('includes/widget/card_announcement', {}, {cache: true})
!=partial('includes/widget/card_top_self', {}, {cache: true})
.sticky_layout
if showToc
include ./card_post_toc.pug
!=partial('includes/widget/card_recent_post', {}, {cache: true})
//- !=partial('includes/widget/card_ad', {}, {cache: true})
//- !=partial('includes/widget/card_newest_comment', {}, {cache: true})
!=partial('includes/widget/card_categories', {}, {cache: true})
!=partial('includes/widget/card_tags', {}, {cache: true})
//- !=partial('includes/widget/card_archives', {}, {cache: true})
!=partial('includes/widget/card_webinfo', {}, {cache: true})
!=partial('includes/widget/card_bottom_self', {}, {cache: true})
个人觉得card_author
头像下面“文章”“标签”“分类”三个栏目没啥用,就把这个文件butterfly/layout/includes/widget/card_author.pug
中的这些代码删掉,然后就有简洁的头像了~
.card-info-data.site-data.is-center
a(href=url_for(config.archive_dir) + '/')
.headline= _p('aside.articles')
.length-num= site.posts.length
a(href=url_for(config.tag_dir) + '/')
.headline= _p('aside.tags')
.length-num= site.tags.length
a(href=url_for(config.category_dir) + '/')
.headline= _p('aside.categories')
.length-num= site.categories.length
此外,主页侧边栏“最新文章”、归档以及主页文章简介面上有图片,如果不想要这些图片,需要在主题配置文件下面的部分进行修改:
cover:
# display the cover or not (是否顯示文章封面)
index_enable: false
aside_enable: false
archives_enable: false
# the position of cover in home page (封面顯示的位置)
# left/right/both
position: both
# When cover is not set, the default cover is displayed (當沒有設置cover時,默認的封面顯示)
default_cover: /private_img/banner2.gif
# - https://i.loli.net/2020/05/01/gkihqEjXxJ5UZ1C.jpg
文章加密功能的设置,在文件butterfly/layout/includes/widget/card_post_toc.pug
中进行编辑。
6.5 文章置顶
【推荐】hexo-generator-index
从 2.0.0 开始,已经支持文章置顶功能。你可以直接在文章的front-matter
区域里添加sticky: 1
属性来把这篇文章置顶。数值越大,置顶的优先级越大。
---
title: {{ title }}
author: 揭晓
categories:
- null
- null
- null
tags:
- null
- null
- null
mathjax:
abstract: 这里有东西被加密了,需要输入密码查看哦。
message: 您好,这里需要密码。
wrong_pass_message: 抱歉,这个密码看着不太对,请再试试。
wrong_hash_message: 抱歉,这个文章不能被纠正,不过您还是能看看解密后的内容。
typora-root-url: images
abbrlink:
date: {{ date }}
sticky:
description:
password:
---
6.6 目录折叠
由于我个人的目录比较大,完全展开三级目录的话,右边栏就完全被目录铺满了。butterfly
主题提供了目录可折叠的选项,只需要在主题配置文件/butterfly/config.yml
设置:
card_categories:
enable: true
limit: 0 # if set 0 will show all
expand: true # none/true/false
sort_order: # Don't modify the setting unless you know how it works
6.7 本地搜索
6.7.1 主题v4.5.1原生功能
插件安装
npm install hexo-generator-search --save
修改站点配置文件_config.yml
, 添加以下内容
search:
path: search.xml
field: post #默认post,将覆盖博客所有帖子;page,只覆盖博客所有页面;all所有
content: true #若为false,则设置不搜索文章内容,只根据搜索标题
format: html
主题配置文件/blog/themes/butterfly/_config.yml
如下修改:
# Local search
local_search:
enable: true
preload: true #預加載,開啟後,進入網頁後會自動加載搜索文件。關閉時,只有點擊搜索按鈕後,才會加載搜索文件
CDN:
6.7.2 原生搜索功能解读
需要编辑的文件为主题下的:themes/butterfly/source/js/search/local-search.js
。
基于butterfly下local-search文档解读的js学习笔记。
通过阅读这个文件,我发现决定了搜索结果的输出内容的代码为:[大概在166行]
if (dataContent !== '') {
str += '<p class="search-result">' + pre + matchContent + post + '</p>'
}
如果要自定义搜索结果样式(不推荐修改,因为加载速度会变慢)最关键的一行代码为[大概在153行]:
keywords.forEach(keyword => {
if(keyword[0]==='#' && keyword.length>1){
keyword = keyword.substring(1)
}
let regexStr = keyword
const specialRegex = /[^\w\s]+/ // match special characters
if (keyword.length === 1 && specialRegex.test(keyword)) {
regexStr = `\\${keyword}`
}
matchContent = matchContent.replaceAll(keyword, '<span class="search-keyword">' + keyword + '</span>')
dataTitle = dataTitle.replaceAll(keyword, '<span class="search-keyword">' + keyword + '</span>')
})
由上述两段代码此可见,最关键的变量是matchContent
(正文中包含关键词的部分),它是怎么定义的?[大概在151行]
let matchContent = dataContent.substring(start, end)
它由dataContent
定义【范围是从start到end】,dataContent
又是怎么定义的?
let dataTitle = data.title ? data.title.trim().toLowerCase() : '' //获取标题
const dataContent = data.content ? data.content.trim().replace(/<[^>]+>/g, '').toLowerCase() : '' //获取正文,其中【.replace(/<[^>]+>/g, '')】去掉了网页标签(正则表达式的意思是去掉'<>'中的内容)
const dataUrl = data.url.startsWith('/') ? data.url : GLOBAL_CONFIG.root + data.url //获取链接
+ const dataTags = data.tags ? data.tags : '' //获取标签
这里定义了文章的标题dataTitle
、正文内容dataContent
、文章链接dataUrl
。这三项内容都来自data
变量的方法。而data
的方法是怎么定义的?
const res = await response.text()
const t = await new window.DOMParser().parseFromString(res, 'text/xml')
const a = await t
data = [...a.querySelectorAll('entry')].map(item =>{
return {
title: item.querySelector('title').textContent,
content: item.querySelector('content') && item.querySelector('content').textContent,
url: item.querySelector('url').textContent,
+ tags: item.querySelector('tags') && item.querySelector('tags').textContent
}
})
back_selec
通过读data
定义的代码,我们发现querySelector
很关键!深入学习可以参考js学习笔记的相关内容。
其实对于data变量的各个方法,其定义的根据来自插件hexo-generator-search
生成的模板文件/hexo-generator-search/templates/search.xml
。打开这个search.xml
文件,我们就能看到entry
title
url
tags
tag
这些奇怪的css选择器了。
6.7.3 搜索结果对话框显示标签
上面的tags
行是参照content
行添加的(url行末尾要加一个英文逗号,否则会报错)。因为一篇post的front-matter不一定包含tags
和content
,因此需要做一下是否有tags的判断(&&
),否则会出错。
原版代码:
if (dataContent !== '') {
str += '<p class="search-result">' + pre + matchContent + post + '</p>'
}
if (dataContent !== '') {
//- 自定义开始:生成的搜索结果框里,加入显示tags
let splitT = ''
//- 第一步:下面是去掉dataTags里非汉字和字母(数字)的部分,然后用两个汉字分号';;'把各个tags分隔开(保存在spliT变量里)
let space = 1
for (let i=0;i<dataTags.length;i++){
if (/\S/.test(dataTags[i])){
// \S 匹配Unicode非空白
space = 0
splitT = splitT.concat(dataTags[i])
}else{
if(space===0){
splitT = splitT + ';;'
space = 1
}
}
}
//去掉splitT末尾的双分号;;
for(let i=0;i<splitT.length;i++){
let l = splitT.length
if(splitT[l-1]==';' && l>1){
splitT = splitT.substring(0,l-2)
}
}
//- 第二步: highlight all keywords
keywords.forEach(keyword => {
if(keyword[0] === '#' & keyword.length>1){
keyword = keyword.substring(1) // 如果第一个字符为#且长度大于1,将关键词第一个#去掉后再匹配
}
splitT = splitT.replace(new RegExp(keyword,"gi"),'<span class="search-keyword">' + keyword + '</span>')
})
//- 第三步:由于第一步产生的为纯文本且包括双分号,此步骤去掉分号且加上fas fa-tag、控制字体(保存在splitTags里)
let splitTags = '<br/><i class="fas fa-tag"><span style="font-family:times">'
space = 1
for(let i=0;i<splitT.length;i++){
if(splitT[i] !== ';'){
space = 0
splitTags = splitTags.concat(splitT[i])
}else{
if(space===0){
splitTags = splitTags + '</span></i>   <i class="fas fa-tag"><span style="font-family:times">'
space = 1
}
}
}
splitTags = splitTags + '</span></i>'
post = /S/.test(splitT) ? post + splitTags : post
//- 自定义结束
str += '<p class="search-result">' + pre + matchContent + post + '</p>'
}
效果如下:

6.7.4 按照标签搜索
【最终目标】搜索框输入的关键词,如果其第一个字符是
#
且关键词长度大于1,就进行标签匹配;否则和之前的搜索功能一样。【准备工作】
需要编辑的文件为主题下的:
themes/butterfly/source/js/search/local-search.js
。hexo-generator-search插件里负责搜索的js文件为
/hexo-generator-search/lib/json_generaotr.js
。该文件里面提供了获取帖子(posts)和页面(pages)标题(title)、内容(content)、标签(tags)、目录(categories)……,确实没有获取author的方法。不要随便添加,因为还要和/hexo-generator-search/templates/search.xml
对应上!
先读一下butterfly主题local-search的代码逻辑!大致分为以下几个步骤:
①搜索功能从大概96行data.forEach(data => {
开始。foreach()是一个遍历函数,用来遍历所有的帖子。遍历过程分为两个子步骤:第一,先看遍历的帖子的标题、正文、标签(tags是我自己加的)是否包含关键词,如果包含令isMatch=true,否则isMatich=false;【大概107-128行】第二,对于isMatch=true的帖子,在搜索框显示其内容【大概131-201行】。
②我的目的是,搜索框输入的关键词,如果其第一个字符是#
且关键词长度大于1,就进行标签匹配;否则和之前的搜索功能一样。
③为了匹配标签,需要在data.forEach(data => {
部分新定义2个变量。[大概103行]
let dataTags = data.tags ? data.tags.trim().toLowerCase() : '' //获取标签
let indexTag = -1 //- +++添加标签定位变量
let l_keywords = keywords.toString().split('').length //- +++获取搜索关键词的长度
以及给data增加了一个tags方法:【前面已添加】
data = [...a.querySelectorAll('entry')].map(item =>{
return {
title: item.querySelector('title').textContent,
content: item.querySelector('content') && item.querySelector('content').textContent,
url: item.querySelector('url').textContent,
+ tags: item.querySelector('tags') && item.querySelector('tags').textContent
}
})
主要是对下面这点代码进行魔改:【大概111行】
keywords.forEach((keyword, i) => {
indexTitle = dataTitle.indexOf(keyword)
indexContent = dataContent.indexOf(keyword)
if (indexTitle < 0 && indexContent < 0) {
isMatch = false
} else {
if (indexContent < 0) {
indexContent = 0
}
if (i === 0) {
firstOccur = indexContent
}
}
})
魔改之后是下面这个样子:
if (dataTitle !== '' || dataContent !== '') {
keywords.forEach((keyword, i) => {
if (keywords[0][0] === '#' && l_keywords > 1 && keywords[0][1] !== '#'){ //- 最后一个判断条件修复了正文中'##'无法搜索的问题
//如果关键词第一个字符是#且长度大于1,那么进行tag搜索
keyword = keyword.substring(1) // 将关键词第一个#去掉后再匹配
//- 定义dataTags0的意义:去掉tags里面的网页标签代码,否则会把网页标签里面的代码(非正文内容)也匹配
let dataTags0 = ''
for(let i=0; i<dataTags.length;i++){
dataTags0 = dataTags0.concat(dataTags[i].replace(/<[^>]+>/g, ''))
}
dataTags0 = dataTags0.trim().toLowerCase()
indexTag = dataTags0.indexOf(keyword)
if ( indexTag < 0 ){
isMatch = false
}else{
firstOccur = 0
}
}else {
indexTitle = dataTitle.indexOf(keyword)
indexContent = dataContent.indexOf(keyword)
if (indexTitle < 0 && indexContent < 0) {
isMatch = false
} else {
if (indexContent < 0) {
indexContent = 0
}
if (i === 0) {
firstOccur = indexContent
}
}
}
})
} else {
isMatch = false
}
6.7a butterfly4.8的本地搜索
2023年4月10日,jerryc更新了butterfly主题的4.8版本,这个版本对本地搜索功能进行了大幅度修改,特别是基础框架进行了比较大的调整。对于我而言,意味着之前折腾的“标签搜索”、“标题搜索”的功能都没法派上用场了。于是为了自己的使用方便,不能不重新探索,在此做学习笔记。
6.7a.1 在搜索结果框内显示tags标签
搜索结果的展示对应于以下代码:(大概在154-161行)
if (slicesOfTitle.length !== 0) {
resultItem += `<div class="local-search-hit-item"><a href="${url.href}"><span class="search-result-title">${this.highlightKeyword(title, slicesOfTitle[0])}</span>`
} else {
resultItem += `<div class="local-search-hit-item"><a href="${url.href}"><span class="search-result-title">${title}</span>`
}
slicesOfContent.forEach(slice => {
resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...</p></a>`
})
如前,定义数据的方法在大概181-186行,我们需要增加一个tags方法:
this.datas = isXml
? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => ({
title: element.querySelector('title').textContent,
content: element.querySelector('content').textContent,
url: element.querySelector('url').textContent,
+ tags: element.querySelector('tags') && element.querySelector('tags').textContent
}))
接下来189-194行也增添一个匹配tags的相关内容:
// Only match articles with non-empty titles
this.datas = this.datas.filter(data => data.title).map(data => {
data.title = data.title.trim()
data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : ''
data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/')
+ data.tags = data.tags ? data.tags : ''
return data
})
实现本地搜索并显示结果的关键函数是getResultItems
(大概在104行开始)。
介绍几个作者的自定义函数:
highlightKeyword (val, slice)
:将文段val片段中包含的slice替换为指定的强调格式。mergeIntoSlice(start, end, index)
:(57-88行)Merge hits into slicesgetIndexByWord (words, text, caseSensitive = false)
:关键词为words
,查找的文段为text
,caseSensitive
用来控制匹配是否对大小写敏感。
从第158行代码我们可以看出,slicesOfContent
是需要进行查找替换的正文变量。
slicesOfContent.forEach(slice => {
resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...</p></a>`
})
其定义为:(在121-129行)
let slicesOfContent = []
while (indexOfContent.length !== 0) {
const item = indexOfContent[0]
const { position } = item
// Cut out 120 characters. The maxlength of .search-input is 80.
const start = Math.max(0, position - 20)
const end = Math.min(content.length, position + 100)
slicesOfContent.push(this.mergeIntoSlice(start, end, indexOfContent))
}
可见,变量slicesOfContent
非空(会被定义)的前提是变量indexOfContent
长度不为0。下面我们去看看indexOfContent
变量是怎么定义的?(106-111行)
原版:
this.datas.forEach(({ title, content, url }) => {
// The number of different keywords included in the article.
const [indexOfTitle, keysOfTitle] = this.getIndexByWord(keywords, title)
const [indexOfContent, keysOfContent] = this.getIndexByWord(keywords, content)
const includedCount = new Set([...keysOfTitle, ...keysOfContent]).size
修改版:
this.datas.forEach(({ title, content, url, tags }) => {
// The number of different keywords included in the article.
const [indexOfTitle, keysOfTitle] = this.getIndexByWord(keywords, title)
const [indexOfContent, keysOfContent] = this.getIndexByWord(keywords, content)
+ const [indexOfTags, keysOfTags] = this.getIndexByWord(keywords, tags)
const includedCount = new Set([...keysOfTitle, ...keysOfContent]).size
因此我们可以模仿slicesOfContent
的定义,也定义一个slicesOfTags
。【目前来看,此步完全没有必要】
其实只要在原版154-156行附近添加我们之前的修改即可:
if (slicesOfTitle.length !== 0) {
resultItem += `<div class="local-search-hit-item"><a href="${url.href}" target="_blank"><span class="search-result-title">${this.highlightKeyword(title, slicesOfTitle[0])}</span>`
} else {
resultItem += `<div class="local-search-hit-item"><a href="${url.href}" target="_blank"><span class="search-result-title">${title}</span>`
}
//----------- 自定义:搜索框内显示文章的tags -------------------
let dataTags = tags
let splitT = ''
//- 第一步:下面是去掉dataTags里非汉字和字母(数字)的部分,然后用两个汉字分号';;'把各个tags分隔开(保存在spliT变量里)
let space = 1
for (let i=0;i<dataTags.length;i++){
if (/\S/.test(dataTags[i])){
// \S 匹配Unicode非空白
space = 0
splitT = splitT.concat(dataTags[i])
}else{
if(space===0){
splitT = splitT + ';;'
space = 1
}
}
}
//去掉splitT末尾的双分号;;,将字母变为小写
for(let i=0;i<splitT.length;i++){
let l = splitT.length
if(splitT[l-1]==';' && l>1){
splitT = splitT.substring(0,l-2)
}
splitT = splitT.trim().toLowerCase()
}
//- 第二步: highlight all keywords
keywords.forEach(keyword => {
if(keyword[0] === '#' & keyword.length>1){
keyword = keyword.substring(1) // 如果第一个字符为#且长度大于1,将关键词第一个#去掉后再匹配
}
splitT = splitT.replaceAll(keyword,'<span class="search-keyword">' + keyword +'</span>')
})
//- 第三步:由于第一步产生的为纯文本且包括双分号,此步骤去掉分号且加上fas fa-tag、控制字体(保存在splitTags里)
let splitTags = '<br/><i class="fas fa-tag"><span style="font-family:times">'
space = 1
for(let i=0;i<splitT.length;i++){
if(splitT[i] !== ';'){
space = 0
splitTags = splitTags.concat(splitT[i])
}else{
if(space===0){
splitTags = splitTags + '</span></i>   <i class="fas fa-tag"><span style="font-family:times">'
space = 1
}
}
}
splitTags = splitTags + '</span></i>'
slicesOfContent.forEach(slice => {
resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...${splitTags}</p></a>`
})
6.7a.2 扩展搜索功能:按照文章的tags进行搜索
介绍几个作者的自定义函数:
highlightKeyword (val, slice)
:将文段val片段中包含的slice替换为指定的强调格式。mergeIntoSlice(start, end, index)
:(57-88行)Merge hits into slicesgetIndexByWord (words, text, caseSensitive = false)
:关键词为words
,查找的文段为text
,caseSensitive
用来控制匹配是否对大小写敏感。
一些变量的类型:
slicesOfContent
:ObjectslicesOfTitle
:ObjectindexOfContent
:ObjectindexOfTags
:Objecttags
:Stringkeywords
:Objectkeywords[0]
:String
查看Object的属性和值:
resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...<br/>${Object.keys(keywords)+Object.values(keywords)}</p></a>` //测试版本,获取Object的相关信息
JS常见坑:对象赋值会影响原对象
现象:直接用
=
的方式把一个对象赋值给另一个对象,会导致修改新对象时,原对象也发生变化var obj1 = {'name': '1111'}; var obj2 = obj1; obj2.name = '2222'; console.log(obj1.name); //'2222'
原因:JavaScript 中对象的赋值是默认引用赋值的(两个对象指向相同的内存地址)。具体内容可参考JavaScript语法中的Object部分。
问题出在第175行的判断条件上:
while (indexOfContent.length !== 0) {
const item = indexOfContent[0]
const { position } = item
// Cut out 120 characters. The maxlength of .search-input is 80.
const start = Math.max(0, position - 20)
const end = Math.min(content.length, position + 100)
slicesOfContent.push(this.mergeIntoSlice(start, end, indexOfContent))
}
这段的本意是:如果正文(content)中匹配到关键字,那么就显示正文,否则搜索结果里就不输出包含关键字的正文片段。于是问题(Bug)就出现了,如果一个关键字是标题中有的但是正文没有的,那么输出的结果就只有标题没有正文。
6.7b 3-hexo主题的本地搜索
主题项目地址:https://github.com/yelog/hexo-theme-3-hexo。
搜索效果:
![]() 通过作者搜索 |
![]() 通过标签搜索 |
ejs和js代码
<nav id="title-list-nav">
<% site.posts.forEach(function(post, i){ %>
<% if (post.hidden === true) { return true } %>
<a <% if(post.top){%>id="top"<%}%> class="<%= __('all_articles') %> <% post.categories.forEach(function(category, i){ %><%=category.name%> <% }) %>"
href="<%- url_for(post.path) %>"
data-tag="<% post.tags.forEach(function(tag, i){ %><%=tag.name%><% if (i+1<post.tags.length){%>,<%}})%>"
data-author="<% if(theme.author && theme.author.on==true && post.author) {%><%=post.author %><%}%>" >
<span class="post-title" title="<%=post.title %>"><%=post.title %></span>
<span class="post-date" title="<%= date(post.date, 'YYYY-MM-DD HH:mm:ss')%>"><%= date(post.date, 'YYYY/MM/DD') %></span>
</a>
<% }) %>
<div id="no-item-tips">
</div>
</nav>
// /source/js/script.js
$searchInput.on("input", function (e) {
inputChange();
});
$searchInput.on("change", function (e) {
inputChange();
});
/*根据搜索条件,过滤文章列表*/
function inputChange() {
var i;
setTimeout(function () {
$searchInput.focus()
}, 50)
var val = $searchInput.val().trim();
$('#search-panel').show().siblings().hide()
$outlineList.hide();
if ($('#local-search-result').length>0) {
if (val.length>3 && (val.substr(0,3).toLowerCase() === 'in:' || val.substr(0,3).toLowerCase()==='in:')) {
$outlineList.hide();
$('#title-list-nav').hide()
$('#local-search-result').show();
searchAll(val.substr(3))
} else {
$('#title-list-nav').show();
$('#local-search-result').hide();
}
} else {
$outlineList.hide();
$('#title-list-nav').show();
}
var categories = $(".nav-left ul li>div.active").data('rel').split('<--->')
// 处理特殊字符
for (i = 0; i < categories.length; i++) {
categories[i] = categories[i]
.replace(/(?=\/|\\|#|\(|\)|\[|\]|\.)/g, "\\")
}
var activeTitle = categories.join('.');
var searchType = '';
var containType = '';
$('#no-item-tips').hide()
$(".nav-right nav a .post-title .search-keyword").each(function () {
$(this).parent().html($(this).parent().attr('title'))
})
if (val === "") {
$(".nav-right nav a").css("display", "none");
$(".nav-right nav a." + activeTitle).css("display", "block");
} else if (val.substr(0, 1) === "#") {
searchType = '标签'
containType = '为'
if (val.substr(1).length !== 0) {
$(".nav-right nav a").css("display", "none");
$(".nav-right nav").find("a." + activeTitle + ":contains_tag('" + val.substr(1) + "')").css("display", "block");
}
} else if (val.substr(0, 1) === "@") {
searchType = '作者'
containType= '为'
if (val.substr(1).length !== 0) {
$(".nav-right nav a").css("display", "none");
$(".nav-right nav").find("a." + activeTitle + ":contains_author('" + val.substr(1) + "')").css("display", "block");
}
} else {
searchType = '标题'
containType = '包含'
// $(".nav-right nav a").css("display", "none");
$(".nav-right nav").find("a." + activeTitle + ":"+ ($('#search-panel > .icon-case-sensitive').hasClass('active') ? 'containsSensitive' : 'contains') + "('" + val + "')").css("display", "block");
$(".nav-right nav a").each(function () {
var title = $(this).children('.post-title').attr('title');
for (i = 0; i < categories.length; i++) {
if (!$(this).hasClass(categories[i])) {
$(this).css('display', 'none').children('.post-title').html(title)
return true;
}
}
var caseSensitive = $('#search-panel > .icon-case-sensitive').hasClass('active');
var vals = (caseSensitive ? val : val.toUpperCase()).split('');
var inputReg = new RegExp(vals.join('[\\s\\S]*'));
if (inputReg.test(caseSensitive ? title : title.toUpperCase())) {
// 给匹配到的字符添加高亮
var nowPos = 0;
var titleHtml = title.split('')
var titleCase = (caseSensitive ? title : title.toUpperCase()).split('')
for (i = 0; i < vals.length; i++) {
nowPos = titleCase.indexOf(vals[i], nowPos)
titleHtml[nowPos] = ['<span class="search-keyword">', titleHtml[nowPos], '</span>'].join('')
}
$(this).css('display', 'block').children('.post-title').html(titleHtml.join(''))
} else {
$(this).css('display', 'none').children('.post-title').html(title)
}
})
}
if (val !== '') {
$('#default-panel .icon-search').addClass('active')
if (val === 'in:') {
$('#no-item-tips').show().html('正在进行全局关键字搜索,请输入关键字');
} else if (!val.startsWith('in:') && $(".nav-right nav a:visible").length === 0) {
$('#no-item-tips').show().html('未在 <span>' + activeTitle + '</span> 分类中找到'+ searchType + containType + ' <span>' + val.replace(/^[@|#]/g,'') + '</span> 的文章');
}
} else {
$('#default-panel .icon-search').removeClass('active')
}
}
6.8 博客首页pagination放在最近文章的顶端,便于翻页
需要修改的文件为butterfly/layout/index.pug
。改后的代码为:
extends includes/layout.pug
block content
include ./includes/mixins/post-ui.pug
#recent-posts.recent-posts
include includes/pagination.pug
+postUI
include includes/pagination.pug
效果如下:

6.8a tag-hide
inline
在文本里面添加按钮隐藏内容,只限文字。格式为:
{% hideInline content,display,bg,color %}
content
: 文本内容display
: 按钮显示的文字(可选)bg
: 按鈕的背景颜色(可选)color
: 按钮文字的颜色(可选)
例子如下:
哪个英文字母最酷?
门里站着一个人?
6.9 字体修改
自定义 css并引入,不会的同学参考Hexo 博客添加自定义 css 和 js 文件。
选择自己使用的字体,我用的是思源宋体。由于有些浏览器(如Safari和手机上的浏览器)不支持自己修改字体,这种时候就会使用系统的默认字体!
如何破局?需要在网页的头文件处<head></head>
将需要设置的字体导入!具体的做法是在主题配置文件themes/butterfly/_config.yml
作如下两个地方的修改:(这里是我的个性化修改,看官也可以根据自己的需要来设置,相关的字体在谷歌Fonts API处自行查找即可。)
# Global font settings
# Don't modify the following settings unless you know how they work (非必要不要修改)
font:
global-font-size: 16.5px
code-font-size:
font-family: Times, Noto Serif SC
code-font-family:
# Font settings for the site title and site subtitle
# 左上角網站名字 主頁居中網站名字
blog_title_font:
font_link:
font-family: Ma Shan Zheng
# Inject
# Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag)
# 插入代码到头部 </head> 之前 和 底部 </body> 之前
inject:
head:
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.loli.net/css2?family=Noto+Serif+SC:wght@400;500;700&display=swap" rel="stylesheet"> #思源宋体
- <link href="https://fonts.loli.net/css2?family=Ma+Shan+Zheng:wght@400;500;700&display=swap" rel="stylesheet"> #Mashanzheng
bottom:
- <script type='text/javascript' src='https://ajax.loli.net/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
- <script id="dsq-count-scr" src="//https-www-dhndzwxj-top.disqus.com/count.js" async></script>
高级应用 API
Google Fonts 还开放了一些接口(称为 Developer API),用于获取字体库的信息数据。
比如实时获取字体库实际可用的字体及其相关信息:
https://www.googleapis.com/webfonts/v1/webfonts?key=*YOUR-API-KEY*
这个请求的返回结果是一个 JSON 类型的数据,包括了每种字体的名称,样式种类(比如 regular,italic),版本,修改时间,包含的样式包的请求地址,等等。
请注意,在 URL 里面有一个 key,这个 key 是和您的 web 应用工程相联系的,只有注册过的 web 应用才能成功调用 Developer API。我们必须要在 Google Cloud Console 注册之后,才能获取这个 key。
关于这个 Developer API, 可以参阅这个链接。
参考下面这边文章来修改头文件:Web使用思源字体。前端 CDNJS 库及 Google Fonts、Ajax 和 Gravatar 国内加速服务。
谷歌Fonts API网址:https://fonts.google.com/
解决谷歌字体API加载速度慢的问题:解决引入fonts.googleapis.com/css字体网页响应缓慢问题。
6.10 网页侧边栏卡片
最重要的文件是butterfly/layout/includes/widget/index.pug
。
//- /butterfly/layout/includes/widget/index.pug
#aside-content.aside-content
//- post
if is_post()
if showToc && theme.toc.style_simple
.sticky_layout
include ./card_post_toc.pug
else
!=partial('includes/widget/card_announcement', {}, {cache: true})
!=partial('includes/widget/card_top_self', {}, {cache: true})
.sticky_layout
!=partial('includes/widget/card_back', {}, {cache: true})
if showToc
include ./card_post_toc.pug
!=partial('includes/widget/card_recent_post', {}, {cache: true})
!=partial('includes/widget/card_ad', {}, {cache: true})
else
//- page
!=partial('includes/widget/card_author', {}, {cache: true})
!=partial('includes/widget/card_announcement', {}, {cache: true})
!=partial('includes/widget/card_top_self', {}, {cache: true})
.sticky_layout
!=partial('includes/widget/card_recent_post', {}, {cache: true})
!=partial('includes/widget/card_categories', {}, {cache: true})
!=partial('includes/widget/card_tags', {}, {cache: true})
!=partial('includes/widget/card_webinfo', {}, {cache: true})
!=partial('includes/widget/card_ad', {}, {cache: true})
!=partial('includes/widget/card_newest_comment', {}, {cache: true})
!=partial('includes/widget/card_archives', {}, {cache: true})
!=partial('includes/widget/card_bottom_self', {}, {cache: true})
对于作者卡片butterfly/layout/includes/widget/card_author.pug
,我需要两种形态:一方面是主页上内容要全面(如下面左图),另一方面是文章内它的内容要简略。对于后者,我新建了一个card_back.pug
文件。
///butterfly/layout/includes/widget/card_back.pug
if theme.aside.card_author.enable
.card-widget.card-info
.is-center
.avatar-img
img(src=url_for(theme.avatar.img) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt="avatar")
if theme.aside.card_author.button.enable
a#card-info-btn.button--animated(href=theme.aside.card_author.button.link)
i(class=theme.aside.card_author.button.icon)
span=theme.aside.card_author.button.text
if(theme.social)
.card-info-social-icons.is-center
!=fragment_cache('social', function(){return partial('includes/header/social')})
6.11 时钟
- 安装插件,在博客根目录
[Blogroot]
下打开终端,运行以下指令:
npm install hexo-butterfly-clock-anzhiyu --save
- 添加配置信息,以下为写法示例 在站点配置文件
_config.yml
或者主题配置文件_config.butterfly.yml
中添加
# electric_clock
# see https://anzhiy.cn/posts/fc18.html
electric_clock:
enable: true # 开关
priority: 5 #过滤器优先权
enable_page: all # 应用页面
exclude:
# - /posts/
# - /about/
layout: # 挂载容器类型
type: class
name: sticky_layout
index: 0
loading: https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu/lib/loading.gif #加载动画自定义
clock_css: https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu/lib/clock.min.css
clock_js: https://cdn.cbd.int/hexo-butterfly-clock-anzhiyu/lib/clock.min.js
ip_api: https://widget.qweather.net/simple/static/js/he-simple-common.js?v=2.0
qweather_key: # 和风天气key
gaud_map_key: # 高得地图web服务key
default_rectangle: false # 开启后将一直显示rectangle位置的天气,否则将获取访问者的地理位置与天气
rectangle: 112.982279,28.19409 # 获取访问者位置失败时会显示该位置的天气,同时该位置为开启default_rectangle后的位置
- 参数释义
参数 | 备选值/类型 | 释义 |
---|---|---|
priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为10,选填 |
enable | true/false | 【必选】控制开关 |
enable_page | path | 【可选】填写想要应用的页面,如根目录就填’/’,分类页面就填’/categories/’。若要应用于所有页面,就填all ,默认为all |
exclude | path | 【可选】填写想要屏蔽的页面,可以多个。写法见示例。原理是将屏蔽项的内容逐个放到当前路径去匹配,若当前路径包含任一屏蔽项,则不会挂载。 |
layout.type | id/class | 【可选】挂载容器类型,填写id或class,不填则默认为id |
layout.name | text | 【必选】挂载容器名称 |
layout.index | 0和正整数 | 【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位 |
loading | URL | 【可选】电子钟加载动画的图片 |
clock_css | URL | 【可选】电子钟样式CDN资源 |
clock_js | URL | 【可选】电子钟执行脚本CDN资源 |
ip_api | URL | 【可选】获取时钟IP的API |
配置文件
6.n 主题配置文件
# Main menu navigation (導航目錄)
# see https://butterfly.js.org/posts/4aa8abbe/#導航菜單
# --------------------------------------
menu:
主页: / || fas fa-home
关于: /about/ || fas fa-heart
结构||fa fa-heartbeat:
目录: /categories/ || fas fa-folder-open
归档: /archives/ || fas fa-archive
标签: /tags/ || fas fa-tags
# Code Blocks (代碼相關)
# --------------------------------------
highlight_theme: light # darker / pale night / light / ocean / mac / mac light / false
highlight_copy: true # copy button
highlight_lang: true # show the code language
highlight_shrink: false # true: shrink the code blocks / false: expand the code blocks | none: expand code blocks and hide the button
highlight_height_limit: false # unit: px
code_word_wrap: false
# copy settings
# copyright: Add the copyright information after copied content (複製的內容後面加上版權信息)
copy:
enable: true
copyright:
enable: false
limit_count: 0
# social settings (社交圖標設置)
# formal:
# icon: link || the description
social:
# fab fa-github: https://github.com/xxxxx || Github
# fas fa-envelope: mailto:xxxxxx@gmail.com || Email
# search (搜索)
# see https://butterfly.js.org/posts/ceeb73f/#搜索系統
# --------------------------------------
# Algolia search
algolia_search:
enable: false
hits:
per_page: 6
# Local search
local_search:
enable: true
preload: true #預加載,開啟後,進入網頁後會自動加載搜索文件。關閉時,只有點擊搜索按鈕後,才會加載搜索文件
CDN:
# Math (數學)
# --------------------------------------
# About the per_page
# if you set it to true, it will load mathjax/katex script in each page (true 表示每一頁都加載js)
# if you set it to false, it will load mathjax/katex script according to your setting (add the 'mathjax: true' in page's front-matter)
# (false 需要時加載,須在使用的 Markdown Front-matter 加上 mathjax: true)
# MathJax
mathjax:
enable: true
per_page: false
# KaTeX
katex:
enable: false
per_page: false
hide_scrollbar: true
# Image (圖片設置)
# --------------------------------------
# Favicon(網站圖標)
favicon: /private_img/favicon.png
# Avatar (頭像)
avatar:
img: /private_img/yuyingnan.jpg #https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png
effect: false
# Disable all banner image
disable_top_img: false
# The banner image of home page
index_img: /private_img/bg0.png
# If the banner of page not setting, it will show the top_img
default_top_img: /private_img/bg1.png
# The banner image of archive page
archive_img:
# If the banner of tag page not setting, it will show the top_img
# note: tag page, not tags page (子標籤頁面的 top_img)
tag_img:
# The banner image of tag page
# format:
# - tag name: xxxxx
tag_per_img:
# If the banner of category page not setting, it will show the top_img
# note: category page, not categories page (子分類頁面的 top_img)
category_img:
# The banner image of category page
# format:
# - category name: xxxxx
category_per_img: /private_img/banner2.gif
cover:
# display the cover or not (是否顯示文章封面)
index_enable: false
aside_enable: false
archives_enable: false
# the position of cover in home page (封面顯示的位置)
# left/right/both
position: both
# When cover is not set, the default cover is displayed (當沒有設置cover時,默認的封面顯示)
default_cover: /private_img/banner2.gif
# - https://i.loli.net/2020/05/01/gkihqEjXxJ5UZ1C.jpg
# Replace Broken Images (替換無法顯示的圖片)
error_img:
flink: /private_img/404.gif
post_page: /private_img/banner.gif
# A simple 404 page
error_404:
enable: true
subtitle: '无法打开该网页'
background: https://i.loli.net/2020/05/19/aKOcLiyPl2JQdFD.png
post_meta:
page: # Home Page
date_type: both # created or updated or both 主頁文章日期是創建日或者更新日或都顯示
date_format: date # date/relative 顯示日期還是相對日期
categories: true # true or false 主頁是否顯示分類
tags: false # true or false 主頁是否顯示標籤
label: true # true or false 顯示描述性文字
post:
date_type: both # created or updated or both 文章頁日期是創建日或者更新日或都顯示
date_format: date # date/relative 顯示日期還是相對日期
categories: true # true or false 文章頁是否顯示分類
tags: true # true or false 文章頁是否顯示標籤
label: true # true or false 顯示描述性文字
# wordcount (字數統計)
# see https://butterfly.js.org/posts/ceeb73f/#字數統計
wordcount:
enable: true
post_wordcount: true
min2read: true
total_wordcount: true
# Display the article introduction on homepage
# 1: description
# 2: both (if the description exists, it will show description, or show the auto_excerpt)
# 3: auto_excerpt (default)
# false: do not show the article introduction
index_post_content:
method: 3
length: 500 # if you set method to 2 or 3, the length need to config
# anchor
anchor:
button:
enable: false
always_show: false
icon: # the unicode value of Font Awesome icon, such as '\3423'
auto_update: false # when you scroll in post, the URL will update according to header id.
# Post
# --------------------------------------
# toc (目錄)
toc:
post: true
page: false
number: false #自动编号
expand: true
style_simple: false # for post
post_copyright:
enable: false
decode: false
author_href:
license: CC BY-NC-SA 4.0
license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/
# Sponsor/reward
reward:
enable: true
QR_code:
- img: /private_img/wechat.png
link:
text: 微信
- img: /private_img/alipay.jpg
link:
text: 支付宝
# Post edit
# Easily browse and edit blog source code online.
post_edit:
enable: false
# url: https://github.com/user-name/repo-name/edit/branch-name/subdirectory-name/
# For example: https://github.com/jerryc127/butterfly.js.org/edit/main/source/
url: https://github.com/dhndzwxj/hexo-backup/edit/master/source/
# Related Articles
related_post:
enable: false
limit: 6 # Number of posts displayed
date_type: created # or created or updated 文章日期顯示創建日或者更新日
# figcaption (圖片描述文字)
photofigcaption: false
# post_pagination (分頁)
# value: 1 || 2 || false
# 1: The 'next post' will link to old post
# 2: The 'next post' will link to new post
# false: disable pagination
post_pagination: 1
# Displays outdated notice for a post (文章過期提醒)
noticeOutdate:
enable: true
style: flat # style: simple/flat
limit_day: 30 # When will it be shown
position: top # position: top/bottom
message_prev: 本文上次更新距离今天已经过去
message_next: 天, 文中内容可能已经过时,望周知。
# Share System (分享功能)
# --------------------------------------
# AddThis
# https://www.addthis.com/
addThis:
enable: false
pubid:
# Share.js
# https://github.com/overtrue/share.js
sharejs:
enable: true
sites: facebook,twitter,wechat,weibo,qq
# AddToAny
# https://www.addtoany.com/
addtoany:
enable: false
item: facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link
# Comments System
# --------------------------------------
comments:
# Up to two comments system, the first will be shown as default
# Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo
use: Disqus
text: true # Display the comment name next to the button
# lazyload: The comment system will be load when comment element enters the browser's viewport.
# If you set it to true, the comment count will be invalid
lazyload: true
count: true # Display comment count in top_img
card_post_count: false # Display comment count in Home Page
# disqus
# https://disqus.com/
disqus:
shortname: https-www-dhndzwxj-top #disqus_ZdiIR8wtv4
apikey: # For newest comments widget
# Alternative Disqus - Render comments with Disqus API
# DisqusJS 評論系統,可以實現在網路審查地區載入 Disqus 評論列表,兼容原版
# https://github.com/SukkaW/DisqusJS
disqusjs:
shortname:
apikey:
option:
# livere (來必力)
# https://www.livere.com/
livere:
uid:
# gitalk
# https://github.com/gitalk/gitalk
gitalk:
client_id:
client_secret:
repo:
owner:
admin:
option:
# valine
# https://valine.js.org
valine:
appId: # leancloud application app id
appKey: # leancloud application app key
avatar: monsterid # gravatar style https://valine.js.org/#/avatar
serverURLs: # This configuration is suitable for domestic custom domain name users, overseas version will be automatically detected (no need to manually fill in)
bg: # valine background
visitor: false
option:
# waline - A simple comment system with backend support fork from Valine
# https://waline.js.org/
waline:
serverURL: # Waline server address url
bg: # waline background
pageview: false
option:
# utterances
# https://utteranc.es/
utterances:
repo:
# Issue Mapping: pathname/url/title/og:title
issue_term: pathname
# Theme: github-light/github-dark/github-dark-orange/icy-dark/dark-blue/photon-dark
light_theme: github-light
dark_theme: photon-dark
# Facebook Comments Plugin
# https://developers.facebook.com/docs/plugins/comments/
facebook_comments:
app_id:
user_id: # optional
pageSize: 10 # The number of comments to show
order_by: social # social/time/reverse_time
lang: zh_TW # Language en_US/zh_CN/zh_TW and so on
# Twikoo
# https://github.com/imaegoo/twikoo
twikoo:
envId:
region:
visitor: false
option:
# Giscus
# https://giscus.app/
giscus:
repo:
repo_id:
category_id:
theme:
light: light
dark: dark
option:
# Remark42
# https://remark42.com/docs/configuration/frontend/
remark42:
host: # Your Host URL
siteId: # Your Site ID
option:
# Artalk
# https://artalk.js.org/guide/frontend/config.html
artalk:
server:
site:
visitor: false
option:
# Chat Services
# --------------------------------------
# Chat Button [recommend]
# It will create a button in the bottom right corner of website, and hide the origin button
chat_btn: false
# The origin chat button is displayed when scrolling up, and the button is hidden when scrolling down
chat_hide_show: false
# chatra
# https://chatra.io/
chatra:
enable: false
id:
# tidio
# https://www.tidio.com/
tidio:
enable: false
public_key:
# daovoice
# http://daovoice.io/
daovoice:
enable: false
app_id:
# gitter
# https://gitter.im/
gitter:
enable: false
room:
# crisp
# https://crisp.chat/en/
crisp:
enable: false
website_id:
# messenger
# https://developers.facebook.com/docs/messenger-platform/discovery/facebook-chat-plugin/
messenger:
enable: false
pageID:
lang: zh_TW # Language en_US/zh_CN/zh_TW and so on
# Footer Settings
# --------------------------------------
footer:
owner:
enable: true
since: 2020
custom_text: 欢迎参观我的<a href='https://github.com/dhndzwxj' target='_blank'>github</a>个人主页!
copyright: true # Copyright of theme and framework
# Analysis
# --------------------------------------
# Baidu Analytics
# https://tongji.baidu.com/web/welcome/login
baidu_analytics:
# Google Analytics
# https://analytics.google.com/analytics/web/
google_analytics:
# CNZZ Analytics
# https://www.umeng.com/
cnzz_analytics:
# Cloudflare Analytics
# https://www.cloudflare.com/zh-tw/web-analytics/
cloudflare_analytics:
# Microsoft Clarity
# https://clarity.microsoft.com/
microsoft_clarity:
# Advertisement
# --------------------------------------
# Google Adsense (谷歌廣告)
google_adsense:
enable: false
auto_ads: true
js: https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js
client:
enable_page_level_ads: true
# Insert ads manually (手動插入廣告)
# ad:
# index:
# aside:
# post:
# Verification (站長驗證)
# --------------------------------------
site_verification:
# - name: google-site-verification
# content: xxxxxx
# - name: baidu-site-verification
# content: xxxxxxx
# Beautify/Effect (美化/效果)
# --------------------------------------
# Theme color for customize
# Notice: color value must in double quotes like "#000" or may cause error!
theme_color:
enable: true
main: '#E68258' #'#00B300' #'#1A1A1A' #滑动条的颜色,背景以上的颜色
paginator: "#e68258" #底部网页分页数字的颜色
button_hover: "#FF9966" #鼠标停留在button上颜色渐变的效果;选择了比主题色更浅一号的橙色
text_selection: '#9F6506' #'#000066' #"#0000CC" #选中文字后的颜色
link_color: "#b86ed0" #网页链接的颜色
meta_color: "#858585"
hr_color: "#A4D8FA"
code_foreground: "#F47466"
code_background: "#FFFF00"
toc_color: '#E68258' #"#e68258" #"#00c4b6"
blockquote_padding_color: "#e68258" #引用部分左边长条边框颜色
blockquote_background_color: "#e68258" #引用部分的背景颜色
# The top_img settings of home page
# default: top img - full screen, site info - middle (默認top_img全屏,site_info在中間)
# The position of site info, eg: 300px/300em/300rem/10% (主頁標題距離頂部距離)
index_site_info_top:
# The height of top_img, eg: 300px/300em/300rem (主頁top_img高度)
index_top_img_height:
# The user interface setting of category and tag page (category和tag頁的UI設置)
# index - same as Homepage UI (index 值代表 UI將與首頁的UI一樣)
# default - same as archives UI 默認跟archives頁面UI一樣
category_ui: # 留空或 index
tag_ui: # 留空或 index
# Website Background (設置網站背景)
# can set it to color or image (可設置圖片 或者 顔色)
# The formal of image: url(http://xxxxxx.com/xxx.jpg)
background:
# Footer Background
footer_bg: true
# the position of bottom right button/default unit: px (右下角按鈕距離底部的距離/默認單位為px)
rightside-bottom:
# Enter transitions (開啓網頁進入效果)
enter_transitions: true
# Background effects (背景特效)
# --------------------------------------
# canvas_ribbon (靜止彩帶背景)
# See: https://github.com/hustcc/ribbon.js
canvas_ribbon:
enable: false
size: 150
alpha: 0.6
zIndex: -1
click_to_change: false
mobile: false
# Fluttering Ribbon (動態彩帶)
canvas_fluttering_ribbon:
enable: true
mobile: true
# canvas_nest
# https://github.com/hustcc/canvas-nest.js
canvas_nest:
enable: false
color: '0,0,255' #color of lines, default: '0,0,0'; RGB values: (R,G,B).(note: use ',' to separate.)
opacity: 0.7 # the opacity of line (0~1), default: 0.5.
zIndex: -1 # z-index property of the background, default: -1.
count: 99 # the number of lines, default: 99.
mobile: false
# Typewriter Effect (打字效果)
# https://github.com/disjukr/activate-power-mode
activate_power_mode:
enable: false
colorful: true # open particle animation (冒光特效)
shake: true # open shake (抖動特效)
mobile: false
# Mouse click effects: fireworks (鼠標點擊效果: 煙火特效)
fireworks:
enable: false
zIndex: 9999 # -1 or 9999
mobile: false
# Mouse click effects: Heart symbol (鼠標點擊效果: 愛心)
click_heart:
enable: false
mobile: false
# Mouse click effects: words (鼠標點擊效果: 文字)
ClickShowText:
enable: false
text:
# - I
# - LOVE
# - YOU
fontSize: 16.5px
random: false
mobile: false
# Default display mode (網站默認的顯示模式)
# light (default) / dark
display_mode: light
# Beautify (美化頁面顯示)
beautify:
enable: false
field: post # site/post
title-prefix-icon: # '\f0c1'
title-prefix-icon-color: # '#F47466'
# Global font settings
# Don't modify the following settings unless you know how they work (非必要不要修改)
font:
global-font-size: 16.5px
code-font-size:
font-family: Times, Noto Serif SC
code-font-family:
# Font settings for the site title and site subtitle
# 左上角網站名字 主頁居中網站名字
blog_title_font:
font_link:
font-family: Ma Shan Zheng
# The setting of divider icon (水平分隔線圖標設置)
hr_icon:
enable: true
icon: # the unicode value of Font Awesome icon, such as '\3423'
icon-top:
# the subtitle on homepage (主頁subtitle)
subtitle:
enable: true
# Typewriter Effect (打字效果)
effect: true
# Effect Speed Options (打字效果速度參數)
startDelay: 300 # time before typing starts in milliseconds
typeSpeed: 150 # type speed in milliseconds
backSpeed: 50 # backspacing speed in milliseconds
# loop (循環打字)
loop: true
# source 調用第三方服務
# source: false 關閉調用
# source: 1 調用一言網的一句話(簡體) https://hitokoto.cn/
# source: 2 調用一句網(簡體) http://yijuzhan.com/
# source: 3 調用今日詩詞(簡體) https://www.jinrishici.com/
# subtitle 會先顯示 source , 再顯示 sub 的內容
source: false
# 如果關閉打字效果,subtitle 只會顯示 sub 的第一行文字
sub:
- 随手写一写,揭晓这世界
# Loading Animation (加載動畫)
preloader:
enable: false
# source
# 1. fullpage-loading
# 2. pace (progress bar)
source: 1
# pace theme (see https://codebyzach.github.io/pace/)
pace_css_url:
# aside (側邊欄)
# --------------------------------------
aside:
enable: true
hide: false
button: true
mobile: true # display on mobile
position: left # left or right
display:
archive: true
tag: true
category: true
card_author:
enable: true
description:
button:
enable: true
# icon: fab fa-github
text: 回到首页
link: /
card_announcement:
enable: false
content: 随手写一些,揭晓这世界
card_recent_post:
enable: true
limit: 5 # if set 0 will show all
sort: updated # date or updated
sort_order: 'updated' # Don't modify the setting unless you know how it works
card_categories:
enable: true
limit: 0 # if set 0 will show all
expand: false # none/true/false
sort_order: # Don't modify the setting unless you know how it works
card_tags:
enable: false
limit: 0 # if set 0 will show all
color: false
sort_order: # Don't modify the setting unless you know how it works
card_archives:
enable: false
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
# busuanzi count for PV / UV in site
# 訪問人數
busuanzi:
site_uv: true
site_pv: true
page_pv: true
# Time difference between publish date and now (網頁運行時間)
# Formal: Month/Day/Year Time or Year/Month/Day Time
runtimeshow:
enable: true
publish_date: 8/21/2020 00:00:00 #发布时间
# Aside widget - Newest Comments
newest_comments:
enable: true
sort_order: # Don't modify the setting unless you know how it works
limit: 6
storage: 10 # unit: mins, save data to localStorage
avatar: true
# Bottom right button (右下角按鈕)
# --------------------------------------
# Conversion between Traditional and Simplified Chinese (簡繁轉換)
translate:
enable: true
# The text of a button
default: 繁
# the language of website (1 - Traditional Chinese/ 2 - Simplified Chinese)
defaultEncoding: 2
# Time delay
translateDelay: 0
# The text of the button when the language is Simplified Chinese
msgToTraditionalChinese: '繁'
# The text of the button when the language is Traditional Chinese
msgToSimplifiedChinese: '簡'
# Read Mode (閲讀模式)
readmode: true
# dark mode
darkmode:
enable: true
# Toggle Button to switch dark/light mode
button: true
# Switch dark/light mode automatically (自動切換 dark mode和 light mode)
# autoChangeMode: 1 Following System Settings, if the system doesn't support dark mode, it will switch dark mode between 6 pm to 6 am
# autoChangeMode: 2 Switch dark mode between 6 pm to 6 am
# autoChangeMode: false
autoChangeMode: false
# Don't modify the following settings unless you know how they work (非必要請不要修改 )
# Choose: readmode,translate,darkmode,hideAside,toc,chat,comment
# Don't repeat 不要重複
rightside_item_order:
enable: false
hide: # readmode,translate,darkmode,hideAside
show: # toc,chat,comment
# Lightbox (圖片大圖查看模式)
# --------------------------------------
# You can only choose one, or neither (只能選擇一個 或者 兩個都不選)
# medium-zoom
# https://github.com/francoischalifour/medium-zoom
medium_zoom: false
# fancybox
# http://fancyapps.com/fancybox/3/
fancybox: true
# Tag Plugins settings (標籤外掛)
# --------------------------------------
# mermaid
# see https://github.com/mermaid-js/mermaid
mermaid:
enable: true
# built-in themes: default/forest/dark/neutral
theme:
light: default
dark: dark
# Note (Bootstrap Callout)
note:
# Note tag style values:
# - simple bs-callout old alert style. Default.
# - modern bs-callout new (v2-v3) alert style.
# - flat flat callout style with background, like on Mozilla or StackOverflow.
# - disabled disable all CSS styles import of note tag.
style: flat
icons: true
border_radius: 3
# Offset lighter of background in % for modern and flat styles (modern: -12 | 12; flat: -18 | 6).
# Offset also applied to label tag variables. This option can work with disabled note tag.
light_bg_offset: 0
# other
# --------------------------------------
# Pjax
# It may contain bugs and unstable, give feedback when you find the bugs.
# https://github.com/MoOx/pjax
pjax:
enable: false
exclude:
# - xxxx
# - xxxx
# Inject the css and script (aplayer/meting)
aplayerInject:
enable: false
per_page: true
# Snackbar (Toast Notification 彈窗)
# https://github.com/polonel/SnackBar
# position 彈窗位置
# 可選 top-left / top-center / top-right / bottom-left / bottom-center / bottom-right
snackbar:
enable: false
position: bottom-left
bg_light: '#49b1f5' # The background color of Toast Notification in light mode
bg_dark: '#1f1f1f' # The background color of Toast Notification in dark mode
# https://instant.page/
# prefetch (預加載)
instantpage: false
# https://github.com/vinta/pangu.js
# Insert a space between Chinese character and English character (中英文之間添加空格)
pangu:
enable: false
field: site # site/post
# Lazyload (圖片懶加載)
# https://github.com/verlok/vanilla-lazyload
lazyload:
enable: false
field: site # site/post
placeholder:
blur: false
# PWA
# See https://github.com/JLHwung/hexo-offline
# ---------------
# pwa:
# enable: false
# manifest: /pwa/manifest.json
# apple_touch_icon: /pwa/apple-touch-icon.png
# favicon_32_32: /pwa/32.png
# favicon_16_16: /pwa/16.png
# mask_icon: /pwa/safari-pinned-tab.svg
# Open graph meta tags
# https://developers.facebook.com/docs/sharing/webmasters/
Open_Graph_meta:
enable: true
option:
# twitter_card:
# twitter_image:
# twitter_id:
# twitter_site:
# google_plus:
# fb_admins:
# fb_app_id:
# Add the vendor prefixes to ensure compatibility
css_prefix: true
# Inject
# Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag)
# 插入代码到头部 </head> 之前 和 底部 </body> 之前
inject:
head:
- <link rel="preconnect" href="https://fonts.googleapis.com">
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
- <link href="https://fonts.loli.net/css2?family=Noto+Serif+SC:wght@400;500;700&display=swap" rel="stylesheet"> #思源宋体
- <link href="https://fonts.loli.net/css2?family=Ma+Shan+Zheng:wght@400;500;700&display=swap" rel="stylesheet"> #Mashanzheng
# - <link rel="stylesheet" href="/xxx.css">
bottom:
- <script type='text/javascript' src='https://ajax.loli.net/ajax/libs/jquery/3.2.1/jquery.min.js'></script>
- <script id="dsq-count-scr" src="//https-www-dhndzwxj-top.disqus.com/count.js" async></script>
# - <script src="xxxx"></script>
# CDN
# Don't modify the following settings unless you know how they work
# 非必要請不要修改
CDN:
# The CDN provider of internal scripts (主題內部 js 的 cdn 配置)
# option: local/jsdelivr/unpkg/cdnjs/custom
# Dev version can only choose. ( dev版的主題只能設置為 local )
internal_provider: local
# The CDN provider of third party scripts (第三方 js 的 cdn 配置)
# option: local/jsdelivr/unpkg/cdnjs/custom
# when set it to local, you need to install hexo-butterfly-extjs
third_party_provider: jsdelivr
# Add version number to CDN, true or false
version: false
# Custom format
# For example: https://cdn.staticfile.org/${cdnjs_name}/${version}/${min_cdnjs_file}
custom_format:
option:
# main_css:
# main:
# utils:
# translate:
# local_search:
# algolia_js:
# algolia_search_v4:
# instantsearch_v4:
# pjax:
# gitalk:
# gitalk_css:
# blueimp_md5:
# valine:
# disqusjs:
# disqusjs_css:
# twikoo:
# waline_js:
# waline_css:
# sharejs:
# sharejs_css:
# mathjax:
# katex:
# katex_copytex:
# mermaid:
# canvas_ribbon:
# canvas_fluttering_ribbon:
# canvas_nest:
# lazyload:
# instantpage:
# typed:
# pangu:
# fancybox_css_v4:
# fancybox_v4:
# medium_zoom:
# snackbar_css:
# snackbar:
# activate_power_mode:
# fireworks:
# click_heart:
# ClickShowText:
# fontawesomeV6:
# flickr_justified_gallery_js:
# flickr_justified_gallery_css:
# aplayer_css:
# aplayer_js:
# meting_js:
# prismjs_js:
# prismjs_lineNumber_js:
# prismjs_autoloader:
# artalk_js:
# artalk_css:
7 Git 教程及博客备份
这一步是要把我们本地的所有个人文件在网上进行备份——这很有必要,因为保不齐哪一天我们手残把电脑格式化了。不过想实现备份功能,要求我们对Git的一些内容有必要的了解,否则做起备份来往往会盲人摸象,反而自己把自己的文件用命令行代码清空了(这里有我的故事,有酒么?)。
Git教程:点这里。
7.1 生成 SSH Key
本例以 Github 为例作为远程仓库,如果你没有 Github 可以在官网 https://github.com/注册。由于你的本地 Git 仓库和 GitHub 仓库之间的传输是通过SSH加密的,所以我们需要配置验证信息:
使用以下命令生成 SSH Key:
ssh-keygen -t rsa -C "youremail@example.com"
ssh-keygen -t rsa -C "dhndzwxj@ruc.edu.cn"
ssh-keygen -t ed25519 -C "dhndzwxj@ruc.edu.cn"
查看当前的远程库:(执行时加上-v
参数,你还可以看到每个别名的实际链接地址。)
$ git remote
backup
$ git remote -v
backup git@github.com:dhndzwxj/hexo-backup.git (fetch)
backup git@github.com:dhndzwxj/hexo-backup.git (push)
7.2 查看远程分支的更新状态
git remote show [仓库别名]
* remote backup
Fetch URL: git@github.com:dhndzwxj/hexo-backup.git
Push URL: git@github.com:dhndzwxj/hexo-backup.git
HEAD branch: master
Remote branch:
master tracked
Local ref configured for 'git push':
master pushes to master (up to date)
Fetch URL和Push URL表示fetch
和push
的链接,master tracked
表示本地和远程建立了连接。up to date
表示本地和远程同步状态,local out of date
表示本地落后于远程了。
7.3 博客备份
注意每次备份前,都需要commit一下~(回忆前面讲的内容,如果不commit就只是在本地有缓存,没有上传到缓存区)然后下面一键三连:
➜ dlog git:(master) ✗ git add .
➜ dlog git:(master) ✗ git commit -m '211023版本提交'
➜ dlog git:(master) ✗ git push backup master
后面的backup
是远程仓库名,master
是backup这个远程仓库的默认分支名,这些名字都可以自己个性化命名,它们都不是代码中的关键字!
不过在提交的时候,往往出现主题文件无法commit的情况!为什么呢?因为主题文件也是从仓库里拉取下来的,它被关联到了作者的git仓库,所以提交不上去!
怎么解决呢?
①从暂存区删除该文件夹
git rm --cache themes/butterfly451
②把 themes/主题名/.git
文件夹到放到别的位置,比方说桌面。记得把 themes/主题名/.gitignore
里的 _config
去掉
③git status
查看当前状态
④直接按步骤提交就行了
git add themes/butterfly470/
让后三连即可。
7.3.a 多设备备份不同步的问题
有时候可能忘记先从仓库拉代码(git pull),直接在不是新版本的文件上进行编辑,这样在git push代码的时候会遭遇reject 的情况,也就是版本冲突了。
建议的解决方法是,第一步先把此次修改的文件备份到一个新的文件夹中。第二步,把版本倒退。先看版本号
git log

然后倒退回没有争议的版本:【HEAD->master是本地的版本,backup/master是github仓库中的版本,此处的例子里二者是同步的,出问题的时候是不同步的。commit后面一大长串字母是版本号。按键盘上的Q键可退出此版本号界面。】
git reset --hard [版本号]
第三步,按照备份到其他文件夹的文件,把老版本的对应文件进行修改。
标签
7.4 多设备拉取、备份实战
7.4.1 往返于多个设备之间:流程式
在家里上传代码
git remote add [远程仓库别名] [远程仓库地址]
git push [远程仓库别名] [分支]
到公司新电脑上第一次获取代码
git clone [远程仓库地址]
# 切换分支
git checkout [分支]
在Windows下,如果仓库中的一个pdf文件的路径太长,导致clone到本地之后无法checkout,解决方法是:
git reset
git config core.protectNTFS false
git checkout
在公司进行开发(约定在dev分支进行开发)
#切换到dev分支进行开发
git checkout dev
#把master分支合并到dev【仅一次】
git merge master
#修改代码
#提交代码
git add .
git commit -m '记录信息'
git push [远程仓库别名] dev
回到家中继续写代码
#切换到dev分支进行开发
git checkout dev
#拉代码
git pull [远程仓库别名] dev
#继续开发
#提交代码
git add .
git commit -m '记录信息'
git push [远程仓库别名] dev
……以上流程循环往复。
开发完毕,要上线
# 把dev分支的代码合并到master分支上
git checkout master
git merge dev
git push [远程仓库别名] master
# 把dev分支也推送到远程
git checkout dev
git merge master
git push [远程仓库别名] dev
7.4.2 往返于多个设备之间:分布式(多设备共同开发、合并)
在设备1和设备2共同开发,将代码合并的过程中可能会发生冲突。
怎么办?手动解决!
7.5 上传超过100M的文件
就需要借助Git LFS。首先下载git-lfs(https://github.com/git-lfs/git-lfs),安装好后进入本地仓库目录,执行下面的命令。
git lfs track "file"
file是需要上传的大文件。执行完命令后会发现目录下生成了一个".gitattributes"文件,文件内记录了我们要上传文件的信息。只有先把".gitattributes"传上去,才可以上传大文件。
git add .gitattributes
git commit -m "submit file"
git push -u origin master
上传完毕后,开始上传大文件。
git add file
git commit -m "add file"
git push -u origin master
如果发现自己空间不足,可以删去一些大文件或者购买更多的空间。
如果在上传过程中出现如下报错:
batch response: Git LFS is disabled for this repository.
Uploading LFS objects: 0% (0/1), 0 B | 0 B/s, done
就说明你的账号被冻结了,需要在GitHub后台提交解封申请。https://support.github.com/contact
工作日一般几个小时就会帮你把账号解封,解封后就可以继续上传大文件啦~
8. 将博客站点托管到Vercel上!
为什么使用Vercel托管网站?就我的个人使用体验而言,优点有以下几点:
- 服务器在国内(据我查到的资料是在中国台湾),网页加载速度比Github Page快太多;
- 支持绑定Github账号,可以直接导入Github仓库
- 我看到的资料里,在Github上
hexo d
后,Vercel这边也会自动更新,比较省心。
下面看看怎么做吧~首先上Vercel官网,注册一个自己的账号(点击网页右上角的“Sign Up”),然后(如下图)用Github账号就可以!

登陆成功后,我们下一步是在Vercel上新建一个项目(Project,如下图左),导入一个Git仓库(如下图右),点击Import就可以了
![]() |
![]() |
导入后便进入下面这个页面(下图左)。这里的意思是我们要不要建立一个开发团队,由于这项服务是收费的,所以我们当然就不需要啦,点击“Skip”即可跳过此步。然后进入下一个页面(下图右),直接点击“Deploy”,就可以将我们的Github项目部署到Vercel上面啦!做到这里,基本上就大功告成了!(下图右里面有一个选项叫做“PROJECT NAME”,也就是项目名称的意思,这个是能自己取名字的哦!)
![]() |
![]() |
最后一步是要知道,我们生成的Vercel网页的网址是什么?
如下图1,先在自己的主页上打开自己刚刚部署好的项目,然后点击图2的“View Domains”,即查看自己的域名。在图3中点击“Edit”,即对自己的域名进行编辑,最后在图4红框处进行改名(后面的".vercel.app"不能改,只能改前面的内容),点击Save进行保存。
![]() 图1 |
![]() 图2 |
![]() 图3 |
![]() 图4 |
9.Vercel绑定域名
1- 在vercel的主界面,点击Setting。
2-点击domains设置
3-在域名添加界面,填入信息,点击add按钮

10.将Github绑定自己的域名
1-先买一个域名。
2-在github仓库里添加CNAME文件,并在文件中填写绑定的域名。

文件内容如下:(文件里填写要绑定的域名且不要包含Http://和www) 说白了就是你购买的域名,前面不要 http
和 www
进入设置,找到 Custom domain添加域名后保存即可(添加CNAME文件并在文件中填写绑定的域名后应该会自动保存,看看有没有自动保存)
3-ping你的http://github.io域名,得到一个IP;
4-然后在域名中添加两条A记录,使用刚刚得到的IP。一个主机记录为www
,一个主机记录为@
。
5-对DNS的配置不是立即生效的,过10分钟再去访问你的域名看看有没有配置成功 : )
N-1. 博客站点根目录文件配置
对博客站点根目录进行个性化配置。这是我的个人设置,大家可以根据自己的需要更改。
博客站点根目录文件配置
# Hexo Configuration
## Docs: https://hexo.io/docs/configuration.html
## Source: https://github.com/hexojs/hexo/
# Site
title: 小荷才露尖尖角
subtitle: 蜻蜓
description: '随手写一写,揭晓这世界' #网站描述
keywords: 马克思主义政治经济学
author: 揭晓 #你的名字
language: zh-CN #网站使用的语言
timezone: Asia/Shanghai #网站时区
# URL
## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
url: https://dhndzwxj.github.io # http://example.com
root: /
permalink: :abbrlink.html #前面绝对不能加'/'
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
#abbrlink配置
abbrlink:
alg: crc32 # 算法:crc16(default) and crc32
rep: dec # 进制:dec(default) and hex
# Directory 目录配置
source_dir: source #资源文件夹,这个文件夹用来存放内容
public_dir: public #公共文件夹,这个文件夹用于存放生成的站点文件
tag_dir: tags #标签文件夹
archive_dir: archives #归档文件夹
category_dir: categories #分类文件夹
code_dir: downloads/code #Include code 文件夹
i18n_dir: :lang #国际化文件夹
skip_render: #跳过指定文件的渲染,您可使用 glob 来配置路径
# Writing 写作配置
new_post_name: :title.md #新文章的文件名称
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 #把文件名称转换为 (1) 小写或 (2) 大写
render_drafts: false #显示草稿
post_asset_folder: true #是否启动资源文件夹
relative_link: true #把链接改为与根目录的相对位址
future: true
highlight:
enable: false
line_number: false #决定了代码块左侧是否有行数
auto_detect: false
tab_replace: ''
wrap: true
hljs: true
prismjs:
enable: true
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: 10
order_by: -date
# 归档页面
archive_generator:
per_page: 50
yearly: true
monthly: true
#效果,首页每间隔10篇文章就分页,归档页每间隔50篇文章才分页。
# 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
## http://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:
#搜索功能
search:
path: search.xml
field: post
content: true
format: html
# #搜索功能2
# algolia:
# applicationID: 'applicationID'
# apiKey: 'apiKey'
# indexName: '...'
# # 文章加密
encrypt: # hexo-blog-encrypt
abstract: 有东西被加密了, 请输入密码查看.
message: 您好, 这里需要密码.
tags:
- {name: tagName, password: 密码A}
- {name: tagName, password: 密码B}
wrong_pass_message: 抱歉, 这个密码看着不太对, 请再试试.
wrong_hash_message: 抱歉, 这个文章不能被校验, 不过您还是能看看解密后的内容.
# # mermaid chart
# mermaid: ## mermaid url https://github.com/knsv/mermaid
# enable: true # default true
# version: "9.2.2" # default v7.1.2
# options: # find more api options from https://github.com/knsv/mermaid/blob/master/src/mermaidAPI.js
# #startOnload: true // default true
# # math
# math:
# engine: 'mathjax'
# mathjax:
# _config.yml
math:
katex:
css: 'https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css'
options:
throwOnError: false
mathjax:
css: 'https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css'
options:
conversion:
display: false
tex:
svg:
# electric_clock
# see https://akilar.top/posts/4e39cf4a/
electric_clock:
enable: true # 开关
priority: 5 #过滤器优先权
enable_page: all # 应用页面
exclude:
- /posts/
- /about/
layout: # 挂载容器类型
type: class
name: sticky_layout
index: 0
loading: https://npm.elemecdn.com/hexo-butterfly-clock/lib/loading.gif #加载动画自定义
clock_css: https://npm.elemecdn.com/hexo-butterfly-clock/lib/clock.min.css #电子钟样式CDN资源
clock_js: https://npm.elemecdn.com/hexo-butterfly-clock/lib/clock.min.js #电子钟执行脚本CDN资源
ip_api: https://pv.sohu.com/cityjson?ie=utf-8 #获取时钟IP的API
# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
type: git
# repo: git@gitee.com:dhndzwxj/dhndzwxj.gitee.io.git
repo: git@github.com:dhndzwxj/dhndzwxj.github.io.git
branch: master
# Extensions
## Plugins: https://hexo.io/plugins/
plugins:
# - hexo-footnote
## Themes: https://hexo.io/themes/
theme: butterfly451
# theme: lanscape
N. 拓展知识
1. CSS预处理器
CSS指的是层叠样式表(Cascading Style Sheets),描述了如何在屏幕、纸张或其他媒体上显示 HTML 元素。可以同时控制多张网页的布局,外部样式存储在css文件中。一句话,css的目的是对html中每一个组块(如<a>
,<p>
,<i>
,<h1>
)进行个性化定制!
选择器
CSS 规则集(rule-set)由选择器和声明块组成:
- 选择器指向您需要设置样式的 HTML 元素。(css是对html中各种元素进行个性化定制的服务!)
- 声明块包含一条或多条用分号分隔的声明。每条声明都包含一个 CSS 属性名称和一个值,以冒号分隔。多条 CSS 声明用分号分隔,声明块用花括号括起来。

例子:
p {
color: red;
text-align: center;
}
例子解释
p
是CSS
中的选择器(它指向要设置样式的HTML
元素:<p>
)。color
是属性,red
是属性值text-align
是属性,center
是属性值
CSS(Cascading Style Sheet-级联样式表)的缺陷也很明显。这种「面向命名语言」,不太友好,格式死板,低复制效率。css 预处理器就是这样被创造出来,弥补了直接写 css 的一些缺憾:
- 语法不够强大,比如无法嵌套书写导致模块化开发中需要书写很多重复的选择器;
- 没有变量和合理的样式复用机制,使得逻辑上相关的属性值必须以字面量的形式重复输出,导致难以维护。
Stylus:2010年产生,来自Node.js社区,主要用来给Node项目进行CSS预处理支持,在此社区之内有一定支持者。Stylus 中文文档。
Stylus 是一个高效、动态以及丰富的 CSS 预处理器。它同时支持缩进的和通俗的两种风格的 CSS 语法风格。Stylus 扩展名为「 *.styl 」,Nib是 Stylus 的应用的类库。给你的「* .styl 」添加 Nib 的最快方式是克隆 Nib 的 Git 版本库并引入,因为有了 Nib,Stylus 的高效性才更为突出。Stylus 在容错性上的突出特性也十分吸引我,你可以在一个 Stylus 文件里这样写,且它们都会被编译成标准 css:
/*style.styl*/
/*类似于CSS标准语法*/
h1 {
color: #963;
background-color:#333;
}
/*省略大括号({})*/
h1
color: #963;
background-color: #333;
/*省略大括号({})和分号(;)*/
h1
color:#963
background-color:#333
Try Stylus!
这一部分参考知乎徐小七七。
body,html
margin:0
padding:0
编译成:
body,
html {
margin: 0;
padding: 0;
}
stylus : 强大的功能丰富的语言
-pos(type, args)
i = 0
position: unquote(type)
{args[i]}: args[i + 1] is a 'unit' ? args[i += 1] : 0
{args[i += 1]}: args[i + 1] is a 'unit' ? args[i += 1] : 0
absolute()
-pos('absolute', arguments)
fixed()
-pos('fixed', arguments)
#prompt
absolute: top 150px left 5px
width: 200px
margin-left: -(@width / 2)
#logo
fixed: top left
编译成
#prompt {
position: absolute;
top: 150px;
left: 5px;
width: 200px;
margin-left: -100px;
}
#logo {
position: fixed;
top: 0;
left: 0;
}
stylus:nibstylus插件
@import 'nib'
body
background: linear-gradient(20px top, white, black)
编译成
body {
background: -webkit-linear-gradient(20px top, #fff, #000);
background: -moz-linear-gradient(20px top, #fff, #000);
background: -o-linear-gradient(20px top, #fff, #000);
background: -ms-linear-gradient(20px top, #fff, #000);
background: linear-gradient(20px top, #fff, #000);
}
2.JavaScript 实例
https://www.w3school.com.cn/js/js_examples.asp
前面的html
是网页页面的主要内容,css
是格式化定制页面外观的代码。而JavaScript
的作用让上面的内容“动起来”,是让页面内容发生改变的代码。
JavaScript是一种脚本语言。脚本语言又被称为扩建的语言,或者动态语言,是一种编程语言,用来控制软件应用程序,脚本通常以文本(如ASCII)保存,只在被调用时进行解释或编译。
根据我的理解,JavaScript就是网页代码中的“函数”(function),它可以根据开发者的目的对网页内容、属性进行一些改变。
语法结构
<script>
...
</script>
弹框的脚本:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>无标题文档</title>
<script>
alert("Welcome! Can I help you?");
</script>
</head>
<body>
..
</body>
</html>
如果将JavaScript代码写在body内,需要这么写:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>无标题文档</title>
</head>
<body>
<script>
document.write("<h1>Welcome!</h1>");
document.write("<h3>Can I help you?</h3>");
</script>
</body>
</html>
复杂一点的代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>无标题文档</title>
</head>
<body>
<h1>中国图书网</h1>
<p id="do">社科类图书</p>
<div id="DIV">军事类图书</div>
<p>
<button type="button" onclick="Function()">
单击
</button>
</p>
<script>
function Function(){
document.getElementById("do").innerHTML="计算机类图书";
document.getElementById("DIV").innerHTML="历史类图书";
}
</script>
<p style='color:red'>
单击按钮,改变显示内容。
</p>
</body>
</html>