Hexo 博客主题开发
By WyyOIer |
2022-04-10 13:37:24 /
2022-07-25 13:03:37
nan

这是一项大工程。。。工程量预计 $\text{40 hours+}$

目标:创建一个主要为自己 Hexo 博客的主题 $\text{hexo-theme-MeaningLessNess}$ 并记录开发过程。

大部分的内容看起来会比较枯燥。。。不知道能不能坚持做下去

主题

主题 | Hexo

在创建之前,我们需要了解一个 $\text{Hexo}$ 主题 $\text{Theme}$ 文件夹的构成。

打开默认主题 $\text{\Blog\themes\landscape}$,找到四个文件夹和一个文件:

创建主题

在 $\text{\Blog\themes}$ 目录下创建一个名为 $\text{hexo-theme-example}$ 的文件夹,将上述部分先加入到主题文件夹中。

注意,后文的主题文件夹名为 $\text{hexo-theme-MeaningLessNess}$。

记得将主题配置文件中的主题改为制作的主题。

Hexo-博客主题开发-01.png

学习

学习一下 $\text{HTML},\text{CSS},\text{JavaScript}$ 等的使用,也可以边做边学。

空白主题的基本配置

我们先来简单的布置一些主站界面。

我们在 $\text{\hexo-theme-MeaningLessNess\layout}$ 路径放入三个文件:

在 $\text{\layout\index.ejs}$ 中,放入如下代码:


  
    
    
    Home
  
  
    

Hello World.

运行后可以得到:

Hexo-博客主题开发-02.png

对了,这里我发现不要用记事本去编辑中文内容,可能会产生乱码,于是我采用了编程软件编辑,果然能打印中文字体了。

现在我们需要使用一个 partial() 函数包含其它文件,这样能使我们更好的组织代码。

我们在 $\text{\layout}$ 目录下建立 $\text{_partial}$ 文件夹,用于存放一些部分的布局文件,这样我们可以在 $\text{\layout\index.ejs}$ 等文件中直接使用。

在 $\text{_partial}$ 文件夹中,我们可以放置如下文件:

比如我在 $\text{\layout_partial\header.eps}$ 中写入:

Welcome.

在 $\text{\layout\index.eps}$ 中稍作修改:


  
    
    
    WyyOIer
  
  
    <%- partial('_partial/header') %>
    

qwq

即可看到主站界面变为:

Hexo-博客主题开发-03.png

也就是在加载 $\text{indes.ejs}$ 的 <%- partial('_partial/header') %> 时,自动将 $\text{\layout_partial\header.ejs}$ 中的内容放置在代码中,对后续操作和检查十分方便,而 partial() 函数可以传参,具体操作和效果深入修改时再说。

设计

在配置主题之前,先进行一些小小的设计,让后续的编写更有目标性。

参考了一些官网的主题。 Themes | Hexo

正式制作

文章 $(\text{post})$

目录

主要希望实现显示文章的一级标题、二级标题、三级标题所组成的目录表格,并且点击后能够自动跳转到相应位置。

由于需要用到 $\text{JavaScript}$,借鉴了一下其他人的代码,但始终无效。

检查后发现,代码中有这样一句话:

var labelList = $("#article").children();

打开浏览器的检查页面,发现了如下报错信息:

Hexo-博客主题开发-06.png

百度发现后,需要引用 $\text{JavaScript}$ 的 $\text{jQuery}$ 库,可以从网上下载,也可直接使用在线文件。


再次刷新后发现已能正常显示。

代码块

在 $\text{\source\javascript}$ 目录下创建 $\text{codeBlocks}$ 文件夹,然后创建五个 $\text{javascript}$ 文件:

$\text{codeBlockFuction.js:}$

$(function () {
    $('pre').wrap('
'); });

$\text{codeLang.js:}$

$(function () {
  var $highlight_lang = $('
'); $('pre').after($highlight_lang); $('pre').each(function () { var code_language = $(this).attr('class'); if (!code_language) { return true; }; var lang_name = code_language.replace("line-numbers", "").trim().replace("language-", "").trim(); lang_name = lang_name.slice(0, 1).toUpperCase() + lang_name.slice(1); $(this).siblings(".code_lang").text(lang_name); }); });

$\text{codeCopy.js:}$

$(function () {
    var $copyIcon = $('');
    $('.code-area').prepend($copyIcon);
new ClipboardJS('.fa-copy', {
    target: function (trigger) {
        return trigger.nextElementSibling;
    }
});

});

$\text{clipboard.min.js:}$

! function (t, e) {
"object" == typeof exports && "object" == typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define([], e) : "object" == typeof exports ? exports.ClipboardJS = e() : t.ClipboardJS = e()
}(this, function () {
return function (n) {
    var o = {};

function r(t) {
    if (o[t]) return o[t].exports;
    var e = o[t] = {
        i: t,
        l: !1,
        exports: {}
    };
    return n[t].call(e.exports, e, e.exports, r), e.l = !0, e.exports
}
return r.m = n, r.c = o, r.d = function (t, e, n) {
    r.o(t, e) || Object.defineProperty(t, e, {
        enumerable: !0,
        get: n
    })
}, r.r = function (t) {
    "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, {
        value: "Module"
    }), Object.defineProperty(t, "__esModule", {
        value: !0
    })
}, r.t = function (e, t) {
    if (1 & t && (e = r(e)), 8 & t) return e;
    if (4 & t && "object" == typeof e && e && e.__esModule) return e;
    var n = Object.create(null);
    if (r.r(n), Object.defineProperty(n, "default", {
            enumerable: !0,
            value: e
        }), 2 & t && "string" != typeof e)
        for (var o in e) r.d(n, o, function (t) {
            return e[t]
        }.bind(null, o));
    return n
}, r.n = function (t) {
    var e = t && t.__esModule ? function () {
        return t.default
    } : function () {
        return t
    };
    return r.d(e, "a", e), e
}, r.o = function (t, e) {
    return Object.prototype.hasOwnProperty.call(t, e)
}, r.p = "", r(r.s = 0)

}([function (t, e, n) {
    "use strict";
    var r = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) {
            return typeof t
        } : function (t) {
            return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t
        },
        i = function () {
            function o(t, e) {
                for (var n = 0; n < e.length; n++) {
                    var o = e[n];
                    o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(t, o.key, o)
                }
            }
            return function (t, e, n) {
                return e && o(t.prototype, e), n && o(t, n), t
            }
        }(),
        a = o(n(1)),
        c = o(n(3)),
        u = o(n(4));

function o(t) {
    return t && t.__esModule ? t : {
        default: t
    }
}
var l = function (t) {
    function o(t, e) {
        ! function (t, e) {
            if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function")
        }(this, o);
        var n = function (t, e) {
            if (!t) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
            return !e || "object" != typeof e && "function" != typeof e ? t : e
        }(this, (o.__proto__ || Object.getPrototypeOf(o)).call(this));
        return n.resolveOptions(e), n.listenClick(t), n
    }
    return function (t, e) {
        if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function, not " + typeof e);
        t.prototype = Object.create(e && e.prototype, {
            constructor: {
                value: t,
                enumerable: !1,
                writable: !0,
                configurable: !0
            }
        }), e && (Object.setPrototypeOf ? Object.setPrototypeOf(t, e) : t.__proto__ = e)
    }(o, c.default), i(o, [{
        key: "resolveOptions",
        value: function () {
            var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {};
            this.action = "function" == typeof t.action ? t.action : this.defaultAction, this.target = "function" == typeof t.target ? t.target : this.defaultTarget, this.text = "function" == typeof t.text ? t.text : this.defaultText, this.container = "object" === r(t.container) ? t.container : document.body
        }
    }, {
        key: "listenClick",
        value: function (t) {
            var e = this;
            this.listener = (0, u.default)(t, "click", function (t) {
                return e.onClick(t)
            })
        }
    }, {
        key: "onClick",
        value: function (t) {
            var e = t.delegateTarget || t.currentTarget;
            this.clipboardAction && (this.clipboardAction = null), this.clipboardAction = new a.default({
                action: this.action(e),
                target: this.target(e),
                text: this.text(e),
                container: this.container,
                trigger: e,
                emitter: this
            })
        }
    }, {
        key: "defaultAction",
        value: function (t) {
            return s("action", t)
        }
    }, {
        key: "defaultTarget",
        value: function (t) {
            var e = s("target", t);
            if (e) return document.querySelector(e)
        }
    }, {
        key: "defaultText",
        value: function (t) {
            return s("text", t)
        }
    }, {
        key: "destroy",
        value: function () {
            this.listener.destroy(), this.clipboardAction && (this.clipboardAction.destroy(), this.clipboardAction = null)
        }
    }], [{
        key: "isSupported",
        value: function () {
            var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : ["copy", "cut"],
                e = "string" == typeof t ? [t] : t,
                n = !!document.queryCommandSupported;
            return e.forEach(function (t) {
                n = n && !!document.queryCommandSupported(t)
            }), n
        }
    }]), o
}();

function s(t, e) {
    var n = "data-clipboard-" + t;
    if (e.hasAttribute(n)) return e.getAttribute(n)
}
t.exports = l

}, function (t, e, n) {
    "use strict";
    var o, r = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) {
            return typeof t
        } : function (t) {
            return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t
        },
        i = function () {
            function o(t, e) {
                for (var n = 0; n < e.length; n++) {
                    var o = e[n];
                    o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(t, o.key, o)
                }
            }
            return function (t, e, n) {
                return e && o(t.prototype, e), n && o(t, n), t
            }
        }(),
        a = n(2),
        c = (o = a) && o.__esModule ? o : {
            default: o
        };
    var u = function () {
        function e(t) {
            ! function (t, e) {
                if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function")
            }(this, e), this.resolveOptions(t), this.initSelection()
        }
        return i(e, [{
            key: "resolveOptions",
            value: function () {
                var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {};
                this.action = t.action, this.container = t.container, this.emitter = t.emitter, this.target = t.target, this.text = t.text, this.trigger = t.trigger, this.selectedText = ""
            }
        }, {
            key: "initSelection",
            value: function () {
                this.text ? this.selectFake() : this.target && this.selectTarget()
            }
        }, {
            key: "selectFake",
            value: function () {
                var t = this,
                    e = "rtl" == document.documentElement.getAttribute("dir");
                this.removeFake(), this.fakeHandlerCallback = function () {
                    return t.removeFake()
                }, this.fakeHandler = this.container.addEventListener("click", this.fakeHandlerCallback) || !0, this.fakeElem = document.createElement("textarea"), this.fakeElem.style.fontSize = "12pt", this.fakeElem.style.border = "0", this.fakeElem.style.padding = "0", this.fakeElem.style.margin = "0", this.fakeElem.style.position = "absolute", this.fakeElem.style[e ? "right" : "left"] = "-9999px";
                var n = window.pageYOffset || document.documentElement.scrollTop;
                this.fakeElem.style.top = n + "px", this.fakeElem.setAttribute("readonly", ""), this.fakeElem.value = this.text, this.container.appendChild(this.fakeElem), this.selectedText = (0, c.default)(this.fakeElem), this.copyText()
            }
        }, {
            key: "removeFake",
            value: function () {
                this.fakeHandler && (this.container.removeEventListener("click", this.fakeHandlerCallback), this.fakeHandler = null, this.fakeHandlerCallback = null), this.fakeElem && (this.container.removeChild(this.fakeElem), this.fakeElem = null)
            }
        }, {
            key: "selectTarget",
            value: function () {
                this.selectedText = (0, c.default)(this.target), this.copyText()
            }
        }, {
            key: "copyText",
            value: function () {
                var e = void 0;
                try {
                    e = document.execCommand(this.action)
                } catch (t) {
                    e = !1
                }
                this.handleResult(e)
            }
        }, {
            key: "handleResult",
            value: function (t) {
                this.emitter.emit(t ? "success" : "error", {
                    action: this.action,
                    text: this.selectedText,
                    trigger: this.trigger,
                    clearSelection: this.clearSelection.bind(this)
                })
            }
        }, {
            key: "clearSelection",
            value: function () {
                this.trigger && this.trigger.focus(), window.getSelection().removeAllRanges()
            }
        }, {
            key: "destroy",
            value: function () {
                this.removeFake()
            }
        }, {
            key: "action",
            set: function () {
                var t = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : "copy";
                if (this._action = t, "copy" !== this._action && "cut" !== this._action) throw new Error('Invalid "action" value, use either "copy" or "cut"')
            },
            get: function () {
                return this._action
            }
        }, {
            key: "target",
            set: function (t) {
                if (void 0 !== t) {
                    if (!t || "object" !== (void 0 === t ? "undefined" : r(t)) || 1 !== t.nodeType) throw new Error('Invalid "target" value, use a valid Element');
                    if ("copy" === this.action && t.hasAttribute("disabled")) throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
                    if ("cut" === this.action && (t.hasAttribute("readonly") || t.hasAttribute("disabled"))) throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
                    this._target = t
                }
            },
            get: function () {
                return this._target
            }
        }]), e
    }();
    t.exports = u
}, function (t, e) {
    t.exports = function (t) {
        var e;
        if ("SELECT" === t.nodeName) t.focus(), e = t.value;
        else if ("INPUT" === t.nodeName || "TEXTAREA" === t.nodeName) {
            var n = t.hasAttribute("readonly");
            n || t.setAttribute("readonly", ""), t.select(), t.setSelectionRange(0, t.value.length), n || t.removeAttribute("readonly"), e = t.value
        } else {
            t.hasAttribute("contenteditable") && t.focus();
            var o = window.getSelection(),
                r = document.createRange();
            r.selectNodeContents(t), o.removeAllRanges(), o.addRange(r), e = o.toString()
        }
        return e
    }
}, function (t, e) {
    function n() {}
    n.prototype = {
        on: function (t, e, n) {
            var o = this.e || (this.e = {});
            return (o[t] || (o[t] = [])).push({
                fn: e,
                ctx: n
            }), this
        },
        once: function (t, e, n) {
            var o = this;

​    function r() {
​        o.off(t, r), e.apply(n, arguments)
​    }
​    return r._ = e, this.on(t, r, n)
},
emit: function (t) {
​    for (var e = [].slice.call(arguments, 1), n = ((this.e || (this.e = {}))[t] || []).slice(), o = 0, r = n.length; o < r; o++) n[o].fn.apply(n[o].ctx, e);
​    return this
},
off: function (t, e) {
​    var n = this.e || (this.e = {}),
​        o = n[t],
​        r = [];
​    if (o && e)
​        for (var i = 0, a = o.length; i < a; i++) o[i].fn !== e && o[i].fn._ !== e && r.push(o[i]);
​    return r.length ? n[t] = r : delete n[t], this
}

}, t.exports = n

}, function (t, e, n) {
    var d = n(5),
        h = n(6);
    t.exports = function (t, e, n) {
        if (!t && !e && !n) throw new Error("Missing required arguments");
        if (!d.string(e)) throw new TypeError("Second argument must be a String");
        if (!d.fn(n)) throw new TypeError("Third argument must be a Function");
        if (d.node(t)) return s = e, f = n, (l = t).addEventListener(s, f), {
            destroy: function () {
                l.removeEventListener(s, f)
            }
        };
        if (d.nodeList(t)) return a = t, c = e, u = n, Array.prototype.forEach.call(a, function (t) {
            t.addEventListener(c, u)
        }), {
            destroy: function () {
                Array.prototype.forEach.call(a, function (t) {
                    t.removeEventListener(c, u)
                })
            }
        };
        if (d.string(t)) return o = t, r = e, i = n, h(document.body, o, r, i);
        throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList");
        var o, r, i, a, c, u, l, s, f
    }
}, function (t, n) {
    n.node = function (t) {
        return void 0 !== t && t instanceof HTMLElement && 1 === t.nodeType
    }, n.nodeList = function (t) {
        var e = Object.prototype.toString.call(t);
        return void 0 !== t && ("[object NodeList]" === e || "[object HTMLCollection]" === e) && "length" in t && (0 === t.length || n.node(t[0]))
    }, n.string = function (t) {
        return "string" == typeof t || t instanceof String
    }, n.fn = function (t) {
        return "[object Function]" === Object.prototype.toString.call(t)
    }
}, function (t, e, n) {
    var a = n(7);

function i(t, e, n, o, r) {
    var i = function (e, n, t, o) {
        return function (t) {
            t.delegateTarget = a(t.target, n), t.delegateTarget && o.call(e, t)
        }
    }.apply(this, arguments);
    return t.addEventListener(n, i, r), {
        destroy: function () {
            t.removeEventListener(n, i, r)
        }
    }
}
t.exports = function (t, e, n, o, r) {
    return "function" == typeof t.addEventListener ? i.apply(null, arguments) : "function" == typeof n ? i.bind(null, document).apply(null, arguments) : ("string" == typeof t && (t = document.querySelectorAll(t)), Array.prototype.map.call(t, function (t) {
        return i(t, e, n, o, r)
    }))
}

}, function (t, e) {
    if ("undefined" != typeof Element && !Element.prototype.matches) {
        var n = Element.prototype;
        n.matches = n.matchesSelector || n.mozMatchesSelector || n.msMatchesSelector || n.oMatchesSelector || n.webkitMatchesSelector
    }
    t.exports = function (t, e) {
        for (; t && 9 !== t.nodeType;) {
            if ("function" == typeof t.matches && t.matches(e)) return t;
            t = t.parentNode
        }
    }
}])
});

$\text{codeShrink.js:}$

$(function () {
  var $code_expand = $('');

  $('.code-area').prepend($code_expand);
  $('.code-expand').on('click', function () {
    if ($(this).parent().hasClass('code-closed')) {
      $(this).siblings('pre').find('code').show();
      $(this).parent().removeClass('code-closed');
    } else {
      $(this).siblings('pre').find('code').hide();
      $(this).parent().addClass('code-closed');
    }
  });
});

然后在 $\text{\css\main.css}$ 中,加入代码:

code {
    padding: 1px 5px;
    font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace;
    /* font-size: 0.91rem; */
    color: #e96900;
    background-color: #f8f8f8;
    border-radius: 2px;
}

pre code {
    padding: 0;
    color: #e8eaf6;
    background-color: #272822;
}

pre[class*="language-"] {
    padding: 1.2em;
    margin: .5em 0;
}

code[class*="language-"],
pre[class*="language-"] {
    color: #e8eaf6;
    white-space: pre-wrap !important;
} */

.line-numbers-rows {
    border-right-width: 0px !important;
}

.line-numbers {
    padding: 1.5rem 1.5rem 1.5rem 3.2rem !important;
    margin: 1rem 0 !important;
    background: #272822;
    overflow: auto;
    border-radius: 0.35rem;
    tab-size: 4;
}


pre {
    padding: 1.5rem !important;
    margin: 1rem 0 !important;
    background: #272822;
    overflow: auto;
    border-radius: 0.35rem;
    tab-size: 4;
}

pre::before {
    content: "";
    height: 16px;
    margin-bottom: 0;
    display: block;
}

pre::after {
    content: " ";
    position: absolute;
    border-radius: 50%;
    background: #ff5f56;
    width: 12px;
    height: 12px;
    top: 0;
    left: 12px;
    margin-top: 12px;
    -webkit-box-shadow: 20px 0 #ffbd2e, 40px 0 #27c93f;
    box-shadow: 20px 0 #ffbd2e, 40px 0 #27c93f;
}

code {
    padding: 1px 5px;
    font-family: Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace;
    font-size: 0.91rem;
    color: #e96900;
    background-color: #f8f8f8;
    border-radius: 2px;
}

.code_copy {
    position: absolute;
    top: 0.7rem;
    right: 35px;
    z-index: 1;
    filter: invert(50%);
    cursor: pointer;
}

.code_lang {
    position: absolute;
    top: 1.2rem;
    right: 60px;
    line-height: 0;
    font-weight: bold;
    font-family: normal;
    z-index: 1;
    filter: invert(50%);
    cursor: pointer;
} 

.code-expand {
    position: absolute;
    top: 4px;
    right: 0px;
    filter: invert(50%);
    padding: 7px 10px;
    z-index: 1;
    cursor: pointer;
    transition: all .3s;
    transform: rotate(0deg);
}

.code-closed .code-expand {
    transform: rotate(-180deg) !important;
    transition: all .3s;
}

.code-closed pre::before {
    height: 0px;
}

pre code {
    padding: 0;
    color: #e8eaf6;
    background-color: #272822;
}

pre[class*="language-"] {
    padding: 1.2em;
    margin: .5em 0;
}

code[class*="language-"],
pre[class*="language-"] {
    color: #e8eaf6;
    white-space: pre-wrap !important;
}

最后在 $\text{post.ejs}$ 中加入:





 

众所周知,复制了别人大量的代码一定会出问题。 hexo s 后,代码块不能正常显示。

Hexo-博客主题开发-07.png

经过一番胡乱百度,发现需要在博客配置文件修改:

highlight:
  enable: true
  line_number: true
  auto_detect: false
  tab_replace: ''
  wrap: true
  hljs: false
prismjs:
  enable: false
  preprocess: true
  line_number: true
  tab_replace: ''
highlight:
  enable: false
  line_number: true
  auto_detect: false
  tab_replace: ''
  wrap: true
  hljs: false
prismjs:
  enable: true  
  preprocess: false
  line_number: true
  tab_replace: ''

然后就成功了吗?并没有。

Hexo-博客主题开发-08.png

还是没有代码高亮。

查阅了相关资料,去代码高亮官网下载文件后,放入至 $\text{\source}$ 路径中并按提示在 $\text{post.ejs}$ 引用:




可以看到代码块终于有高亮显示,但不知道为什么 html 语言还是没有高亮。

Hexo-博客主题开发-09.png

最后在 $\text{\css\main.css}$ 中,作如下修改:

pre {
    max-height: 500px; /* update */
    padding: 1.5rem !important;
    margin: 1rem 0 !important;
    background: #272822;
    overflow: auto;
    border-radius: 0.35rem;
    tab-size: 4;
}

这样的目的是限制代码块的高度不要超过 500 px,防止占用过长的页面。

链接

在每个链接后添加 fa fa-link 图标,如: link

我们可以使用 ::after 伪元素为某个元素添加类似后缀的东西,注意 content 中不能插入图片,于是去 $\text{font-awesome.css}$ 中找到了 fa fa-link 的图标,用 \f08e 表示,故在 $\text{main.css}$ 放入如下代码:

#article a::after {
   content: ' \f08e ';
   font-family: FontAwesome;
}

然后发现,不仅正常的链接被显示,所有的大标题前也都有链接,这是因为在添加标题标签时自动补充的链接。

Hexo-博客主题开发-10.png

我们发现每个小标题都有一个叫 headerlinkclass,所以在 $\text{main.css}$ 中补充:

#article a.headerlink::after {
    content: '';
}

这样即可让小标题前不显示链接标记。

图片

因为图片有时不易被发现,所以希望实现在每个图片下都有一个 fa fa-image 加图片名称的显示。

这次自己实现了一个 $\text{JavaScript}$。

var labelList = $("#article").children(), imgList = [];
console.log('labellength: ' + labelList.length);
var res = 0;


function dfs(ulist) {
    for(var i = 0;i < ulist.length;i++) {
        console.log(ulist[i]);
        if($(ulist[i]).is("img")) {
            imgList.push({node: $(ulist[i]), alt: ulist[i].alt});
        }
        var vlist = ulist[i].children;
        if(vlist.length != 0) {
            dfs(vlist);
        }
    }

}

dfs(labelList);

imgList.forEach(function (image) {
    image.node.after('

' + image.alt + '

'); }); console.log('res: ' + res);

学过算法的应该能理解吧,这里主要说几个内置函数:

也不知道能不能这么说,完全是自己的理解。

这样我们就能实现图片后添加图片名称,但前提是你需要在写文章引用图片时,保留图片名。

其它

还有一些零零碎碎的细节,不作过多介绍,请参见 main.css 的 $\text{post.ejs}$ 部分。

$\text{More Tips}$

  1. $\text{Font Awesome}$

    Font Awesome 字体为您提供可缩放矢量图标,它可以被定制大小、颜色、阴影以及任何可以用CSS的样式。

    使用方法:

    Font Awesome 中文网 – | 字体图标中下载文件,解压出文件夹,拷出当中的 $\text{css}$ 和 $\text{font}$ 文件夹,然后放在主题文件夹中的某个位置。

  2. $\text{Hexo}$ 部署至 $\text{github}$

    通过 hexo g -d 指令部署,即可将本地文件上传至 $\text{github}$ 仓库。在传取少量主题个性图片时,可以将图片放在 $\text{\hexo-theme-MeaningLessNess\source\images}$ 中,部署后可以通过 $\text{url/images/name.*}$ 访问。如将我的博客网站图标以 $\text{favicon.ico}$ 存在上述路径中,则访问时可以通过 https://wyy-oier.github.io/images/favicon.ico 访问。

  3. Hexo g 指令生成缺少 $\text{index.html}$ 等主要文件

    生成文件或者部署到本地时,不包含 $\text{index.html}$ 文件,导致了博客无法正常加载。

    Hexo-博客主题开发-04.png

    解决方案:我采用了暴力方法,直接 hexo init。重新 hexo ghexo d,果然就有相应的文件了。

    Hexo-博客主题开发-05.png

    再把主题复制回去,即可修复成功。