关于使用hugo搭建博客过程中,遇到的一些小问题的解决方案…

该文档基于 hugo@0.124.0 版本

官方使用文档

前言:安装配置Hugo

1、Homebrew

MacOS系统

$ brew install hugo
$ hugo version

2、下载指定版本的二进制文件

MacOS系统

注意:此方法适用于当你的包管理工具(如:Homebrew)升级过高,无法安装hugo历史版本,而你的本地项目又依赖旧版本时

实例背景:Blogs项目中的模板语法依赖hugo@0.124.0,在我升级完homebrew后,主动升级了hugo@0.136.0,导致大量语法冲突,项目无法启动。需要下载官方的二进制文件,安装指定版本的 Hugo。

  • i. 前往 Hugo Releases 页面。

  • ii. 找到你需要的版本,下载与 macOS 兼容的二进制文件(hugo_extended_X.X.X_darwin-amd64.tar.gzhugo_extended_X.X.X_darwin-universal.tar.gz)。

    • darwin 表示适用于 macOS 系统,而 universal 则意味着它支持在 Intel 和 Apple Silicon(M1/M2)架构的 Mac 上运行
  • iii. 解压缩文件,将 hugo 二进制文件复制到 /usr/local/bin/ 目录中(或其他包含在 PATH 的目录),以便可以直接运行 hugo 命令:

    # 解压,生成一个hugo可执行文件
    $ tar -xzf hugo_extended_X.X.X_darwin-universal.tar.gz
    
    # 将 hugo 可执行文件复制到 /usr/local/bin/ 目录中(或其他包含在 PATH 的目录),以便可以直接运行 hugo 命令
    $ sudo mv hugo /usr/local/bin/
    
    # 运行hugo命令,确认是否安装成功
    $ hugo version
    

这样便可在 macOS 上使用指定版本的 Hugo 了。


一、配置项

1、高亮问题

关键步骤

将下载解压的 highlight 文件夹放置于 static/js/

  • highlight.pack.js 决定那些语言的代码会根据语法进行高亮
  • style文件夹中选择喜欢的css样式文件,css文件决定代码高亮的颜色

最后我选择了 agate.css

然后需要在<head>中增加:

// 主题可根据列表选择 - agate
<link rel="stylesheet" href="/js/highlight/styles/agate.css">

<body>结束前增加:

<script src="/js/highlight/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

2、菜单问题

增加权重

3、目录(toc)问题

描述:最新版似乎有个bug:文章目录无法支持3级标题以下的显示 #240

Hugo (>=0.60.0) uses Goldmark as default renderer. You may add the following settings to your config.toml.

[markup]
  [markup.tableOfContents]
    endLevel = 3 # 最小标题
    ordered = false
    startLevel = 2 # 最大标题

See https://gohugo.io/getting-started/configuration-markup/#table-of-contents

二、语法问题

  • 基本布局页:layouts/_default/baseof.html
  • 个性化配置页:layouts/partials/head.html、layouts/partials/zhihu.svg

2、锚点:“back to top”按钮

个性化配置页 —— layouts/partials/footer.html页面嵌入如下代码:

<script src="https://unpkg.com/vanilla-back-to-top@7.1.14/dist/vanilla-back-to-top.min.js"></script>
<script>
  addBackToTop({
    diameter: 56,
    backgroundColor: 'rgb(255, 82, 82)',
    textColor: '#fff'
  })
</script>

3、站内搜索

  • DuckDuckGo search
  • google search
  • 商业解决方案 Algolia
  • 手动Coding解决
  • golang 暴力解法

手动Coding解决核心思想
(1) 搜索框触发 search function

function search () {
  $.getJSON(baseUrl + 'search.json', function(result){
    // 拿到搜索结果
  })
}

(2) search.json

{ "results": [
    {
    "url": "http://example.com/article3/",
    "title": "Article3",
    "content": "word3a word3b word3c"
    },
    {
    "url": "http://example.com/article2/",
    "title": "Article2",
    "content": "word2a word2b word2c"
    },
    {
    "url": "http://example.com/article1/",
    "title": "Article1",
    "content": "word1a word1b word1c"
    }
]}

(3) 需要考虑的点

  • 如何根据现有文章,执行HUGO命令,自动生成 search.json 文件
  • 拿到搜索结果,以什么形式展示搜索拿到的文章列表(新的search page 或 下拉面板)

golang 暴力解法思路:

  • 利用hugo生成的index.xml文件进行内容搜索
  • hugo的content目录下的html文件会被编译到模板中去
  • 利用以上特性使用js实现功能
    • html 转文本
    • 时间格式转换

实现代码

+++
title = "搜索"
menu = "main"
weight = 30
+++

<style>
  /* 手机适配 */
  @media screen and (max-width: 500px) {
     .search{
       padding-right: 25px;
     }

     .search input{
       width: 100%;
     }

     .search button{
       display: none;
     }
  }
  /* 电脑适配 */
  @media screen and (min-width: 500px) {
      .search{
        width: 500px;
      }

      .search input{
        width: 444px;
      }
  }
  /*
    * @desc: 搜索页屏蔽page信息
    * 1、头部 作者&分类信息
    * 2、底部作者信息
    */
  .post__meta, .authorbox {
    display: none;
  }

  /* 通用样式 */
  .search{
    margin: auto;
  }

  .search input{
    outline: none;
    border: 1.5px solid #62a1d5;
    height: 32px;
    padding: 10px;
  }
  .search button{
    outline: none;
    border: 0px;
    height: 32px;
    width: 36px;
    position:absolute;
    background-color:#62a1d5;
    margin-left: 0.8rem;
  }
  .search .icon{
    width: 26px;
    height: 26px;
  }
  .post__header .post__title {
    display: none;
  }
  .post-title a {
    color: #000;
  }
  .read-more {
    margin: 4px 0 8px;
    text-align: right;
    padding: 0 0.8rem 0;
  }
  .post {
    margin-bottom: 12px;
  }
  h3 {
    margin: 0.2rem 0 1rem;
  }
  .utterances {
    display: none;
  }
</style>
<div class="search">
  <input type="text" placeholder="请输入搜索内容..." id="search-key" />
  <button onclick="search()">
    <svg t="1583982313567" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1271"
      width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink">
      <defs>
        <style type="text/css"></style>
      </defs>
      <path d="M694.857143 475.428571q0-105.714286-75.142857-180.857142T438.857143 219.428571 258 294.571429 182.857143 475.428571t75.142857 180.857143T438.857143 731.428571t180.857143-75.142857T694.857143 475.428571z m292.571428 475.428572q0 29.714286-21.714285 51.428571t-51.428572 21.714286q-30.857143 0-51.428571-21.714286l-196-195.428571q-102.285714 70.857143-228 70.857143-81.714286 0-156.285714-31.714286t-128.571429-85.714286-85.714286-128.571428T36.571429 475.428571t31.714285-156.285714 85.714286-128.571428 128.571429-85.714286T438.857143 73.142857t156.285714 31.714286 128.571429 85.714286 85.714285 128.571428T841.142857 475.428571q0 125.714286-70.857143 228l196 196q21.142857 21.142857 21.142857 51.428572z"
        p-id="1272" fill="#ffffff"></path>
    </svg>
  </button>
</div>
<h1 id="search-tip" style="color: #c05b4d;text-align: center;display: none;">搜索中,请稍后 ...</h1>
<br />
<div id="result"></div>

<script type="text/javascript">
  // enter
  window.onload = function() {
    document.onkeydown = function(ev) {
      var event = ev || event
      if (event.keyCode == 13) {
        search()
      }
    }
  }

  // search
  function search() {
    key = document.getElementById("search-key").value;
    if (key === "") {
      return;
    }
    document.getElementById("search-key").value = "";

    // tip
    document.getElementById("search-tip").innerText = "搜索中,请稍后 ...";
    document.getElementById("search-tip").style.display = "block";

    // clear
    var el = document.getElementById('result');
    var childs = el.childNodes;
    for (var i = childs.length - 1; i >= 0; i--) {
      el.removeChild(childs[i]);
    }

    // xml
    xmltext = new XMLHttpRequest;
    xmltext.open("GET", "/index.xml", false);
    xmltext.send();
    resp = xmltext.responseXML;
    items = resp.getElementsByTagName("item");
    // search
    var i = 0;
    haveResult = false;
    while (i < items.length) {
      txt = items[i].getElementsByTagName("title")[0].innerHTML + items[i].getElementsByTagName("description")[0].innerHTML
      if (txt.indexOf(key) > -1) {
        haveResult = true;
        title = items[i].getElementsByTagName("title")[0].innerHTML;
        link = items[i].getElementsByTagName("link")[0].innerHTML;
        time = items[i].getElementsByTagName("pubDate")[0].innerHTML;
        mark = items[i].getElementsByTagName("description")[0].innerHTML;
        time = formatDate(time)
        mark = xmlToTextString(mark);
        addItem(title, link, time, mark)
      }
      i++;
    }
    if (!haveResult) {
      document.getElementById("search-tip").innerText = "搜索完毕,未发现结果 ...";
      document.getElementById("search-tip").style.display = "block";
    }
  }

  // add
  function addItem(title, link, time, mark) {
    document.getElementById("search-tip").style.display = "none";
    tmpl = "<article class=\"post\" style=\"border-bottom: 1px solid #e6e6e6;\" >" +
      "<header class=\"post-header\">" +
      "<h3 class=\"post-title\"><a class=\"post-link\" href=\"" + link + "\" target=\"_blank\">" + title + "</a></h3>" +
      "<div class=\"post-meta\">" +
      " <span class=\"post-time\">日期:" + time + "</span>" +
      "</div>" +
      " </header>" +
      "<div class=\"post-content\">" +
      "<div class=\"post-summary\">摘要:" + mark + "</div>" +
      "<div class=\"read-more\">" +
      "<a href=\"" + link + "\" class=\"read-more-link\" target=\"_blank\">阅读更多</a>" +
      "</div>" +
      " </div>" +
      "</article>"
    div = document.createElement("div")
    div.innerHTML = tmpl;
    document.getElementById('result').appendChild(div)
  }

  // html 转化 text()
  function xmlToTextString(xmlStr) {
    var htmlStr = xmlStr.replace(
      /&amp;/g, "&").replace(
      /&quot;/g, '"').replace(
      /&lt;/g, "<").replace(
      /&gt;/g, ">");
    var dd = htmlStr.replace(/<\/?.+?>/g, "");
    var dds= dd.replace(/ /g, "");
    return dds
  }

  // 1. 若小于10,前面加0
  function isZero(m){
    return m<10?'0'+m:m
  }
  
  // 将字符串转为Date格式,获取对应的年、月、日、时、分、秒。组合格式
  function formatDate(DateStr) {
    //时间戳是整数,否则要parseInt转换
    var time = new Date(DateStr); // 需要使用Date格式进行日期转化,若是时间戳、字符串时间,需要通过new Date(..)转化
    var y = time.getFullYear();
    var m = time.getMonth()+1;
    var d = time.getDate();
    return y+'年'+isZero(m)+'月'+isZero(d)+'日';    
  }
</script>

最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~

微信公众号

本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!