---
# System prepended metadata

title: hugo部署博客

---

## 简介

为啥采用hugo，不采用hexo框架？

问题很好，没办法解释，两个框架都使用过，个人感觉hexo在主题上更丰富些，hugo在维护上更方便点，所以还是用回了hugo



文章搬运再修饰了脚本控制部分 ： https://letere-gzj.github.io/hugo-stack/  （啥？喷我搬运！人家已经写得非常好了，艺术品欣赏就好何必自己欺骗自己说是自己的呢🤡）

## 环境准备

### 安装git

- 前往【[Git官网](https://git-scm.com/)】，下载安装程序
- 一直点下一步，默认安装即可

![image-20240920111531850-17268021497931](https://hackmd.io/_uploads/HJxXm2DC0.png)


### 安装hugo

官网：**[The world’s fastest framework for building websites | Hugo (gohugo.io)](https://gohugo.io/)**

- 前往【[Hugo Github Tags](https://github.com/gohugoio/hugo/tags)】，选择对应版本下载，下载后解压即可
- Windows下载版本：**hugo_extended_xxxxx_windows_amd64.zip**

![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/hugo%E4%B8%8B%E8%BD%BD.png)



## 搭建博客

### 创建博客

- （1）在**hugo.exe**所在文件夹的地址栏敲打cmd，然后Enter唤起命令行



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/1%E5%90%AF%E5%8A%A8cmd.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/1启动cmd.png)



- （2）敲打命令`hugo new site xxxx`创建hugo文件



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/2newSite.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/2newSite.png)



- （3）敲打命名`cd xxxx`切换目录，并把**hugo.exe**复制到刚生成的文件夹中



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/3cd.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/3cd.png)

目录结构：

```
.
├── archetypes      # 存放文章模板
├── config.toml     # 简单的配置文件
├── content         # 存放文章
├── data            # 存放生成静态页面时的配置文件
├── layouts         # 存放页面布局的模板
├── static          # 存放图片等静态内容
└── themes          # 存放下载的主题
```

- （4）敲打命令`hugo server -D`启动服务，访问[http://localhost:1313](http://localhost:1313/)，Ctrl+C停止服务 **（hugo默认是没有主题的，需要进行主题配置）**



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/4server.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/4server.png)





[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/5pageNotFind.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/5pageNotFind.png)



------

### 配置主题

- （1）前往【[Hugo Themes](https://themes.gohugo.io/)】，查找自己喜欢的主题，进行下载



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/1.1hugo%E4%B8%BB%E9%A2%98%E4%B8%8B%E8%BD%BD.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/1.1hugo主题下载.png)



- （2）这边以【[Stack主题](https://github.com/CaiJimmy/hugo-theme-stack/tags)】为例，将下载好的主题解压，放到`/themes`文件夹中



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/1.2%E4%B8%BB%E9%A2%98%E8%A7%A3%E5%8E%8B.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/1.2主题解压.png)



- （3）将`exampleSite`样例数据中的 **Content** 和 **hugo.yaml** 复制到主文件夹中，并删掉`hugo.toml`和`content/post/rich-content`



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/1.3%E4%B8%BB%E9%A2%98%E5%A4%8D%E5%88%B61.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/1.3主题复制1.png)





[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/1.4%E4%B8%BB%E9%A2%98%E5%A4%8D%E5%88%B62.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/1.4主题复制2.png)



- （4）修改 **hugo.yaml** 中的 **theme**，将他修改为跟主题文件夹同名



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/1.5%E4%B8%BB%E9%A2%98%E5%90%8D%E4%BF%AE%E6%94%B9.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/1.5主题名修改.png)



- （5）再次启动hugo服务，查看主题，具体主题配置修改 **hugo.yaml**，这里不细说，感兴趣可自行查找相关文章



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/stack%E4%B8%BB%E9%A2%98.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/stack主题.png)



------

## Github部署

### 常规部署

- （1）前往【[Github官网](https://github.com/)】，创建仓库 **{github用户名}.github.io**



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/github%E4%BB%93%E5%BA%93%E5%88%9B%E5%BB%BA.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/github仓库创建.png)



- （2）前往`Setting -> Pages -> Branch`选择main分支，然后保存，会自动开启 **https://{github用户名}.github.io** 的地址，这地址也是以后访问博客的地址



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/githubPage%E5%90%AF%E5%8A%A8.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/githubPage启动.png)



- （3）回到hugo文件中，执行命令`hugo -D`，会生成 **public** 静态资源文件夹



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/%E7%94%9F%E6%88%90%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/生成静态文件.png)



- （4）在 **public** 执行以下命令上传到github仓库上，第一次上传可能需要输入账号密码

```shell
git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin {你的github仓库地址}
git push -u origin main
```

- （5）上传成功后访问 **https://{github用户名}.github.io**，成功搭建属于自己的Hugo博客

### Github Action自动部署

- （1）Github创建一个新的仓库，用于存放Hugo的主文件
- （2）前往`Setttings -> Developer Settings -> Personal access tokens`，创建一个token(classic)



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/createToken1.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/createToken1.png)



- （3）token选择永不过期，并勾选 **repo** 和 **workflow** 选项



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/createToken2.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/createToken2.png)



- （4）为保证安全，将生成的token，保存的仓库的变量中，前往`Settings -> Secrets and variables -> Actions`中设置



[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/createToken3.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/createToken3.png)





[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/setToken1.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/setToken1.png)



- （5）在hugo主文件创建一个`.github/workflows/xxxx.yaml`文件，将以下内容复制进去，想具体了解更多，可查看【[Github Action文档](https://docs.github.com/zh/actions)】

```yaml
name: deploy

# 代码提交到main分支时触发github action
on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
        - name: Checkout
          uses: actions/checkout@v4
          with:
              fetch-depth: 0

        - name: Setup Hugo
          uses: peaceiris/actions-hugo@v3
          with:
              hugo-version: "latest"
              extended: true

        - name: Build Web
          run: hugo -D

        - name: Deploy Web
          uses: peaceiris/actions-gh-pages@v4
          with:
              PERSONAL_TOKEN: ${{ secrets.你的token变量名 }}
              EXTERNAL_REPOSITORY: 你的github名/你的仓库名
              PUBLISH_BRANCH: main
              PUBLISH_DIR: ./public
              commit_message: auto deploy

```

- （6）在hugo主文件创建`.gitignore`文件，来避免提交不必要的文件

```shell
# 自动生成的文件
public
resources
.hugo_build.lock

# hugo命令
hugo.exe
```

- （7）将hugo的主文件上传到仓库，上传成功后会触发Github Action，来自动部署你的静态页面

```shell
git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin {你的github仓库地址}
git push -u origin main
```

[![img](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-%E5%85%8D%E8%B4%B9%E9%83%A8%E7%BD%B2%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/githubActionRun.png)](https://letere-gzj.github.io/hugo-stack/p/hugohugo--github-免费部署自己的博客/githubActionRun.png)

### 脚本控制部署

做GitHub action自动化流程控制任务时，发现上传文件的时候并不符合，既然依旧需要git push进行上传，何不用脚本来进行编写控制能，还省去一堆麻烦

#### shell脚本

```shell
//删除public文件夹
rf -rf D:\hugo\qingjiegong\public

//hugo生成站点文件
hugo -D

//GitHub上传更新
cd public
git init
git add .
git commit -m "update"
//git branch -M master    //master还是main分支看自己部署时的存放路径
//git remote add origin https://你的GitHub仓库名.git
//git push -u origin master    
git push
```

#### bat脚本

Windows系统采用bat脚本，文件后缀名为.bat

```powershell
//删除public文件夹
rd /s /q D:\hugo\qingjiegong\public

//hugo生成站点文件
hugo -D

//GitHub上传更新
cd public
git init
git add .
git commit -m "update"
//git branch -M master    //master还是main分支看自己部署时的存放路径
//git remote add origin https://你的GitHub仓库名.git
//git push -u origin master    
git push
```

## 音乐播放器设置

### 引入音乐播放器

- 【[Aplayer官方文档](https://aplayer.js.org/#/zh-Hans/)】

| 名称            | 默认值                             | 描述                                                         |
| :-------------- | :--------------------------------- | :----------------------------------------------------------- |
| container       | document.querySelector(’.aplayer') | 播放器容器元素                                               |
| fixed           | false                              | 开启吸底模式, [详情](https://aplayer.js.org/#/home?id=fixed-mode) |
| mini            | false                              | 开启迷你模式, [详情](https://aplayer.js.org/#/home?id=mini-mode) |
| autoplay        | false                              | 音频自动播放                                                 |
| theme           | ‘#b7daff’                          | 主题色                                                       |
| loop            | ‘all’                              | 音频循环播放, 可选值: ‘all’, ‘one’, ’none’                   |
| order           | ’list’                             | 音频循环顺序, 可选值: ’list’, ‘random’                       |
| preload         | ‘auto’                             | 预加载，可选值: ’none’, ‘metadata’, ‘auto’                   |
| volume          | 0.7                                | 默认音量，请注意播放器会记忆用户设置，用户手动设置音量后默认音量即失效 |
| audio           | -                                  | 音频信息, 应该是一个对象或对象数组                           |
| audio.name      | -                                  | 音频名称                                                     |
| audio.artist    | -                                  | 音频艺术家                                                   |
| audio.url       | -                                  | 音频链接                                                     |
| audio.cover     | -                                  | 音频封面                                                     |
| audio.lrc       | -                                  | [详情](https://aplayer.js.org/#/home?id=lrc)                 |
| audio.theme     | -                                  | 切换到此音频时的主题色，比上面的 theme 优先级高              |
| audio.type      | ‘auto’                             | 可选值: ‘auto’, ‘hls’, ’normal’ 或其他自定义类型, [详情](https://aplayer.js.org/#/home?id=mse-support) |
| customAudioType | -                                  | 自定义类型，[详情](https://aplayer.js.org/#/home?id=mse-support) |
| mutex           | true                               | 互斥，阻止多个播放器同时播放，当前播放器播放时暂停其他播放器 |
| lrcType         | 0                                  | [详情](https://aplayer.js.org/#/home?id=lrc)                 |
| listFolded      | false                              | 列表默认折叠                                                 |
| listMaxHeight   | -                                  | 列表最大高度                                                 |
| storageName     | ‘aplayer-setting’                  | 存储播放器设置的 localStorage key                            |

- （1）在博客主目录中创建文件`layouts\partials\footer\custom.html`，此文件为Stack主题作者留给我们加入自定义组件用的文件(可以查看主题源码同路径文件找到) ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E5%99%A8%E5%BC%95%E5%85%A51.png)
- （2）查看官方文档，引入对应的脚本，css到`custom.html`中，页面最下面就会出现音乐播放器

```html
<!-- 【custom.html】 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css">
<div id="aplayer"></div>
<script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script>

<script>
const ap = new APlayer({
    container: document.getElementById('aplayer'),
    audio: []
});
</script>
```

[![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E5%99%A8%E5%BC%95%E5%85%A52.png)](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-引入音乐播放放器并实现音乐不中断功能/音乐播放器引入2.png)



- （3）修改 **ap对象** 配置，改成吸底模式

```html
const ap = new APlayer({
    ...,
    // 吸底模式
    fixed: true
});
```

[![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E5%99%A8%E5%BC%95%E5%85%A53.png)](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-引入音乐播放放器并实现音乐不中断功能/音乐播放器引入3.png)



- （4）填写 audio 数组，引入歌曲，封面和歌词
  - **lrcType** 的值具体看【[文档](https://aplayer.js.org/#/zh-Hans/?id=歌词)】
  - 歌曲，封面，歌词支持远程链接 or 本地路径
  - 若想通过本地路径引入，请将资源文件放在`static`文件夹下，通过hugo方法`{{ .Site.Home.Permalink }}`，获取网站主路径来拼接文件路径，进行引入

```html
const staticDir = {{ .Site.Home.Permalink }}
const ap = new APlayer({
    ...,
    lrcType: 3,
    audio: [
        {
             name: 'name', // 歌名
             artist: 'artist', // 歌手
             url: 'url.mp3', // 歌曲路径
             cover: 'cover.jpg', // 封面路径
             lrc: 'lrc.lrc', // 歌词路径
        },
        {
            // 本地路径引入写法（有子目录就在staticDir后面继续拼接）
            name: 'name',
            artist: 'artist',
            url: staticDir + 'url.mp3',
            cover: staticDir + 'cover.jpg',
            lrc: staticDir + 'lrc.lrc',
        }
    ]
});
```

[![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E5%99%A8%E5%BC%95%E5%85%A54.png)](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-引入音乐播放放器并实现音乐不中断功能/音乐播放器引入4.png)



***到这一步音乐播放器已经引入完了***

### 音乐播放器样式切换

- （1）通过阅读Stack主题的源码可以看到，主题样式的切换是通过`[data-scheme="light/darck"] {...}`
- （2）所以我们可以准备两种Aplayer的css，用`[data-scheme="light"]{ 亮的css样式 }`包裹亮的，用`[data-scheme="dark"]{ 暗的css样式 }`包裹暗的，这里直接给各位准备好了
  - [aplayer-light.scss](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-引入音乐播放放器并实现音乐不中断功能/aplayer-light.scss)（Ctrl+S保存）
  - [aplayer-dark.scss](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-引入音乐播放放器并实现音乐不中断功能/aplayer-dark.scss)（Ctrl+S保存）
- （3）在博客主目录中创建文件`assets\scss\custom.scss`，此文件为Stack主题作者留给我们加入自定义样式用的文件(可以查看主题源码同路径文件找到) ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%92%AD%E6%94%BE%E5%99%A8%E6%A0%B7%E5%BC%8F%E5%88%87%E6%8D%A21.png)
- （4）将上述两个scss文件放到跟 **custom.scss**同目录下，并通过`@import`来进行引入文件

```scss
/**
 * 【custom.scss】
 */
@import "aplayer-light.scss";
@import "aplayer-dark.scss";
```

- （5）因为音乐播放器的css改为我们本地文件引入了，所以 **custom.html** 中的link标签可以将它注释掉或者删掉了

```html
<!-- custom.html -->
...
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css"> -->
```

[![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%92%AD%E6%94%BE%E5%99%A8%E6%A0%B7%E5%BC%8F%E5%88%87%E6%8D%A22.png)](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-引入音乐播放放器并实现音乐不中断功能/播放器样式切换2.png)



***到这一步样式随主题切换已经完成了***

------

### 音乐播放进度保留

- （1）实现思路：页面切换前，把歌曲的 **歌曲序号**，**播放时长**，**播放状态** 记录下来；在页面切换加载完之后，我们调用Aplayer的接口切换歌曲，调整时长，并选择播放or暂停
- （2）通过`console.log(ap)`打印ap对象，或者官方文档，可以找到我们需要的参数
  - **list.index**：歌曲序号
  - **audio.currentTime**：播放时长
  - **paused** ：播放状态（是否暂停） ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E4%BF%9D%E7%95%99%E6%92%AD%E6%94%BE%E8%BF%9B%E5%BA%A61.png)
- （3）通过【[官方文档](https://aplayer.js.org/#/zh-Hans/?id=api)】可以查找到我们需要调用的接口
  - **ap.list.switch(index: number)**：切换歌曲
  - **ap.seek(time: number)**：调整时长
  - **ap.play()**：播放歌曲
- （4）需要的东西已经找齐，只需在 **custom.html** 加入以下代码即可

```html
<!-- 【custom.html】 -->
...
<script>
    ...
    /**
     * 页面销毁前监听
     */
    window.onbeforeunload = () => {
        // 将播放信息用对象封装，并存入到localStorage中
        const playInfo = {
            index: ap.list.index,
            currentTime: ap.audio.currentTime,
            paused: ap.paused
        };
        localStorage.setItem("playInfo", JSON.stringify(playInfo));
    };

    /**
     * 页面加载后监听
     */
    window.onload = () => {
        // 从localStorage取出播放信息
        const playInfo = JSON.parse(localStorage.getItem("playInfo"));
        if (!playInfo) {
            return;
        }
        // 切换歌曲
        ap.list.switch(playInfo.index);
        // 等待500ms再执行下一步(切换歌曲需要点时间，不能立马调歌曲进度条)
        setTimeout(() => {
            // 调整时长
            ap.seek(playInfo.currentTime);
            // 是否播放
            if (!playInfo.paused) {
                ap.play()
            }
        }, 500);
    };
</script>
```

### 引入PJAX

#### 基本引入

- （1）PJAX主要分为两个版本，**带JQuery** 和 **不带JQuery**的，下面我演示的是不带JQuery版本的
  - 【[defunkt/jquery-pjax](https://github.com/defunkt/jquery-pjax)】（带JQuery）
  - 【[MoOx/pjax](https://github.com/MoOx/pjax)】（不带JQuery）
- （2）分析页面元素，看哪些是需要我们重新加载的，可以发现是**左侧边栏**，**中间内容**，**右侧边栏** ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/PJAX%E5%BC%95%E5%85%A51.png)
- （3）查看页面源代码，可以发现这些元素都被一个`<div class="main-container">...</div>`包裹着，所以我们将着元素定为要刷新的对象 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/PJAX%E5%BC%95%E5%85%A52.png)
- （4）根据官方文档，在 **custom.html** 加入以下代码来引入PJAX

```html
<!-- 【custom.html】 -->
...
<script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script>
<script>
	var pjax = new Pjax({
	  selectors: [
	    ".main-container"
	  ]
	})
</script>
```

***这样算是已经基本引入了PJAX，但也带来了不少问题，我们一步一步进行修复***

------

#### 文章样式修复

- （1）随便点进其中一篇文章，可以发现文章内容的样式丢失，缺少文章该有的边框 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E6%A0%B7%E5%BC%8F%E4%BF%AE%E5%A4%8D1.png)
- （2）通过手动刷新，对比页面元素发现，决定文章样式的是`<body>` 标签中的class名: **article-page**，存在此class名就会识别到文章该有的css ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E6%A0%B7%E5%BC%8F%E4%BF%AE%E5%A4%8D2.png)
- （3）因为`<body>`内包含整个页面的所有内容，包括我们的音乐播放器，所以不能让PJAX监听`<body>`标签
  - 阅读官方文档发现，官方提供了数据预处理方法，让我们处理数据
  - 我们通过预处理数据，获取到新页面的className，然后我们手动将这className设置到`<body>`上

```html
<!-- 【custom.html】 -->
...
<script>
    ...
    pjax._handleResponse = pjax.handleResponse;
    pjax.handleResponse = function(responseText, request, href, options) {
        if (request.responseText.match("<html")) {
            // 将新页面的html字符串解析成DOM对象
            let newDom = new DOMParser().parseFromString(responseText, 'text/html');
            // 获取新页面中body的className，并设置回当前页面
            let bodyClass = newDom.body.className;
            document.body.setAttribute("class", bodyClass)
            // 放行pjax处理
            pjax._handleResponse(responseText, request, href, options);
        } else {
            // handle non-HTML response here
        }
    }
</script>
```

***这样我们切页面后，都会自动把body中的className更新，就不会丢失样式了***

------

#### 主题切换修复

- （1）当我们切换页面后，点击左下角切换主题颜色的按钮，会发现没有效果，主题颜色切换失效了 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E4%BF%AE%E5%A4%8D1.png)
- （2）阅读Stack主题源码`\assets\ts\colorScheme.ts`发现，在脚本初始化时，会给元素绑定一个点击事件。但因为页面切换了，替换了该元素，但没有该元素重新绑定点击事件，导致点击主题切换失效 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E4%BF%AE%E5%A4%8D2.png)
- （3）解决思路：在PJAX切换完页面后，重新执行一遍`colorScheme.ts`的初始化，使元素重新绑定点击事件。
  - 阅读源码，发现`colorScheme.ts`被`main.ts`引用，在`main.ts`中执行了初始化，并且`main.ts`生成了个全局变量 **Stack**
  - 所以在PJAX执行完后，使用全局变量 **Stack** ，执行里面的初始化方法，重新执行一遍脚本，来绑定点击事件
  - 阅读PJAX文档，发现官方也提供了PJAX执行完后的事件，我们执行监听这个事件，**Stack** 执行初始化就好 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E4%BF%AE%E5%A4%8D3.png) ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E4%B8%BB%E9%A2%98%E5%88%87%E6%8D%A2%E4%BF%AE%E5%A4%8D4.png)

```html
<!-- 【custom.html】 -->
...
<script>
    ...
    document.addEventListener('pjax:complete', () => {
        // Stack脚本初始化
        window.Stack.init();
    })
</script>
```

***这样元素的点击事件重新绑定，主题颜色就能正常切换了***

------

#### 文章搜索修复

- （1）使用文章搜索功能时，输入关键词，无任何搜索记录，搜索功能失效 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E6%90%9C%E7%B4%A2%E4%BF%AE%E5%A4%8D1.png)
- （2）查看`layouts\ts\search.tsx`文件，发现情况和上面的`colorScheme.ts`类似，存在绑定事件 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E6%90%9C%E7%B4%A2%E4%BF%AE%E5%A4%8D2.png)
- （3）解决思路类似上面的，把 **search.tsx** 初始化内容，封装为一个函数，并把函数 **export** 出来，由 **main.ts** 引入这个函数，并放到 **Stack.init()** 的方法中，利用此方法来重新初始化搜索脚本 - ***（以下操作请复制同路径同名文件到自己主目录下修改，不要在主题源码中修改）*** ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E6%90%9C%E7%B4%A2%E4%BF%AE%E5%A4%8D3.png)

```html
/** 
 * 【search.tsx】
 * 记得把window.addEventListener('load' ...这部分代码注释掉
 * 初始化工作交给Stack.init()处理了，不需要这个了
 */  
...
function searchInit() {
    let search = document.querySelector('.search-result');
    if (search) {
        const searchForm = document.querySelector('.search-form') as HTMLFormElement,
            searchInput = searchForm.querySelector('input') as HTMLInputElement,
            searchResultList = document.querySelector('.search-result--list') as HTMLDivElement,
            searchResultTitle = document.querySelector('.search-result--title') as HTMLHeadingElement;

        new Search({
            form: searchForm,
            input: searchInput,
            list: searchResultList,
            resultTitle: searchResultTitle,
            resultTitleTemplate: window.searchResultTitleTemplate
        });
    }
}

export {
    searchInit
}
```

- （4）**tsx** 类型的文件引入方式有点特殊，需要我们修改以下 **main.ts** 的引入方式，修改`layouts\partials\footer\components\script.html`，改法参考`layouts\page\search.html`，把`"JSXFactory" "createElement"`补充上就好 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E6%90%9C%E7%B4%A2%E4%BF%AE%E5%A4%8D4.png) ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E6%90%9C%E7%B4%A2%E4%BF%AE%E5%A4%8D5.png)



[![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E6%90%9C%E7%B4%A2%E4%BF%AE%E5%A4%8D6.png)](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-引入音乐播放放器并实现音乐不中断功能/文章搜索修复6.png)



***这样search.tsx能正常初始化，文章搜索功能恢复***

------

#### 搜索内容跳转修复

- （1）虽然文章搜索已经修复了，但搜索出来的内容并没有被PJAX识别到，导致PJAX没有拦截，进而导致页面刷新了
- （2）阅读 **search.tsx** 源码可知，搜索内容的数据，是通过React.render，动态渲染回页面的，这些没有触发PJAX，所以PJAX没有识别到。阅读PJAX文档，官方提供给了我们重新解析数的方法，所以在React.render之后，我调用PJAX方法，重新解析页面即可 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%90%9C%E7%B4%A2%E5%86%85%E5%AE%B9%E8%B7%B3%E8%BD%AC%E4%BF%AE%E5%A4%8D1.png)

```tsx
/**
 * 【search.tsx】
 */
private async doSearch(keywords: string[]) {
    ...
    /* 
    方法末尾，让pjax重新解析文档数据，识别动态渲染的数据
    虽然当前文件没有pjax对象，但最后静态页面会生成一个整体的js文件
    pjax对象那时就能识别到，就可成功调用
    */
    pjax.refresh(document);
}
```

***这样动态渲染出的页面数据就能被PJAX识别到，就不会刷新页面跳转了***

------

#### 文章评论修复

- （1）如果博客有开启评论功能的话，会发现评论的脚本不生效了
- （2）由于每个人使用的评论种类各不相同，所以这边也是简单说一下。PJAX官方文档也有说如何处理：
  - 用一个`<div class=js-Pjax></div>`来包裹一个`<script></script>`
  - 在`<script>`标签中，通过`document.createElement('script')`形式，创建对应的评论脚本内容
  - pjax对象通过 **.js-Pjax** 被识别到此内容，进行脚本执行 ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E6%96%87%E7%AB%A0%E8%AF%84%E8%AE%BA%E4%BF%AE%E5%A4%8D1.png)

```html
<!-- custom.html -->
...
<script>
    var pjax = new Pjax({
        selectors: [
            ...
            ".js-Pjax"
        ]
    })
</script>
<div class="js-Pjax">
    <script>
        // TODO: 判断当前是否是文档，且是否开启评论功能
        (function() {
            let script = document.createElement('script');
            // 将对应评论的脚本内容填进去
             script.setAttribute('key', 'value');
            ...
            // 寻找合适的元素，添加脚本进去
            document.querySelector('xxx').appendchild(script)
        })(document)
    </script>
<div>
```

***这样PJAX会自动加载我们的评论脚本***

------

## 引入进度条

- （1）由于使用了PJAX后，无法得知页面的加载情况是否完成，所以引入一个伪进度条，来显示页面内容进度
- （2）前往【[topbar](https://buunguyen.github.io/topbar/)】，点击下载zip包，将解压后的 **topbar.min.js** 放到`assets\js\topbar.min.js` ![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E5%BC%95%E5%85%A5%E8%BF%9B%E5%BA%A6%E6%9D%A11.png)
- （3）通过监听PJAX两个事件 **pjax:send** 和 **pjax:complete** 实现伪进度条

```html
<!--custom.html-->
...
{{ with resources.Get "js/topbar.min.js" }}
    <!-- 引入本地JS脚本 -->
    <script src={{ .Permalink }}></script>
{{ end }}
<script>
    // 修改进度条颜色
    topbar.config({
        barColors: {
            '0': 'rgba(255,  255, 255, 1)', // 进度0%白色
            '1.0': 'rgba(0, 149, 234,  1)' // 进度100%蓝色
        }
    })
	
    document.addEventListener('pjax:send', () => {
        // 显示顶部进度条
        topbar.show();
    })
	
    document.addEventListener('pjax:complete', () => {
        ....
        // 隐藏顶部进度条
        topbar.hide();
    })
</script>
```

[![img](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-%E5%BC%95%E5%85%A5%E9%9F%B3%E4%B9%90%E6%92%AD%E6%94%BE%E6%94%BE%E5%99%A8%E5%B9%B6%E5%AE%9E%E7%8E%B0%E9%9F%B3%E4%B9%90%E4%B8%8D%E4%B8%AD%E6%96%AD%E5%8A%9F%E8%83%BD/%E5%BC%95%E5%85%A5%E8%BF%9B%E5%BA%A6%E6%9D%A12.png)](https://letere-gzj.github.io/hugo-stack/p/hugoaplayer--pjax-引入音乐播放放器并实现音乐不中断功能/引入进度条2.png)



***这样伪进度条就成功引入了，能大概知道页面的加载情况了***



## 配置页面项目

官方文档：https://stack.jimmycai.com/config/menu

如果添加的是自己博客的一个页面，可以在这个页面的 md 文件的 Front Matter 中添加以下内容：

```yaml
menu: 
    main:
        name:                # 名称
        weight: -90
        params:
            icon:            # 图标名称
```

如果不是博客的一个页面，那么在 theme文件夹的config.yaml 中的 menu 部分下添加以下代码：

```yaml
menu:
    main:
        - identifier: 
          name: 
          url: 
          weight: 
          params:
            icon: 
            newTab: 
```

> `identifier`: 项目名
>
> `name`: 显示名称
>
> `url`: 链接
>
> `weight`: 优先级，数值越低优先级越大
>
> params:	
>
> `icon`: 自定义图标
>
> `newTab`: 是否在新标签页打开

> icon图标存放在`根目录\assets\icons`文件夹下



## 全局配置

###  零碎玩意

在 `/assets/scss/custom.scss` 中加入以下代码：

```scss
// 页面基本配色
:root {
  // 全局顶部边距
  --main-top-padding: 30px;
  // 全局卡片圆角
  --card-border-radius: 25px;
  // 标签云卡片圆角
  --tag-border-radius: 8px;
  // 卡片间距
  --section-separation: 40px;
  // 全局字体大小
  --article-font-size: 1.8rem;
  // 行内代码背景色
  --code-background-color: #f8f8f8;
  // 行内代码前景色
  --code-text-color: #e96900;
  // 暗色模式下样式
  &[data-scheme="dark"] {
    // 行内代码背景色
    --code-background-color: #ff6d1b17;
    // 行内代码前景色
    --code-text-color: #e96900;
  }
}

//------------------------------------------------------
// 修复引用块内容窄页面显示问题
a {
  word-break: break-all;
}

code {
  word-break: break-all;
}

//---------------------------------------------------
// 文章内容图片圆角阴影
.article-page .main-article .article-content {
  img {
    max-width: 96% !important;
    height: auto !important;
    border-radius: 8px;
  }
}

//------------------------------------------------
// 文章内容引用块样式
.article-content {
  blockquote {
    border-left: 6px solid #358b9a1f !important;
    background: #3a97431f;
  }
}
// ---------------------------------------
// 代码块基础样式修改
.highlight {
  max-width: 102% !important;
  background-color: var(--pre-background-color);
  padding: var(--card-padding);
  position: relative;
  border-radius: 20px;
  margin-left: -7px !important;
  margin-right: -12px;
  box-shadow: var(--shadow-l1) !important;

  &:hover {
    .copyCodeButton {
      opacity: 1;
    }
  }

  // keep Codeblocks LTR
  [dir="rtl"] & {
    direction: ltr;
  }

  pre {
    margin: initial;
    padding: 0;
    margin: 0;
    width: auto;
  }
}

// light模式下的代码块样式调整
[data-scheme="light"] .article-content .highlight {
  background-color: #fff9f3;
}

[data-scheme="light"] .chroma {
  color: #ff6f00;
  background-color: #fff9f3cc;
}

//-------------------------------------------
// 设置选中字体的区域背景颜色
//修改选中颜色
::selection {
  color: #fff;
  background: #34495e;
}

a {
  text-decoration: none;
  color: var(--accent-color);

  &:hover {
    color: var(--accent-color-darker);
  }

  &.link {
    color: #4288b9ad;
    font-weight: 600;
    padding: 0 2px;
    text-decoration: none;
    cursor: pointer;

    &:hover {
      text-decoration: underline;
    }
  }
}

//-------------------------------------------------
//文章封面高度更改
.article-list article .article-image img {
  width: 100%;
  height: 150px;
  object-fit: cover;

  @include respond(md) {
    height: 200px;
  }

  @include respond(xl) {
    height: 305px;
  }
}

//---------------------------------------------------
// 全局页面布局间距调整
.main-container {
  min-height: 100vh;
  align-items: flex-start;
  padding: 0 15px;
  gap: var(--section-separation);
  padding-top: var(--main-top-padding);

  @include respond(md) {
    padding: 0 37px;
  }
}

//--------------------------------------------------
//页面三栏宽度调整
.container {
  margin-left: auto;
  margin-right: auto;

  .left-sidebar {
    order: -3;
    max-width: var(--left-sidebar-max-width);
  }

  .right-sidebar {
    order: -1;
    max-width: var(--right-sidebar-max-width);

    /// Display right sidebar when min-width: lg
    @include respond(lg) {
      display: flex;
    }
  }

  &.extended {
    @include respond(md) {
      max-width: 1024px;
      --left-sidebar-max-width: 25%;
      --right-sidebar-max-width: 22% !important;
    }

    @include respond(lg) {
      max-width: 1280px;
      --left-sidebar-max-width: 20%;
      --right-sidebar-max-width: 30%;
    }

    @include respond(xl) {
      max-width: 1453px; //1536px;
      --left-sidebar-max-width: 15%;
      --right-sidebar-max-width: 25%;
    }
  }

  &.compact {
    @include respond(md) {
      --left-sidebar-max-width: 25%;
      max-width: 768px;
    }

    @include respond(lg) {
      max-width: 1024px;
      --left-sidebar-max-width: 20%;
    }

    @include respond(xl) {
      max-width: 1280px;
    }
  }
}

//-------------------------------------------------------
//全局页面小图片样式微调
.article-list--compact article .article-image img {
  width: var(--image-size);
  height: var(--image-size);
  object-fit: cover;
  border-radius: 17%;
}
```

### 菜单栏圆角

在 `/assets/scss/custom.scss` 中加入以下代码：

```scss
// 菜单栏样式
// 下拉菜单改圆角样式
.menu {
  padding-left: 0;
  list-style: none;
  flex-direction: column;
  overflow-x: hidden;
  overflow-y: scroll;
  flex-grow: 1;
  font-size: 1.6rem;
  background-color: var(--card-background);

  box-shadow: var(--shadow-l2); //改个阴影
  display: none;
  margin: 0; //改为0
  border-radius: 10px; //加个圆角
  padding: 30px 30px;

  @include respond(xl) {
    padding: 15px 0;
  }

  &,
  .menu-bottom-section {
    gap: 30px;

    @include respond(xl) {
      gap: 25px;
    }
  }

  &.show {
    display: flex;
  }

  @include respond(md) {
    align-items: flex-end;
    display: flex;
    background-color: transparent;
    padding: 0;
    box-shadow: none;
    margin: 0;
  }

  li {
    position: relative;
    vertical-align: middle;
    padding: 0;

    @include respond(md) {
      width: 100%;
    }

    svg {
      stroke-width: 1.33;

      width: 20px;
      height: 20px;
    }

    a {
      height: 100%;
      display: inline-flex;
      align-items: center;
      color: var(--body-text-color);
      gap: var(--menu-icon-separation);
    }

    span {
      flex: 1;
    }

    &.current {
      a {
        color: var(--accent-color);
        font-weight: bold;
      }
    }
  }
}
```

###  滚动条

在 `/assets/scss/custom.scss` 中加入以下代码：

```scss
//将滚动条修改为圆角样式
//菜单滚动条美化
.menu::-webkit-scrollbar {
  display: none;
}

// 全局滚动条美化
html {
  ::-webkit-scrollbar {
    width: 20px;
  }

  ::-webkit-scrollbar-track {
    background-color: transparent;
  }

  ::-webkit-scrollbar-thumb {
    background-color: #d6dee1;
    border-radius: 20px;
    border: 6px solid transparent;
    background-clip: content-box;
  }

  ::-webkit-scrollbar-thumb:hover {
    background-color: #a8bbbf;
  }
}
```

###  加载进度条

在 `/layouts/partials/footer/custom.html` 中加入以下代码：

```html
<script src="https://npm.elemecdn.com/nprogress@0.2.0/nprogress.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://npm.elemecdn.com/nprogress@0.2.0/nprogress.css" crossorigin="anonymous" />
<script>
    NProgress.start();
    document.addEventListener("readystatechange", () => {
        if (document.readyState === "interactive") NProgress.inc(0.8);
        if (document.readyState === "complete") NProgress.done();
    });
</script>
```

###  深色模式开关图标

不太喜欢默认图标，默认图标不够直观，当然你可以不换
我选取了 [Tabler Icons](https://tablericons.com/) 中的 `sun-high` 和 `moon-stars` 这两个图标
在 `/assets/scss/partials/sidebar.scss` 第 154~180 行中将图标名进行修改，最终效果：

```scss
/* .\assets\scss\partials\sidebar.scss Line 154*/
[data-scheme="dark"] {
    #dark-mode-toggle {
        color: var(--accent-color);
        font-weight: 700;

        .icon-tabler-sun-high {
            display: none;
        }

        .icon-tabler-moon-stars {
            display: unset;
        }
    }
}

#dark-mode-toggle {
    margin-top: auto;
    color: var(--body-text-color);
    display: flex;
    align-items: center;
    cursor: pointer;
    gap: var(--menu-icon-separation);

    .icon-tabler-moon-stars {
        display: none;
    }
}
```

在 `/layouts/partials/sidebar/left.html` 第 91~97 行中将图标名进行修改，最终效果：

```html
<!---.\layouts\partials\sidebar\left.html Line 91--->
{{ if (default false .Site.Params.colorScheme.toggle) }}
  <li id="dark-mode-toggle">
    {{ partial "helper/icon" "sun-high" }}
    {{ partial "helper/icon" "moon-stars" }}
    <span>{{ T "darkMode" }}</span>
  </li>
{{ end }}
```

### 修改布局

在 `/assets/scss/grid.scss` 中修改 `left-sidebar` 和 `right-sidebar` 的描述：

```scss
    .left-sidebar {
        order: -3;
        // max-width: var(--left-sidebar-max-width);
        max-width: 10%;
    }

    .right-sidebar {
        order: -1;
        // max-width: var(--right-sidebar-max-width);
        max-width: 20%;

        /// Display right sidebar when min-width: lg
        @include respond(lg) {
            display: flex;
        }
    }
```

把正文的占比改到了 70%, 原来的只有 50% 左右

## 页面

### 显示语言和复制按钮

在 `/assets/ts/custom.ts` 中加入以下代码：

```tsx
// 显示语言和复制按钮
const highlights = document.querySelectorAll('.article-content div.highlight');
        const copyText = `📄拷贝`,
            copiedText = `已拷贝!`;

        highlights.forEach(highlight => {
            const copyButton = document.createElement('button');
            copyButton.innerHTML = copyText;
            copyButton.classList.add('copyCodeButton');
            highlight.appendChild(copyButton);

            const codeBlock = highlight.querySelector('code[data-lang]');
            // 获取语言
            const lang = codeBlock.getAttribute('data-lang');
            if (!codeBlock) return;

            copyButton.addEventListener('click', () => {
                navigator.clipboard.writeText(codeBlock.textContent)
                    .then(() => {
                        copyButton.textContent = copiedText;

                        setTimeout(() => {
                            copyButton.textContent = copyText;
                        }, 1000);
                    })
                    .catch(err => {
                        alert(err)
                        console.log('Something went wrong', err);
                    });
            });

            // Add language code button
            const languageButton = document.createElement('button');
            languageButton.innerHTML = lang.toUpperCase()+'&nbsp;&nbsp;';
            languageButton.classList.add('languageCodeButton');
            
            highlight.appendChild(languageButton);
        });

        new StackColorScheme(document.getElementById('dark-mode-toggle'));
```

在 `/assets/scss/custom.scss` 中加入以下代码（可按需自行修改）：

```scss
//代码复制按钮
.article-content .copyCodeButton {
  position: absolute;
  top: 10px;
  right: 18px;
  border-radius: 12px;
  opacity: 1;
  color: #ffffffad;
  background: none;
  border: none;
  padding: 0;
  font-weight: 500;
}

.article-content .languageCodeButton {
  position: absolute;
  border: none;
  top: 9px;
  right: 69px;
  border-radius: 12px;
  opacity: 1;
  padding: 0 5px;
  background: 0;
  color: #ffffffad;
  font-family: lato;
  font-size: 1.5rem;
}
```

### 首页欢迎横幅

在 `/layouts/index.html` 的 `<section class="article-list">` 前添加以下代码：

```html
<!-- 首页欢迎字幅 -->
<div class="welcome">
  <p style="font-size: 2rem; text-align: center; font-weight: bold">
    <span class="shake">👋</span>
    <span class="jump-text1" > Welcome</span>
    <span class="jump-text2"> To </span>
    <span class="jump-text3" style="color:#e99312">Xa</span><span class="jump-text4" style="color:#e99312">l</span><span class="jump-text5" style="color:#e99312">a</span><span class="jump-text6" style="color:#e99312">o</span><span class="jump-text7" style="color:#e99312">k</span><span class="jump-text8" style="color:#e99312">'s</span>
    <span class="jump-text9" style="color:#e99312">Blog</span>
  </p>
</div>
<!-- 首页欢迎字幅 -->
```

在 `/assets/scss/custom.scss` 中加入以下代码：

```scss
//首页欢迎板块样式
.welcome {
  color: var(--card-text-color-main);
  background: var(--card-background);
  box-shadow: var(--shadow-l2);
  border-radius: 30px;
  display: inline-block;
}

// 👋emoji实现摆动效果
.shake {
  display: inline-block;
  animation: shake 1s;
  animation-duration: 1s;
  animation-timing-function: ease;
  animation-delay: 0s;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: none;
  animation-play-state: running;
  animation-name: shake;
  animation-timeline: auto;
  animation-range-start: normal;
  animation-range-end: normal;
  animation-delay: 2s;
  @keyframes shake {
    0% {
      transform: rotate(0);
    }
    25% {
      transform: rotate(45deg) scale(1.2);
    }
    50% {
      transform: rotate(0) scale(1.2);
    }
    75% {
      transform: rotate(45deg) scale(1.2);
    }
    100% {
      transform: rotate(0);
    }
  }
}
// 实现字符跳动动画
.jump-text1 {
  display: inline-block;
  animation: jump 0.5s 1;
}

.jump-text2 {
  display: inline-block;
  animation: jump 0.5s 1;
  animation-delay: 0.1s;
}

.jump-text3 {
  display: inline-block;
  animation: jump 0.5s 1;
  animation-delay: 0.2s;
}

.jump-text4 {
  display: inline-block;
  animation: jump 0.5s 1;
  animation-delay: 0.3s;
}

.jump-text5 {
  display: inline-block;
  animation: jump 0.5s 1;
  animation-delay: 0.4s;
}

.jump-text6 {
  display: inline-block;
  animation: jump 0.5s 1;
  animation-delay: 0.5s;
}

.jump-text7 {
  display: inline-block;
  animation: jump 0.5s 1;
  animation-delay: 0.6s;
}

.jump-text8 {
  display: inline-block;
  animation: jump 0.5s 1;
  animation-delay: 0.7s;
}

.jump-text9 {
  display: inline-block;
  animation: jump 0.5s 1;
  animation-delay: 0.9s;
}

@keyframes jump {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-20px);
  }
  100% {
    transform: translateY(0);
  }
}
```



## 网站的搜索引擎优化（SEO）

本网站使用Hugo搭建，而且使用的stack主题支持自动生成基于Open Graph协议（OG协议）的标签，此处记录一下如何在Hugo搭建的网站中做搜索引擎优化（SEO）。

### 引言

到本系列的[第三篇文章](https://jinli.io/p/个人网站的建立过程三hugo主题stack的使用与优化/)为止，我们已经建立了一个可以依靠域名和HTTPS协议访问的中英双语个人网站。但是，如果我们试图在搜索引擎上搜索我们的网站，就会发现我们网站上的什么内容都不会出现在搜索引擎上。

这是因为我们的网站并没有被搜索引擎收录，也没有与外界的网站建立连接。因此，想要让其他人能在搜索引擎上发现我们的网站，我们就需要将网站提交给搜索引擎。同时，也可以做一些搜索引擎优化（SEO），以让搜索引擎获得关于我们网站的更详细的信息，这有助于提升我们的网站在搜索引擎中的排名。

### 开启Open Graph

Open Graph（开放图谱）协议，简称OG协议，是Facebook在2010年公布的一项协议，用来标记网页内容。简单来讲，OG协议就是嵌在网页头部的一些标签，这些标签标记了网页的标题、描述等特征，使得网页成为一个“富媒体对象”，可以被其他社交网站引用。

很多搜索引擎都支持OG协议，在网页中使用OG协议的标签，就更有利于提升我们的网页在搜索引擎中的排名。

OG协议的标签在网页中通常表示为类似下面所示的格式：

```
<meta property="og:title" content="The Rock" />
<meta property="og:type" content="video.movie" />
<meta property="og:url" content="https://www.imdb.com/title/tt0117500/" />
<meta property="og:image" content="https://ia.media-imdb.com/images/rock.jpg" />
```

stack主题提供了对OG协议的支持，只需要在网站根目录下的`config/_default/params.en.yaml`和`config/_default/params.zh-cn.yaml`配置文件中开启即可：

```yaml
opengraph:
    twitter:
        # Your Twitter username
        site: JinliCyou

        # Available values: summary, summary_large_image
        card: summary_large_image
```

这样，Hugo在生成和部署网站时就会在网页HTML文件中自动嵌入OG标签。

### 谷歌搜索优化

在将我们的站点信息提交给谷歌时，谷歌需要验证我们对网站的所有权。验证方式有好几种，例如

- 在网站根目录下放一个谷歌生成的验证文件
- 在网页HTML文件头部嵌入谷歌生成的特定标签
- 使用谷歌分析的Tracking ID（或者Measurement ID）

由于stack主题集成了对谷歌分析的支持，这里我们使用第三种验证方式。

#### 开启谷歌分析

[谷歌分析](https://analytics.google.com/analytics/web/provision/#/provision)（Google Analytics）是一个分析网站流量的工具，用它可以统计网站的访问量等信息。

1. 首先前往谷歌分析官网注册谷歌分析的账号，也可以直接用已有的谷歌账号登录。现在的谷歌分析一般是谷歌分析4（Google Analytics 4），使用Measurement ID而非之前的Tracking ID来跟踪网站。
2. 获取Measurement ID。具体可参见谷歌分析的[帮助文档](https://support.google.com/analytics/answer/9539598)。下面是具体操作：
   - 在用户首页找到“Admin“选项，新建一个“Property”，按照说明填入必要的信息。
   - 然后点击“Property”这一列中的“Data Streams”选项。
   - 点击“Add stream”，选择“Web”，填入你的网站域名和网站名字。
   - 再在“Property”页面点击刚添加的stream，就能看到一个以“G-”开头的Measurement ID。记录下你的网站的Measurement ID。
3. 在网站根目录下的`config/_default/config.yaml`配置文件中找到“googleAnalytics"配置项，填入你的Measurement ID。

```
# GA Tracking ID
googleAnalytics: G-measuremntID
```

#### 提交站点地图

站点地图（Site Map）是一个存储有站点网页信息的XML数据文件，通常命名为`sitemap.xml`，将它提交给搜索引擎，搜索引擎将可以获取我们网站的网页信息。

Hugo会在生成和部署网站时在`public`文件夹下自动生成`sitemap.xml`文件。

我们把站点地图提交到谷歌搜索，具体说明可参见谷歌站长页面的[说明](https://developers.google.com/search/docs/beginner/get-started)，下面是具体操作：

1. 登录谷歌搜索控制台（Google Search Console）https://search.google.com/search-console，可以使用在谷歌分析注册的账号。

2. 点击左上角的“Add property”，选择右侧的“URL prefix”方式，输入以https开头的网站网址。在验证所有权的选项中选择“Google Analytics”，点击验证。如果你在上一步开启谷歌分析后使用Hugo重新部署了网站的话，就可以直接验证通过。

3. 提交站点地图文件`sitemap.xml`。在左侧菜单栏点击“Sitemaps”选项，然后在添加站点地图的页面填入`sitemap.xml`所在的URL。例如对于本站，由于是双语站点，Hugo在部署网站时会生成3个`sitemap.xml`文件，分别是`/public/sitemap.xml`、`/public/zh-cn/sitemap.xml`以及`/public/en/sitemap.xml`。

   注意，添加sitemap时不要漏了路径开头的斜杠`/`，即使网站域名后面已经有一个斜杠了，也不能省略。如下图所示 ![添加站点地图文件sitemap.xml](https://jinli.io/p/%E4%B8%AA%E4%BA%BA%E7%BD%91%E7%AB%99%E7%9A%84%E5%BB%BA%E7%AB%8B%E8%BF%87%E7%A8%8B%E5%9B%9B%E7%BD%91%E7%AB%99%E7%9A%84%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E%E4%BC%98%E5%8C%96seo/sitemap.png)

   提交成功之后“status”会显示“success”。

   Hugo生成的3个站点地图中，`/public/sitemap.xml`中的内容其实是指向`/public/zh-cn/sitemap.xml`和`/public/en/sitemap.xml`的，所以我们只提交一个`/public/sitemap.xml`就可以。

一般在站点地图成功提交之后大约1到2天后，就可以看到自己的网站已经被谷歌收录了。可以在谷歌搜索框中输入`site:xxx.com`来查看某个网站是否被谷歌搜索收录。

### 百度搜索优化

针对百度搜索的优化是在[百度资源搜索平台](https://ziyuan.baidu.com/)上完成的。

1. 前往[百度资源搜索平台](https://ziyuan.baidu.com/)，登录百度账号。

2. 点击“链接提交”，然后点击"添加站点"。输入你的网站域名，同样需要验证站点的所有权，这里选择下载验证文件，然后把验证文件放在网站`public`文件夹内。点击“验证”即可。

3. 然后点击左侧菜单栏“资源提交”中的“普通收录”，在资源提交的页面下选择“sitemap”，输入`sitemap.xml`所在的URL就可以了。

   不过在百度提交sitemap有两个限制：

   - 不允许提交索引型sitemap
   - 对新账号每天只允许提交一个sitemap文件