在MAC上搭建HEXO博客指南

记录搭建全过程

安装环境配置

1. node.js

这一步需要调整,使用 Node.js 的版本管理工具 nvm 来安装 Node.js,这样会避免权限问题。

安装nvm

在终端中运行以下命令来安装nvm

使用 curl 安装方法

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

使用 wget 安装方法

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

安装完成后,需要将 nvm 加载到当前的终端会话中。关闭并重新打开终端,或者运行以下命令来加载 nvm。

source ~/.nvm/nvm.sh

我目前只能通过命令加载


如果你使用的是 zsh 作为你的 shell,将上面的命令替换为:

source ~/.nvm/nvm.sh --no-use

验证 nvm 安装,运行以下命令验证 nvm 是否已经安装成功:

nvm --version

安装 Vue.js

nvm install stable  # 使用 nvm 安装最新稳定版 Node.js
nvm use stable     # 使用刚刚安装的 Node.js 版本

# 或者如果你使用的是 n,可以运行以下命令:
# n latest
# n use latest

npm install -g @vue/cli

2. 安装git

(在终端输入git查询语句会自动弹出安装提醒)

3. 核验语句

node -v
Git -v
Npm -v

安装HEXO

npm install -g hexo
hexo -v

创建博客文件夹

hexo init Blog

我是先创建的,在把整个文件夹内容挪到文稿目录下面后,再进行下面操作

cd /Users/star/Documents/Blog 
npm install

输入下面语句,核验是否搭建成功了

Hexo s

关联Github

添加ssh key 到github

检查SSH keys是否存在Github

执行如下命令,检查SSH keys是否存在。如果有文件id_rsa.pubid_dsa.pub,则直接进入步骤1.3将SSH key添加到Github中,否则进入下一步生成SSH key。

ls -al ~/.ssh

生成新的ssh key

ssh-keygen -t rsa -C "w2388619506@163.com"

将ssh key添加到Github中

Find前往文件夹~/.ssh/id_rsa.pub打开id_rsa.pub文件,里面的信息即为SSH key,将这些信息复制到Github的Add SSH key页面即可。

进入Github –> Settings –> SSH keys –> add SSH key:

Title里任意添一个标题,将复制的内容粘贴到Key里,点击下方Add key绿色按钮即可。

重要‼️

每次启动终端后,都需要使用命令行加载nvm

source ~/.nvm/nvm.sh

博客配置文件修改

插件安装

  1. 要将hexo部署到GitHub
npm install hexo-deployer-git --save # 安装部署插件
  1. 短链接
npm install hexo-abbrlink --save
  1. 本地搜索
npm install hexo-generator-searchdb

站点配置文件

  1. GitHub同步设置
# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
  type: git
  repo: git@github.com:Star0time/Star0time.github.io.git
branch: master

相同的部分做替换

  1. 短链接配置

修改config.yml文件中的永久链接设置:

permalink: posts/:abbrlink/ 
# 或者
permalink: posts/:abbrlink.html
# abbrlink 配置

abbrlink:
  alg: crc32      # 支持 crc16(默认)和 crc32
  rep: hex        # 支持 dec(默认)和 hex
  drafts: false   # 是否处理草稿:(true) 处理,(false) 不处理,默认为 false

  # 自动根据目录树生成分类

  # depth: 所需生成的目录树最大深度,应大于 0

  auto_category:
    enable: true  # 默认启用
    depth:        # 默认 3
    over_write: false 
  auto_title: false # 启用自动标题功能,能根据路径自动填充标题
  auto_date: false # 启用自动日期功能,能根据当前时间自动填充日期
  force: false # 启用强制模式,在此模式下,插件会忽略缓存,对每篇已有缩略名的文章重新计算。这只会更新缩略名,不会更新其他前端变量。
  1. 本地搜索
search:
  path: search.xml
  field: post
  content: true
  format: html
  1. 代码块高亮-联合主题设置
syntax_highlighter: prismjs

文章内容按照更新时间排序

# 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: -updated
  
  # Archive generator
archive_generator:
  per_page: 10
  yearly: true
  monthly: true
  order_by: -updated  

主题配置文件

安装主题-next

git clone https://github.com/next-theme/hexo-theme-next themes/next

更新

cd themes/next
git pull

启用本地搜索

NexT配置文件

# Local search
# Dependencies: https://github.com/next-theme/hexo-generator-searchdb
local_search:
  enable: true
  # Show top n results per article, show all results by setting to -1
  top_n_per_article: 1
  # Unescape html strings to the readable one.
  unescape: false
  # Preload the search data when the page loads.
  preload: false

去除底部的”由 Hexo & NexT 强力驱动”

# Powered by Hexo & NexT

  powered: false

代码块高亮-联合站点配置

codeblock:
  # Code Highlight theme
  # All available themes: https://theme-next.js.org/highlight/
  theme:
    light: default
    dark: stackoverflow-dark
  prism:
    light: prism-tomorrow
    dark: prism-tomorrow

侧边栏靠右展示

sidebar:
  # Sidebar position. Available values: left | right
  position: right

侧边栏隐藏

设置侧栏显示的时机,修改 Sidebar display 的值,支持的选项有:

  • post - 默认行为,在文章页面(拥有目录列表)时显示
  • always - 在所有页面中都显示
  • hide - 在所有页面中都隐藏(可以手动展开)
  • remove - 完全移除
sidebar:
  display: hide

添加代码复制按钮

配置copy_button字段:

copy_button:
  enable: true
  # Available values: default | flat | mac
  style: mac
# Fold code block
fold:
  enable: true
  height: 500

返回顶部

back2top:
  enable: true
  # Back to top in sidebar.
  sidebar: false
  # Scroll percent label in b2t button.
  scrollpercent: true

上下篇文章导航按照更新时间排序

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

确保已开启文章导航:

post_navigation:
  enabled: true
  # 添加或修改这一行
  order_by: updated  # 可选: updated, date, title

2. 在Hexo根目录创建脚本

在博客根目录的 scripts/ 文件夹(如果没有就创建一个)中创建 custom-nav.js

'use strict';

hexo.extend.filter.register('template_locals', function(locals) {
  if (locals.page && locals.page.layout === 'post') {
    // 获取所有文章按更新时间排序
    const posts = locals.site.posts.sort('updated', -1).toArray();
    const currentIndex = posts.findIndex(post => 
      post._id === locals.page._id || 
      (post.title === locals.page.title && post.date === locals.page.date)
    );
    
    if (currentIndex !== -1) {
      // 设置上一篇和下一篇
      locals.page.prev = currentIndex > 0 ? posts[currentIndex - 1] : null;
      locals.page.next = currentIndex < posts.length - 1 ? posts[currentIndex + 1] : null;
    }
  }
  
  return locals;
});

文章内容分栏设计(针对股票)

  1. 在博客根目录/source/_data文件夹内新建styles.styl文件,内容如下
// 媒体分栏布局 - 通用样式

// 覆盖主题的默认图片样式
.media-split-layout .image-container img {
  margin-bottom: 0 !important;
}

// 主布局容器 - 基础样式
.media-split-layout {
  display: flex;
  align-items: stretch;
  gap: 20px;
  margin: 40px 0;
  position: relative;
}

// 图片容器 - 共用样式
.image-container {
  flex: 0 0 80%;
  background: white;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  margin-bottom: 0 !important;
}

.image-container img {
  width: auto;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  object-position: center;
  border-radius: 8px;
  margin: 0 !important;
  display: block;
}

// 文字容器 - 共用样式
.text-container {
  flex: 0 0 20%;
  position: relative;
  background: white;
  border-radius: 8px;
  box-shadow: 0 3px 10px rgba(0,0,0,0.05);
  overflow: hidden;
  margin-bottom: 0 !important;
}

// 文字块 - 共用样式
.text-block {
  position: absolute;
  left: 0;
  right: 0;
  padding: 15px;
  border-bottom: 1px solid #f0f0f0;
  transition: background 0.3s ease;
  overflow: auto; /* 改为auto,允许滚动 */
  box-sizing: border-box;
}

.text-block:last-child {
  border-bottom: none;
}

.text-block:hover {
  background: #f8fafc;
}

.text-block h5 {
  margin: 0 0 8px 0;
  color: #333;
  font-weight: 600;
  line-height: 1.2;
}

.text-block p {
  margin: 0;
  line-height: 1.5;
  color: #666;
  /* 移除行数限制,允许完整显示 */
  display: block;
  overflow: visible;
  white-space: normal;
}

// ========== 4块布局样式 ==========
.media-split-layout-4 {
  min-height: 500px;
}

.media-split-layout-4 .text-block {
  height: 25%; /* 100% ÷ 4 */
}

.media-split-layout-4 .text-block h5 {
  font-size: 15px;
}

.media-split-layout-4 .text-block p {
  font-size: 13px;
}

// ========== 6块布局样式 ==========
.media-split-layout-6 {
  min-height: 600px;
}

.media-split-layout-6 .text-block {
  height: 16.6667%; /* 100% ÷ 6 */
}

.media-split-layout-6 .text-block h5 {
  font-size: 14px;
}

.media-split-layout-6 .text-block p {
  font-size: 12px;
  /* 移除行数限制 */
}

// ========== 响应式设计 ==========
@media (max-width: 768px) {
  .media-split-layout {
    flex-direction: column;
    min-height: auto !important;
  }
  
  .image-container,
  .text-container {
    flex: 0 0 100% !important;
    width: 100%;
  }
  
  .image-container {
    height: 300px;
  }
  
  .text-container {
    height: auto;
    min-height: 300px;
  }
  
  .text-block {
    position: relative !important;
    top: 0 !important;
    height: auto !important;
    min-height: 80px;
    overflow: visible; /* 移动端不需要滚动 */
  }
  
  // 移动端调整文字大小
  .media-split-layout-4 .text-block {
    padding: 12px;
  }
  
  .media-split-layout-4 .text-block h5 {
    font-size: 14px;
    margin-bottom: 6px;
  }
  
  .media-split-layout-4 .text-block p {
    font-size: 12px;
    line-height: 1.4;
  }
  
  .media-split-layout-6 .text-block {
    padding: 10px;
  }
  
  .media-split-layout-6 .text-block h5 {
    font-size: 13px;
    margin-bottom: 4px;
  }
  
  .media-split-layout-6 .text-block p {
    font-size: 11px;
    line-height: 1.3;
  }
}
  1. 找到主题设置文件 博客根目录/themes/next/_config.yml,

    找到 custom_file_path,

    新增一条 style: source/_data/styles.styl

  2. 新增文章布局:6块文章布局

<div class="media-split-layout media-split-layout-6">
  <div class="image-container">
    <img src="/images/图片2.jpg" alt="示例图片">
  </div>
  <div class="text-container">
    <div class="text-block" style="top: 0%; height: 16.6667%;">
      <h5 id="section-1">标题1</h5>
      <p>内容1</p>
    </div>
    <div class="text-block" style="top: 16.6667%; height: 16.6667%;">
      <h5 id="section-2">标题2</h5>
      <p>内容2</p>
    </div>
    <div class="text-block" style="top: 33.3333%; height: 16.6667%;">
      <h5 id="section-3">标题3</h5>
      <p>内容3</p>
    </div>
    <div class="text-block" style="top: 50%; height: 16.6667%;">
      <h5 id="section-4">标题4</h5>
      <p>内容4</p>
    </div>
    <div class="text-block" style="top: 66.6667%; height: 16.6667%;">
      <h5 id="section-5">标题5</h5>
      <p>内容5</p>
    </div>
    <div class="text-block" style="top: 83.3333%; height: 16.6667%;">
      <h5 id="section-6">标题6</h5>
      <p>内容6</p>
    </div>
  </div>
</div>
  1. 新增文章布局:4块文章布局
<div class="media-split-layout media-split-layout-4">
  <div class="image-container">
    <img src="/images/图片1.jpg" alt="示例图片">
  </div>
  <div class="text-container">
    <div class="text-block" style="top: 0%; height: 25%;">
      <h5 id="section-1">标题1</h5>
      <p>内容1</p>
    </div>
    <div class="text-block" style="top: 25%; height: 25%;">
      <h5 id="section-2">标题2</h5>
      <p>内容2</p>
    </div>
    <div class="text-block" style="top: 50%; height: 25%;">
      <h5 id="section-3">标题3</h5>
      <p>内容3</p>
    </div>
    <div class="text-block" style="top: 75%; height: 25%;">
      <h5 id="section-4">标题4</h5>
      <p>内容4</p>
    </div>
  </div>
</div>

代码块字号小1.5号

在styles.styl文件内,新增以下代码:

// ========== 代码字体大小调整(小1.5号) ==========

// 基准字体大小假设为16px,小1.5号约为13.5px (0.84em)

// 1. 行内代码(小1.5号)
code:not(pre code) {
  font-size: 0.84em !important;      // 小1.5号
  padding: 0.15em 0.3em !important;
  background-color: rgba(127, 127, 127, 0.05) !important;
  border-radius: 2px !important;
  border: 1px solid rgba(127, 127, 127, 0.08) !important;
}

// 2. 代码块(小1.5号)
pre, pre code {
  font-size: 0.88em !important;      // 小1.5号,比行内代码稍大一点
  line-height: 1.7 !important;       // 增加行高以提高可读性
}

// 3. 代码块容器
.highlight, figure.highlight {
  font-size: 0.88em !important;
}

// 4. 高亮代码区域
.highlight pre, .highlight code {
  font-size: 0.88em !important;
}

// 5. Prism.js 样式(如果使用)
pre[class*="language-"], code[class*="language-"] {
  font-size: 0.88em !important;
}

// 6. 行号调整
.gutter pre {
  font-size: 0.82em !important;       // 行号更小一点
  color: #888 !important;
  text-align: right !important;
  padding-right: 8px !important;
}

// 7. 表格内的代码(更小一些)
table code {
  font-size: 0.78em !important;
}

// 8. 列表和引用块内的代码
li code, blockquote code {
  font-size: 0.8em !important;
}

// 9. 标题内的代码(保持相对大小)
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
  font-size: 0.85em !important;
}

// 10. 移动设备适配
@media (max-width: 767px) {
  code:not(pre code) {
    font-size: 0.78em !important;
  }
  
  pre, pre code {
    font-size: 0.82em !important;
  }
  
  .gutter pre {
    font-size: 0.76em !important;
  }
}

themes/next/_config.yml 中确保以下配置:

codeblock:
  # Code Highlight theme
  # All available themes: https://theme-next.js.org/highlight/
  theme:
    light: default
    dark: stackoverflow-dark
  prism:
    light: prism-tomorrow
    dark: prism-tomorrow
  # Add copy button on codeblock
  copy_button:
    enable: true
    # Available values: default | flat | mac
    style: mac
  # Fold code block
  fold:
    enable: true
    height: 500
  # Display language name
  language: false
    # 添加字体大小配置(如果不存在,添加以下内容)
  font_size:
    # 正常代码块
    normal: 14px
    # 移动设备
    mobile: 13px
    # 行高调整(可选,使小字体更易读)
    line_height: 1.7

新增顶部导航

这是最可靠的方法,因为主题文件是纯HTML/模板文件,可以直接使用模板语法。

步骤1:修改Next主题的文章模板

找到并修改 themes/next/layout/_macro/post.njk 文件:

文章标题和内容之间 添加以下代码(在适当的位置):

njk

{# 添加简洁顶部导航 #}
{% if not is_index and theme.post_navigation and (post.prev or post.next) %}
  {% set prev = post.prev if theme.post_navigation === 'right' else post.next %}
  {% set next = post.next if theme.post_navigation === 'right' else post.prev %}
  
  <nav class="simple-top-nav" style="margin: 20px 0 25px 0; padding: 0; border-top: 1px solid #eee; border-bottom: 1px solid #eee;">
    <div style="display: flex; justify-content: space-between; align-items: center; padding: 10px 0; font-size: 13px;">
      {% if prev %}
      <div style="flex: 1;">
        <a href="{{ url_for(prev.path) }}" rel="prev" style="color: #666; text-decoration: none; display: inline-flex; align-items: center; padding: 5px 10px; border-radius: 4px; transition: all 0.2s ease;">
          <span style="margin-right: 5px;"></span>
          <span style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
            {{ prev.title or __('post.untitled') }}
          </span>
        </a>
      </div>
      {% endif %}
      
      <div style="flex: 0 0 auto; padding: 0 15px; color: #999; font-size: 12px;">
        {% if prev and next %}|{% endif %}
      </div>
      
      {% if next %}
      <div style="flex: 1; text-align: right;">
        <a href="{{ url_for(next.path) }}" rel="next" style="color: #666; text-decoration: none; display: inline-flex; align-items: center; padding: 5px 10px; border-radius: 4px; transition: all 0.2s ease;">
          <span style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
            {{ next.title or __('post.untitled') }}
          </span>
          <span style="margin-left: 5px;"></span>
        </a>
      </div>
      {% endif %}
    </div>
  </nav>
  
  <style>
  /* 简洁悬停效果 */
  .simple-top-nav a:hover {
    color: #0078e7 !important;
    background-color: #f5f5f5 !important;
  }
  
  /* 移动端适配 */
  @media (max-width: 768px) {
    .simple-top-nav a span {
      max-width: 150px !important;
    }
    
    .simple-top-nav div[style*="flex: 0 0 auto"] {
      padding: 0 8px !important;
    }
  }
  
  @media (max-width: 480px) {
    .simple-top-nav a span {
      max-width: 120px !important;
    }
  }
  </style>
{% endif %}

步骤2:具体位置参考

在你的 post.njk 文件中找到这个位置:

njk

{# 文章标题部分 #}
<header class="post-header">
  <!-- 标题代码 -->
</header>

{# ========== 在这里添加上面的导航代码 ========== #}

{# 文章内容部分 #}
<div class="post-body" itemprop="articleBody">
  <!-- 文章内容 -->
</div>

当点击锚点链接跳转到页面上的某个元素(比如 #market-position)时,该元素会出现在视口的顶部边缘,但标题被页面的固定头部(如导航栏)遮挡,导致需要向上滚动才能看到完整的标题。

在styles.styl文件内,新增以下代码:

/* 所有带有 id 的元素在成为滚动目标时,顶部留出 80px 空白 */
[id] {
  scroll-margin-top: 80px;
}

阅读全文增加圆角效果

在styles.styl文件内,新增以下代码

/* 阅读全文增加圆角效果*/
.btn {
border-radius: 10px;
}