教程目标及说明

编写一个可被外部访问的,主要用于存放Markdown文章的,具有多层页面的,特定主题风格的静态页面。

本指南仅记录个人搭建博客的步骤与心得,旨在抛砖引玉,让读者能以最简单的方式入门。如有更多探索需要,请阅读官方文档,或具有权威性质的教程文档。

一些你可能用得到的资源:

Hexo官方文档: https://hexo.io/zh-cn/docs

Hexo主题页面:https://hexo.io/themes/

butterfly主题官方文档:https://butterfly.js.org/posts/4aa8abbe/

_MDN-CSS教学文档:https://developer.mozilla.org/zh-CN/docs/Web/CSS


阶段1 Hexo搭建静态博客

  1. 安装带有npm的Node.js

    • Node.js是JavaScript得以运行的环境
    • npm则是JS的“插件库”,Hexo即是里面的插件之一。
    • 使用node -vnpm -v,确认安装的版本。
  2. 使用npm安装Hexo并初始化

    • 安装:npm install -g hexo-cli,全局安装hexo运行库
    • 初始化:hexo init Blog-test,在根目录下创建名为Blog-test的文件夹,并进行初始化。
    • 安装依赖:cd Blog-test npm install ,依据初始化生成的package.json,在node_module中安装必要的依赖包。
    • 本地预览:Hexo server,在终端按ctrl+c停止预览。
    • Hexo的目录结构:source/post 文件夹下可以存放Markdown文章。
  3. md文件的编写要求

    • 需要在md文件开头(front-matter)写上title:...,并用两个---分隔,否则只会渲染部分内容。
    • 此外,front-matter 还可以写其他内容来实现更加丰富的效果,具体请参考相关文档。
    1
    2
    3
    4
    ---
    title: 标题名
    date: 2026-4-1
    ---
    • hexo的默认页面
  4. 编辑网页相关配置

    • 在根目录下找到文件_config.yml,在其中可以编辑网页的一些基本要素,具体请参阅Hexo文档

到此为止,使用Hexo快速构建静态博客的工作就完成了。接下来是美化部署的流程。


阶段2 使用主题,自定义博客的第一步

  1. 使用现成的主题

    • 访问 Hexo主题页面 以获得主题的预览。

    • 打开主题对应的github页面,将主题仓库clone到themes文件夹中。

    • 更改_config.yml,将themes:...的内容更改为你下载的仓库的文件夹的名称。

    • 在应用主题前,一定要阅读主题的介绍或README,确保安装必要的依赖项。

    • 注:本次使用butterfly作为模板进行网页搭建。具体教程请阅读官方文档

    • 使用了主题后的效果

  2. 在魔改之前:认识themes的文件架构

    • 一个成熟的themes会在文件夹中提供_config.yml,不同于根目录的_config.yml控制整个网页,这个config只保存针对样式的修改。
    • butterfly支持config的外置。在开始修改_config.yml之前,需要先将其复制-粘贴-重命名为_config.butterfly.yml到根目录中。在做出修改时,只修改新文件,避免之后更新覆盖已经修改的设置。
    • 再补充一点:在修改config时,需要对字体、图片进行修改,涉及路径时,需要注意大部分的路径都以 source文件夹为根目录,且在写路径时必须在前面加斜线。在source之外的资源都无法被识别。例如在source/image中放置了一个LOGO.png,那么对应的config就是这样:
1
2
3
nav:
# Navigation bar logo image
logo: \images\LOGO.png
  1. 修改config:LOGO、Banner、Avatar等

    • 有关butterfly的_config.butterfly.yml的修改,参阅官方文档
    • 参照butterfly官方文档,以及config文件中的说明,可对用放在source/images的图片对logo、banner、avatar等进行自定义。
    • 自定义后的页面

阶段3 使用CSS,进行深度魔改

  本段建议参照 “阶段4:使用JavaScript监听页面” 阅读。
  1. 在这章之前:介绍三个与网页息息相关的文件:

    • HTML,即网页的结构,也是对象表,它决定了网页的内容与结构,决定了网页中应当包含什么要素。此外,html还自带了天然的显示层级,即DOM树。
    • CSS,即网页的外观,也是规则表,它选择HTML中规定的元素,并修改这些元素的显示样式。
    • Javascript,即网页的行为,也是监听器,它保证了HTML中的元素何时产生变化。
    • 简而言之:一个完整的网页经Javascript监听,操作html中的元素,依照css的规则产生变化。
  2. 深度魔改之前:什么是CSS?

    • 层叠样式表(Cascading Style Sheets,缩写为 CSS) 是一种样式表语言,用来描述 HTML 或 XML(包括如 SVG、MathML 或 XHTML 之类的 XML 分支语言)文档的呈现方式。CSS 描述了在屏幕、纸质、音频等其他媒体上的元素应该如何被渲染的问题

    • 简单来说,CSS是一种规则表它规定了页面中的某些元素应该具有什么样的显示效果。 例如,通过注入CSS文件,你可以为页面中处于某些特殊位置的图片附上滤镜、加上边框、改颜色等。结合JavaScript脚本,也可以实现切换的动画效果。

    • CSS学习文档:网页链接

  3. CSS的基本语法

    • CSS的基本语法是这样的:
1
2
3
4
5
#选择器{
属性1:赋值;
属性1:赋值;
...
}
    • 选择器用于选择页面中具有某些属性、类名等特征的元素。大括号内则可对该元素的一些属性进行赋值,以实现不同的效果。例如如下的代码:
1
2
3
#page-header.nav-fixed {
background-color: rgba(88, 146, 228, 0.3);
}
    • 这段代码意思是:“选择id为page-header,且属于nav-fixed类的对象,将其背景颜色修改为rgba(88, 146, 228, 0.3)的这种颜色。
    • 看不懂没关系,后面会详细讲解“类”与“属性”的概念。
  1. 如何在页面中找到你要修改的元素

    • 在页面中单击F12,进入开发人员工具页面。
    • 开发者工具页面
    • 单击工具页面左上角,带箭头的小图标,或者按Ctrl+shift+C
    • 元素选择器
    • 单击页面中的图片、文字等,就可以在右侧看到元素的类、id、样式等信息。
    • 主页头图的信息,其ID为page-header,类为full_page
  2. 什么是类?什么是属性?

    • 一个要素的类 (class) 是一种分类标识,它可以用来标识一组元素,让 CSS 可以统一设置样式,通常是JS脚本与CSS之间的桥梁。
    • 一个要素的属性 (attribute) 类似于一个对象的成员变量,一些属性直接控制了要素的显示效果,也有一些控制要素存储的信息等。
    • 总而言之:类是要素的标识,而属性是要素本身带有的信息。
  3. 在config中注入CSS文件

    • 需要在根目录下的source创建css文件夹,将css文件放入其中。
    • 在config.butterfly.yml中,找到如下字段:
1
2
3
4
5
6
7
8
# Inject
# Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag)
inject:
head:
- <link rel="stylesheet" href="/css/custom.css">
# - <link rel="stylesheet" href="/xxx.css">
bottom:
# - <script src="xxxx"></script>
    • 其中href="..."中填写你的css文件路径。
  1. 写一个简单的CSS样式:毛玻璃效果
    • 本次不使用JS脚本,只用butterfly中,banner要素自带的full_pagenav_fixed两个类,完成毛玻璃效果的实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#page-header {
position: relative;
background-color: transparent !important;
transition: all 0.2s ease !important;
}

#page-header::before{
/* 创建覆盖层:模糊效果 */
content: "";
position: absolute;
top: 0;
left: 0;
width: 80%;
height: 80%;
background-image: url(/images/Banner.png);
background-size: cover;
background-position: center;
transition: all 0.2s ease !important;
z-index: -2;
}


#page-header::after {
/* 创建覆盖层:纯色滤镜 */
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(92, 139, 192, 0.3);
z-index: -1;
transition: background-color 0.2s ease;
}


#page-header.nav-fixed::after {
/* 在page-header处于“nav-fixed”下,改变after的属性(变不透明) */
background-color: rgba(88, 146, 228, 0.3);
}

#page-header.nav-fixed::before{
/* 在page-header处于“nav-fixed”下,改变before的属性(应用滤镜)*/
filter: blur(10px);
}
  • 以下是一些必要的说明:
    • #page-header.nav-fixed::after是啥?
      • 这是一个选择器,它的意思是选择id为page-header,具有nav-fixed类的after“伪要素”。
      • 其中,afterbefore是css自带的语法糖,它允许用户在不更改HTML的情况下,创建两个继承原要素的“伪要素”。每个要素都具有一个before与一个after
      • 其中z-index代表的层级,类似于PS的图层,即要素显示的顺序。高的z-index代表这个对象显示在上层。例如这个案例中,就控制了afterbefore的index,让after即滤镜层显示在before的上面。
      • 不具有z-indexposition的对象,则显示的顺序依照F12页面显示的顺序,即DOM树顺序。
    • 有关positionz-index与DOM树,目前有以下几点特性:
      • 当css中没有设置position时,其值默认为static
      • position不为static时,z-index才可起作用。只要z-index是正数,其总能覆盖在同等层级的static要素的上方。
      • z-index控制的层级顺序不是全局的,只能在同等层级的对象之间起作用。例如,如果对page-header本身进行了z-index: 1,对after设定了z-index:2,不会导致after覆盖在header上面,因为page-headerafter父级,对page-header进行改动只会影响这个整体同级元素中的显示顺序。

阶段4 使用JS脚本进行监听

  这里不再涉及JS的具体语法讲解,只讲解实现过程。
  1. 在编写之前:注入JS文件

    • 在source文件夹下创建js文件夹,新建custom.js文件。
    • 在_config.butterfly.yml中更改:
    1
    2
    3
    4
    5
    6
    7
    inject:
    head:
    - <link rel="stylesheet" href="/css/custom.css">
    # - <link rel="stylesheet" href="/xxx.css">
    bottom:
    - <script src="/js/custom.js"></script> #这一行是JS文件
    # - <script src="xxxx"></script>
  2. 源代码讲解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function on_DOM_ready () {
var header = document.getElementById("page-header");
if(!header) return;
//获取page-header

var SCROLL_TRIGGER = 100;

function scroll_handle() {
if(window.scrollY > SCROLL_TRIGGER){
header.classList.add("blur-banner");
}else{
header.classList.remove("blur-banner");
}
}
//定义判定blur-banner的函数

window.addEventListener('scroll',scroll_handle);
//监听器持续运行:当滚动时,判定一次逻辑


scroll_handle();
//在判定之外调用一次,即在生成网页或刷新时判定一次,确保静止时状态正确
}
document.addEventListener('DOMContentLoaded',on_DOM_ready);
//在DOM树构建完成后,执行on_DOM_ready函数
  • 这段代码主要有以下过程:
    • 调用的addEventListener是一个添加监听器的函数,它接受两个参数:触发监听器的时间、一个函数,它将会在事件发生的同时执行这个函数。
    • onDOMready是一个外部定义的函数,其在代码尾部作为回调函数。
    • var header=... 即寻找ID为page-header的要素,并将其赋值给header,如果header为空,则返回(异常处理)
    • 定义一个常量SCROLL_TRIGGER,即触发滤镜的距离。
    • 定义一个判定函数,当滚动超过滚动阈值时,为header赋予blur-banner类,否则移除该类。
    • 再添加一个监听器,在滚动时调用判定函数。
    • 调用一次,确保创建网页或刷新时,banner能正常显示。
  • 当刷新或触发网页时,代码的运行过程:
    • 创建document监听器。
    • 刷新时,构建DOM树。构建完成后触发DOMContentLoaded事件,执行on_DOM_ready()
    • 创建window监听器。
    • 触发一次scroll_handle函数,确保刷新后,处于应触发滤镜位置的header可正确显示滤镜。
    • 当用户滚动页面时,window监听器工作,触发scroll_handle函数。

阶段5 将网页部署至Github