使用 Hexo 主题 Yelee

前言

之前使用的是 next 主题,虽然 next 很强大,但是 next 的样式并不是我很喜欢的类型。
后来无意间在别人博客上看到这种双栏主题,而且还有各种动画效果,作为颜控立马被吸引了,然后发现类似的双栏博客有好几个,像 yilia、spfk、yelee 等。
后者好像都是在 yilia 的基础上修改的,而我选择了 yelee,因为我更喜欢其色调,并且作者很贴心地写了 gitbook 操作指南,这对刚接触博客的小白我来说真是福音。
感谢作者提供了这么好的主题,后来使用上碰到的一些问题和修改,我都一一列举在下面,以后如果还有修改我还会更新。


按照 gitbook 的教程我完成博客的基础配置,效果基本上已经很满足了,但是测试上发现有些小问题,作为强迫症,修改之。

代码块前后留白问题:

代码块中的代码空行消失,都被挤到前后了,issue 上有人解决了:
代码显示错位


然后是页面跳转问题,我希望的是本站内的链接和标签都在本窗口打开,外站的都在新标签页打开,
但是像搜索结果和迷你文章列表等点击会在新标签页打开,得做如下修改:

取消搜索结果跳转新标签页:

目标文件:themes/yelee/source/js/search.js

找到

// show search results
if (isMatch) {
    str += "<li><a href='"+ data_url +"' class='search-result-title' target='_blank'>"+ "> " + data_title +"</a>";
    ...

删除 target='_blank' 字段


取消迷你文章在新窗口打开

迷你文章列表

目标文件:themes/yelee/layout/_partial/open-in-new-tab.ejs

找到

<% if (theme.open_in_new.mini_archives) { %> miniArchives: "a.post-list-link", <% } %>

theme.open_in_new.mini_archives 直接改为 false

我在 _config.yml 里设置 mini_archives 好像不起作用


下面的是我自己的一些修改,如有需要可供参考

我的所有修改记录都放在仓库里了:github

添加文章字数统计

用到的插件是 hexo-wordcount

安装很简单,一条命令 npm i --save hexo-wordcount 即可解决,
下面的配置是信息显示位置和样式,仅供参考,也可以自行修改

目标文件:themes/yelee/layout/_partial/post/tag.ejs
修改如下:

<% if (post.tags && post.tags.length){ %>
-    <div class="article-tag tagcloud">
+    <div class="article-tag tagcloud" style="display: flex; flex-wrap: wrap">
        <%-
          list_tags(post.tags, {
            show_count: false,
            class: 'article-tag'
          })
        %>
+       <span class="post-count">总字数<%= wordcount(post.content) %></span>
+       <span class="post-count">预计阅读<%= min2read(post.content) %>分钟</span>
    </div>
<% } %>

添加样式:
目标文件:themes/yelee/source/css/_partial/tagcloud.styl

找到

.article-tag::before,
.article-category::before
    float left
    color #999
    font base-font-size FontAwesome
    margin-right 5px
    margin-top (1/3)rem

.article-tag::before
    content "\f02b"
    margin-left 1em

.article-category::before
    content "\f02d"

.article-category::before 前插入:

.article-tag
    .article-tag-list
        display flex
        flex-wrap wrap
    span
        cursor pointer
        line-height 29px
        font-size 13px
        color #aaa
        &:before
            content "\27A4"
            margin-left 1em

content: “\27A4” 是 unicode 图标编码,可以换成其他的

unicode 图标

Font Awesome Icon


添加音乐播放器

  • 网易云 iframe 标签
    到网易云网页上找喜欢的歌,点击生产外链播放器,获取 iframe 标签代码,粘贴到想放的地方即可,也可以直接贴在 markdown 文本中,
    我就是这样在每篇文章中插入一首歌;

如果不能正常打开生成外链的页面,只需要把歌曲或者歌单的 id(页面地址就有)记下来就好,标签格式如下

<iframe
  frameborder="no"
  border="0"
  marginwidth="0"
  marginheight="0"
  width=330
  height=450
  src="//music.163.com/outchain/player?type=0&id=883067320&auto=1&height=430"> // 歌曲 type=2,歌单 type=0,id 填对应 id
</iframe>

安装:

$ npm install --save hexo-tag-aplayer

使用:

{% aplayer title author url [picture_url, narrow, autoplay, width:xxx, lrc:xxx] %}
  • 使用 aplayer 库配合 MetingJS 使用

在主题的 _config.yml 文件底部添加两个库:

CDN
  ...
+  aplayer: //cdn.bootcss.com/aplayer/1.6.0/APlayer.min.js # https://github.com/MoePlayer/APlayer
+  meting: //unpkg.com/meting@1.0.1/dist/Meting.min.js # https://github.com/metowolf/MetingJS

目标文件:themes/yelee/layout/_partial/head.ejs
在 jquery 后面导入库

<script src="<%- theme.CDN.jquery %>"></script>
+ <script src="<%- theme.CDN.aplayer %>"></script>
+ <script src="<%- theme.CDN.meting %>"></script>

使用:粘贴以下代码生成播放器

<div
  class="aplayer"
  data-id="883067320"   // 歌曲 / 专辑 / 歌单 ID
  data-server="netease" // 音乐平台:netease、tencent、xiami、kugou、baidu
  data-type="playlist"  // 类型:song、album、playlist、search、artist
  data-mode="random"    // 播放模式:random、single、circulation、order
  data-autoplay="true"  // 自动播放
  >
</div>

简化添加音乐的方法

我喜欢在每篇文章放不同的歌曲,如果按上面的方法添加,每次都得在文章前粘贴一段代码,而且主页加载时也会加载两个 CDN 库,影响加载速度

<div
  class="aplayer"
  data-id="883067320" // 歌曲 ID
  data-server="netease"
  data-type="playlist"
  data-mode="random"
  data-autoplay="true"
/>

下面简化一下步骤

  1. 修改 /themes/yelee/layout/_partial/article.ejs 如下
<div class="article-entry" itemprop="articleBody">
  <% if (index && (post.description || post.excerpt)){ %>
      <% if (post.description){ %>
          <%- post.description %>
      <% } else { %>
          <%- post.excerpt %>
      <% } %>
  <% } else { %>
      <% if (is_page()){ %>
          <%- partial('_partial/page') %>
      <% } %>
+   <% if (post.music){ %>
+       <%- partial('post/player') %>
+   <% } %>
    <%- post.content %>
  <% } %>
</div>
  1. /themes/yelee/layout/post/ 下创建 player.ejs 文件

player:

<script src="<%- theme.CDN.aplayer %>"></script>
<script src="<%- theme.CDN.player %>"></script>

<div class="aplayer" data-id="<%= post.music %>" data-server="netease" data-type="song" data-autoplay="true"></div>
<div style="height: 16px"></div>

<script>
  // 去除移动端的播放器, 减少流量消耗
  if ($(".left-col").css("display") === "none") {
    $(".aplayer").remove();
  }
</script>

这里引入了 cdn 库,如果上文在 head 也引入这两个文件,那可以把 head 里的删了

  1. 每篇文章要加音乐时只需在顶部引入 music: 歌曲 ID
-----------

title: 使用 Hexo 主题 Yelee
date: 2017-08-24
description: " 更换博客主题 Yelee 的前前后后 "
top: true
music: 729638
categories: Web
tags: Yelee

---

Aplayer Fixed

后来 Aplayer 更新了 Fixed 模式,功能算是最完整的

之前的修改可以弃用,player.ejs 可以改成如下:

<script src="<%- theme.CDN.aplayer %>"></script>
<div id="aplayer"></div>
<script src="/js/player.js"></script>

player.js

!function () {
  if ('none' !== $('.left-col').css('display')) {
    const e = new APlayer({
      element: document.getElementById('aplayer'),
      mutex: !0,
      theme: '#EBEEDF',
      order: 'random',
      lrcType: 3,
      fixed: !0
    })
    var xhr = new XMLHttpRequest, listid = 883067320 // 歌单 ID
    xhr.open('get', 'https://api.i-meto.com/meting/api?server=netease&type=playlist&id=' + listid), xhr.onreadystatechange = function () {
      4 === xhr.readyState && (200 === xhr.status ? (e.list.add(JSON.parse(xhr.responseText)), e.play()) : (e.list.add({ // 获取失败时的默认歌曲
        title: 'Fell For U',
        author: 'Noicybino',
        url: 'https://example.com/FellForU.mp3',
        pic: '/img/love.png',
        lrc: ''
      }), e.play()))
    }, xhr.send()
  }
}()

修改头像动画

目标文件:themes/yelee/source/css/_partial/main.styl

修改头像为旋转效果:

    ...
    // animation: profilepic .15s linear infinite alternate;
    // -webkit-animation: profilepic .15s linear infinite alternate;
    animation: profilepic 5s linear infinite;
    -webkit-animation: profilepic 5s linear infinite; // profilepic 动画,周期 5 秒,速度不变,无限循环
}
@keyframes profilepic {
    0% {
        // right: 4px;
        // top: 1x;
        transform: rotate(360deg);
    }
    100% {
        // right: 0px;
        // top: -1px;
        transform: rotate(0deg);
    }
}

animation: name duration timing-function delay iteration-count direction;
animation 是 CSS 动画属性:第一个参数是用 @keyframes 定义的动画,第二个参数是单次动画持续时间,
第三个是动画的速度曲线,第四个参数是动画开始之前的延迟,第五个是动画应该播放的次数,最后一个规定是否应该轮流反向播放动画

添加多合一打赏

实现原理和源码见博文: https://mkblog.cn/922/

给 yelee 的配置文件 _config.yml 加上:

# 打赏
# 将 on 改为 false 去掉打赏
donate:
  on: true
  multipay: /img/multipay.png

multipay.png 上的二维码指向链接 https://blog.wangriyu.wang/pages/donate , 然后根据用户的 userAgent 转向不同服务

目标文件:themes/yelee/layout/_partial/left-col.ejs
找到

<nav class="header-nav">
    <ul class="social">
        <% for (var i in theme.subnav){ %>
            <a class="fa <%= i %>" href="<%- url_for(theme.subnav[i]) %>" title="<%= i %>"></a>
        <%}%>
    </ul>
</nav>

</ul> 下添加:

<% if (theme.donate.on) { %>
    <ul class="social">
        <div style="position: absolute; top: 95%; left: 50%; margin-left: -30px;">
            <p style="display: block">
                <a
                    class="donateIcon"
                    href="javascript:void(0)"
                    onmouseout="
                        var qr = document.getElementById('donate');
                        qr.style.display='none';
                    "
                    onmouseenter="
                        var qr = document.getElementById('donate');
                        qr.style.display='block';
                    "></a>
            </p>
        </div>
    </ul>
<% } %>

left-col.ejs 文件末尾 </div> 之前添加:

<% if (theme.donate.multipay) { %>
    <div id="donate">
        <img id="multipay" src="<%=theme.donate.multipay%>" width="250px" alt="<%=theme.author%> Multipay"/>
        <div class="triangle"></div>
    </div>
<% } %>

目标文件:themes/yelee/source/css/_partial/customise/social-icon.styl
在顶部找到找到

#header .header-nav .social
    margin-top 10px
    text-align center
    font-family Arial
    a
        width base-font-size + 21
        height @width
        border-radius 50%
        margin 0 2px 6px
        vertical-align middle
        font-size .66*@width
        line-height @width
        text-align center
        color white
        background #6f7170
        opacity i-opacity
        box-shadow 1px 2px 2px rgba(0,0,0, .1), 1px 1px 3px rgba(0,0,0, .3)
        &:hover
            opacity 1
            transform scale(1.1)

a 标签的样式后面加上

.donateIcon
    display block
    width 56px
    margin auto
    height 56px
    line-height 56px
    font-size 20px
    color #fff
    border none
    background #4094c7
    border-radius 50%
    text-align center
    -webkit-box-shadow 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12)
    box-shadow 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12)
    -webkit-transition 0.4s ease-in-out
    -moz-transition 0.4s ease-in-out
    -ms-transition 0.4s ease-in-out
    transition 0.4s ease-in-out

目标文件:themes/yelee/source/css/_partial/main.styl
找到 intrude-less 并加上 donate 标签的样式:

.intrude-less {
    width: 76%;
    text-align: center;
    margin: 112px auto 0;
+   #donate {
+       display: none;
+       position: fixed;
+       top: 280px;
+       left: 25px;
+       img {
+           border-radius: 5px;
+       }
+       .triangle {
+           height: 0;
+           width: 0;
+           margin: -5px 0 0 85px;
+           border-right: 45px solid transparent;
+           border-left: 45px solid transparent;
+           border-top: 30px solid #5b91ee;
+       }
+   }
}

修改移动端背景

目标文件:themes/yelee/layout/_partial/background.ejs

<% if (theme.background_image){ %>
  <script>
    $(document).ready(function() {
      var iPad = window.navigator.userAgent.indexOf('iPad');
      if (iPad > -1) {
        let bgColorList = ["#9db3f4", "#414141", "#e5a859", "#f5dfc6", "#c084a0", "#847e72", "#cd8390", "#996731"];
        let bgColor = Math.ceil(Math.random() * (bgColorList.length - 1));
        $("body").css({"background-color": bgColorList[bgColor], "background-size": "cover"});
      } else if ($(".left-col").css("display") === "none") {
        $("body").css({
          "background-image": "url(<%- theme.root_url %>/background/mobile.jpg)",
          "background-repeat": "no-repeat",
          "background-attachment": "fixed",
          "background-size": "cover"
        })
      } else {
        var backgroundnum = <%= theme.background_image %>;
        var backgroundimg = "url(<%- theme.root_url %>/background/bg-x.jpg)".replace(/x/gi, Math.ceil(Math.random() * backgroundnum));
        $("body").css({
          "background": backgroundimg,
          "background-position": "0% 80%",
          "background-attachment": "fixed",
          "background-size": "cover"
        })
      }
    })
  </script>
<% } %>

修改列表显示的 bug

bug 如下:Expressions 和 xScope 的前缀消失
image

markdown 中编写无序列表时,比如 “- [列表名]”, 如果列表名前两个字符含 ‘x’, 则列表前缀消失

排查之后发现是 main.js 里对无序列表进行了判断,若出现 “- [ ]” 或者 “- [x]”,则解析成复选框的样式,需要修改判断条件

目标文件:themes/yelee/source/js/main.js

// Task lists in markdown
$('ul > li').each(function() {
    var taskList = {
-       field: this.textContent.substring(0, 2),
+       field: this.textContent.substring(0, 3),
        check: function(str) {
-           var re = new RegExp(str);
-           return this.field.match(re);
+           return this.field === str;
        }
    }
    ...
}

添加 404 文字特效

原文字特效: 点击查看

如果没有 404 页面,先用 Hexo 命令新建一个名为 404 的页面: hexo new page 404

/source/404/index.md 文件的内容修改如下:

---

toc: false
comments: false
permalink: /404

---

<script src="//cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.5/pixi.min.js"></script>

<div id="yun"></div>

<script>
    /**
     *  404 页面
     */
    (function() {
         var lastTime = 0;
         var vendors = ['ms', 'moz', 'webkit', 'o'];
         for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
           window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
           window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
         }

         if (!window.requestAnimationFrame)
           window.requestAnimationFrame = function(callback, element) {
             var currTime = new Date().getTime();
             var timeToCall = Math.max(0, 16 - (currTime - lastTime));
             var id = window.setTimeout(function() {
                 callback(currTime + timeToCall);
               },
               timeToCall);
             lastTime = currTime + timeToCall;
             return id;
           };

         if (!window.cancelAnimationFrame)
           window.cancelAnimationFrame = function(id) {
             clearTimeout(id);
           };
       }());

    //math2 utils
    var Math2={};
    Math2.random = function (t, n) {
      return Math.random() * (n - t) + t
    }, Math2.map = function (t, n, r, a, o) {
      return (o - a) * ((t - n) / (r - n)) + a
    }, Math2.randomPlusMinus = function (t) {
      return t = t ? t : .5, Math.random() > t ? -1 : 1
    }, Math2.randomInt = function (t, n) {
      return n += 1, Math.floor(Math.random() * (n - t) + t)
    }, Math2.randomBool = function (t) {
      return t = t ? t : .5, Math.random() < t ? !0 : !1
    }, Math2.degToRad = function (t) {
      return rad = t * Math.PI / 180, rad
    }, Math2.radToDeg = function (t) {
      return deg = 180 / (Math.PI * t), deg
    }, Math2.rgbToHex = function (t) {
      function n(t) {
        return ("0" + parseInt(t).toString(16)).slice(-2)
      }
      t = t.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
      var r = n(t[1]) + n(t[2]) + n(t[3]);
      return r.toUpperCase()
    }, Math2.distance = function (t, n, r, a) {
      return Math.sqrt((r - t) * (r - t) + (a - n) * (a - n))
    };
    //mouse
    var mousePos={
      x:0,
      y:0
    };
    window.onmousemove = function(e) {
      e = e || window.event;

      var pageX = e.pageX - 300;
      var pageY = e.pageY + 350;
      if (pageX === undefined) {
        pageX = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - 300;
        pageY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop + 350;
      }
      mousePos = {
        x: pageX,
        y: pageY,
      };
    };

    var options = {
      width: 1000,
      height: 1000,
      keyword: "404",
      density: 8,
      densityText: 2,
      minDist: 20,
    };

    // initialize canvas
    var canvas = document.createElement('canvas');
    canvas.width = options.width;
    canvas.height = options.height;
    canvas.style.width = options.width/2;
    canvas.style.height = options.height/2;
    canvas.getContext('2d').scale(2,2)

    var renderer = new PIXI.autoDetectRenderer(options.width, options.height, {
      transparent: true
    });
    var stage = new PIXI.Container("0X000000", true);
    document.getElementById("yun").appendChild(renderer.view);
    renderer.view.id = "notFound";

    var imageData = false;
    var particles =[];

    function init() {
      positionParticles();
      positionText();
    }

    function positionParticles() {
      var canvas = document.createElement("canvas");
      canvas.width = 500;
      canvas.height = 350;
      var context = canvas.getContext("2d");
      context.fillStyle = "#000000";
      context.font = "300px 'Arial', sans-serif";
      context.fillText(options.keyword, 0, 250);

      var imageData = context.getImageData(0, 0, 350, 500);
      data = imageData.data;

      // Iterate each row and column
      for (var i = 0; i < imageData.height; i += options.density) {
        for (var j = 0; j < imageData.width; j += options.density) {

          // Get the color of the pixel
          var color = data[((j * (imageData.width * 4)) + (i * 4)) - 1];

          // If the color is black, draw pixels
          if (color == 255) {
            var newPar = particle();
            newPar.setPosition(i, j);
            particles.push(newPar);
            stage.addChild(newPar);
          }
        }
      }
    }

    function positionText() {
      var canvas = document.createElement("canvas");
      canvas.width = 400;
      canvas.height = 120;
      var context = canvas.getContext("2d");
      context.fillStyle = "#000000";
      context.font = "80px 'Arial', sans-serif";
      context.fillText("Not Found", 0, 80);

      var imageData = context.getImageData(0, 0, 400, 400);
      data = imageData.data;

      // Iterate each row and column
      for (var i = 0; i < imageData.height; i += options.densityText) {
        for (var j = 0; j < imageData.width; j += options.densityText) {

          // Get the color of the pixel
          var color = data[((j * (imageData.width * 4)) + (i * 4)) - 1];

          // If the color is black, draw pixels
          if (color == 255) {
            var newPar = particle(true);
            newPar.setPosition(i, j);
            particles.push(newPar);
            stage.addChild(newPar);
          }
        }
      }
    }

    function particle(text) {
      $this = new PIXI.Graphics();

      if (text == true) {
        $this.text = true;
      }

      $this.beginFill(0X5CC9F5);

      var radius;
      $this.radius = radius = $this.text ? Math.random() * 3.5 : Math.random() * 10.5;

      $this.drawCircle(0, 0, radius);

      $this.size = this.radius;
      $this.x = -this.width;
      $this.y = -this.height;
      $this.free = false;

      $this.timer = Math2.randomInt(0, 100);
      $this.v = Math2.randomPlusMinus() * Math2.random(.5, 1);
      $this.hovered = false

      $this.alpha = Math2.randomInt(10, 100) / 100;

      $this.vy = -5 + parseInt(Math.random() * 10) / 2;
      $this.vx = -4 + parseInt(Math.random() * 8);

      $this.setPosition = function(x, y) {
        if ($this.text) {
          $this.x = x + (options.width / 2 - 180);
          $this.y = y + (options.height / 2 + 100);
        } else {
          $this.x = x + (options.width / 2 - 250);
          $this.y = y + (options.height / 2 - 175);
        }
      };

      return $this;
    }

    function update() {
      renderer.render(stage);

      for (i = 0; i < particles.length; i++) {
        var p = particles[i];

        if (mousePos.x > p.x && mousePos.x < p.x + p.size && mousePos.y > p.y && mousePos.y < p.y + p.size) {
          p.hovered = true;
        }

        p.scale.x = p.scale.y = scale = Math.max(Math.min(2.5 - (Math2.distance(p.x, p.y, mousePos.x, mousePos.y) / 160), 160), 1);


        p.x = p.x + .2 * Math.sin(p.timer * .15)
        p.y = p.y + .2 * Math.cos(p.timer * .15)
        p.timer = p.timer + p.v;

      }
      window.requestAnimationFrame(update);
    }

    init();
    update();
</script>

修改样式: themes/yelee/source/css/_partial/article.styl
.article-entry 子级中加上如下样式:

#notFound {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translateY(-50%) translateX(-50%);
  width: 80%;
  height: auto;
}
#yun {
  height: 320px;
  width: 100%;
  position: relative;
}
@media only screen and (max-width: 768px) {
  #yun {
    height: 300px;
  }
  #notFound {
    width: 100%;
  }
}
@media only screen and (max-width: 425px) {
  #yun {
    height: 200px;
  }
  #notFound {
    width: 120%;
  }
}

最终效果: 点击查看

如果不想主题文件修改你添加的单页面内容,需要在站点配置文件中配置如下:

//  单个文件夹下全部文件:skip_render: test/*
//  单个文件夹下指定类型文件:skip_render: test/*.html
//  单个文件夹下全部文件以及子目录:skip_render: test/**
//  多个文件夹以及各种复杂情况:
//  skip_render:
//      - `test1/*.html`
//      - `test2/**`

比如 skip_render: pages/** 代表 source/pages/ 目录下所有东西都不会被修改

压缩优化静态文件

使用 hexo-all-minifier 插件压缩 js、html、css 和图片

在博客目录安装插件 npm install hexo-all-minifier --save

对于 Mac 用户,可能还需要安装以下依赖:

$ brew install libtool automake autoconf nasm

在站点配置文件 _config.yml 中添加插件配置

### hexo_all_minifier: https://github.com/chenzhutian/hexo-all-minifier
all_minifier: true
html_minifier:
  enable: true
  ignore_error: false
  silent: false // 设为 true 代表不输出压缩的信息日志,默认为 false
css_minifier:
  enable: true
  silent: false
  exclude: // 排除此类型文件
    - '*.min.css'
js_minifier:
  enable: true
  mangle: true
  silent: false
  exclude:
    - '*.min.js'
image_minifier:
  enable: true
  interlaced: true // 交错 gif 图实现渐进式渲染,默认为 false
  multipass: true // 压缩 svg 格式,默认 false
  optimizationLevel: 3 // 压缩级别 0~7,默认为 2
  pngquant: true // 使用 imagemin-pngquant 插件压缩 png,默认为 false
  progressive: true // 无损转换为渐进式
  silent: false
  exclude: // 排除指定类型的图片文件,比如 gif,jpg, png, 或 svg,默认为 null

使用 hexo clean && hexo g && hexo s --watch --debug 命令查看加载信息

添加谷歌站长并提交站点地图

验证网址:Search Console

然后安装 sitemap 生成器 npm install hexo-generator-sitemap --save

在站点配置文件 _config.yml 中加上

### sitemap
sitemap:
  path: sitemap.xml

部署一遍博客目录下会多出一个 sitemap.xml 文件, 到谷歌站长里添加站点地图即可

这里提一点,搜索引擎会抓取路径在三级以内的地址,如果路径太多可能抓取不到,但 hexo 文章的路径默认会加上日期,比如 http://wangriyu.wang/2017/08/24/Hexo/
在站点配置文件中修改一下 permalink(默认是:year/:month/:day/:title/):

# URL
## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'
url: http://wangriyu.wang
root: /
permalink: :title.html
permalink_defaults:

现在就不会加上日期了

使用七牛云图床

https://portal.qiniu.com
注册七牛云账号,进入对象存储,点击内容管理,上传图片或其他一些资源,上传完成便可以使用产生的外链来代替站内的图片资源,
七牛云还提供了图片处理的服务,添加水印、裁剪缩放等

使用 Sentry.io 的错误收集服务

添加 CDN 库:sentry: //cdn.ravenjs.com/3.18.1/raven.min.js

在 head.ejs 中加入以下代码

<script src="<%- theme.CDN.sentry %>" crossorigin="anonymous"></script>
<script>Raven.config(' 服务地址 ').install()</script>

服务地址改为注册得到的地址,例如 https://XXXXXXXXXXXX@sentry.io/XXXXXX

之后打开 sentry 的网站查看,如果出现错误会一一记录下来
image

该网站支持很多语言,可以嵌入到很多东西中,方便收集错误,但是不建议长期放在网站上,感觉会影响性能

添加二次元人物

安装插件 npm install -save hexo-helper-live2d

在站点配置文件 _config.yml 中添加:

## live2d: https://github.com/EYHN/hexo-helper-live2d
live2d:
  enable: true
  scriptFrom: local
  pluginRootPath: live2dw/
  model:
    use: live2d_models/live2d-widget-model-izumi // 加载本地 model 文件
    scale: 1
    hHeadPos: 0.5
    vHeadPos: 0.618
  display:
    position: right
    width: 100
    height: 200
    hOffset: -50
    vOffset: -85
  mobile:
    show: false
  react:
    opacityDefault: 0.9
    opacityOnHover: 0.3

更换图片背景为动态背景

layout.ejs 文件中的 left-col 和 mid-col 之间插入

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/bubbly-bg@1.0.0/dist/bubbly-bg.js"></script>
<script>
  var userAgent = window.navigator.userAgent;
  bubbly({
    blur: 1,
    bubbleFunc: () = > `hsla(${Math.random() * 50}, 100%, 50%, .3)`,
    bubbles: userAgent.match(/AppleWebKit.*Mobile.*/) ? 15 : 30,
    colorStart: "#fff4e6",
    colorStop: "#ffe9e4",
    compose: "source-over"
  });
</script>

然后把 after-footer 文件中的 <%- partial('_partial/background') %> 删掉

添加 Valine 评论系统

https://blog.wangriyu.wang/2018/03-valine.html

添加隐藏侧边栏按钮

目标文件: themes/yelee/layout/layout.ejs

    ...
+   <div class="hide-left-col" title="隐藏侧栏">
+     <i class="fa fa-angle-double-left"></i>
+   </div>
    <div class="mid-col">
      <%- partial('_partial/mobile-nav', null, {cache: !config.relative_link}) %>
      <div class="body-wrap"><%- body %></div>
      <%- partial('_partial/footer') %>
    </div>
+   <script type="application/javascript">
+     var hide = false, leftWidth = <%- theme.left_col_width %>;
+     function hideLeftCol() {
+       if (hide) {
+         $(".left-col").width(leftWidth);
+         $(".left-col .intrude-less").css('display', '');
+         $("#tocButton").css('display', '');
+         ($('#switch-btn').css('display') === 'block' && $('#switch-area').css('display') === 'block') || $('#toc').slideDown(320);
+         $(".hide-left-col").css("left", leftWidth).html('<i class="fa fa-angle-double-left"></i>');
+         $(".mid-col").css("left", leftWidth)
+         $("#post-nav-button").css("left", leftWidth)
+         $("#post-nav-button > a:nth-child(2)").css("display", "block")
+         hide = false
+       } else {
+         $(".left-col").width(0);
+         $(".left-col .intrude-less").css('display', 'none');
+         $("#toc").css('display', 'none');
+         $("#tocButton").css('display', 'none');
+         $(".hide-left-col").css("left", 0).html('<i class="fa fa-angle-double-right"></i>');
+         $(".mid-col").css("left", 0)
+         $("#post-nav-button").css("left", 0)
+         $("#post-nav-button > a:nth-child(2)").css("display", "none")
+         if ($(".post-list").is(":visible")) {
+           $("#post-nav-button .fa-bars,#post-nav-button .fa-times").toggle();
+           $(".post-list").toggle();
+         }
+         hide = true
+       }
+     }
+     $(".hide-left-col").click(function() {
+       hideLeftCol()
+     });
+   </script>
    <%- partial('_partial/after-footer') %>
  </div>

目标文件: themes/yelee/source/css/_partial/main.styl

.left-col {
  ...
}
+.hide-left-col {
+   z-index: 999;
+   cursor: pointer;
+   transition:all .2s ease-in;
+   position: fixed;
+   top: 0;
+   left: left-col-width;
+   text-align: center;
+   line-height: 30px;
+   display: block;
+   width: 30px;
+   height: 30px;
+   font-size: 28px;
+   background: none;
+   .fa {
+       color: rgb(255, 255, 255, .5);
+   }
+   &:hover {
+       background: rgba(147, 181, 224, .3) !important;
+       .fa {
+           color: white;
+       }
+   }
+}
.mid-col {
  ...
}

...

@media screen and (max-width:768px) {
+   .hide-left-col {
+       display: none;
+   }
    @import "_partial/mobile"
}

...

#post-nav-button {
    left: left-col-width;
    top: 61.8%;
+   transition: left .2s ease-in;
    a {
        border-bottom-color: transparent;
        background: none;
        cursor: pointer;
        .fa-times {
            display: none;
        }
    }
}

目标文件: themes/yelee/source/css/_partial/mobile.styl

.left-col {
-   display: none;
+   display: none !important;
}
.mid-col {
-   left: 0;
+   left: 0 !important;
}

使用 prism 代码高亮

安装插件 npm i -S hexo-prism-plugin

将根目录 _config.yml 中 highlight 部分均设为 fasle,并添加 prism 配置项:

highlight:
- enable: true
+ enable: false
  line_number: false
  auto_detect: false
  tab_replace:

# prism
# https://prismjs.com/#languages-list
+ prism_plugin:
+   mode: 'preprocess'   # realtime/preprocess
+   theme: 'a11y-dark'   # https://github.com/PrismJS/prism-themes#available-themes
+   line_number: true    # default false
+   custom_css:          # optional

将主题的 _config.yml 的 highlight_style 删掉

-highlight_style:
-  on: false
-  inline_code: 3  # Value: 0 - 9 可选
-  code_block: 4  # Value: 0 - 4
-  # Set inline_code to style highlight text
-  # Chose a highlight theme for code block
-  # 通过 inline_code 切换内置文本高亮样式
-  # 通过 code_block 切换内置代码高亮配色主题

删除 themes/yelee/source/css/_partial/customise/ 下的 code-block.styl 和 inline-code.styl

删除 themes/yelee/source/css/_partial/highlight.styl

修改 mobile.styl:

    .article-entry{
        padding-left: 0;
        padding-right: 0;
-       .highlight {
-           padding .35em .6em
-       }
    }

修改 themes/yelee/source/css/style.styl:

@import "_partial/archive"
@import "_partial/article"
@import "_partial/archive"
- @import "_partial/highlight"
@import "_partial/footer"

if share
@ -117,4 +116,14 @@ if search
@import "_partial/customise/color-scheme"

if sidebar
  @import "_partial/sidebar"
  @import "_partial/sidebar"

+ .article-entry code
+   color gray
+   padding .05em .3em
+   border-radius 3px
+   box-shadow 1px 1px 1px rgba(0,0,0, .08)
+   background rgba(255, 250, 165, .7)

+ .article-entry pre code
+   padding 0

⚠️ 注意使用此插件时所有代码块都需要指定语言,否则无法加载高亮效果

语言列表 https://prismjs.com/#languages-list

升级 fancybox 图片浏览插件

修改主题的 _config.yml 中的 CDN:

...
-  fancybox_js: //cdn.bootcss.com/fancybox/2.1.5/jquery.fancybox.min.js
-  fancybox_css: //cdn.bootcss.com/fancybox/2.1.5/jquery.fancybox.min.css
+  fancybox_js: //cdn.bootcss.com/fancybox/3.3.5/jquery.fancybox.min.js
+  fancybox_css: //cdn.bootcss.com/fancybox/3.3.5/jquery.fancybox.min.css
...

修改 themes/yelee/source/js/main.js:

// fancyBox
if(!!yiliaConfig.fancybox){
    require([yiliaConfig.fancybox_js], function(pc){
        var isFancy = $(".isFancy");
        if(isFancy.length != 0){
            var imgArr = $(".article-inner img");
            for(var i=0,len=imgArr.length;i<len;i++){
                var src = imgArr.eq(i).attr("src");
                var title = imgArr.eq(i).attr("alt");
                if(typeof(title) == "undefined"){
                    var title = imgArr.eq(i).attr("title");
                }
                var width = imgArr.eq(i).attr("width");
                var height = imgArr.eq(i).attr("height");
-               imgArr.eq(i).replaceWith("<a href='"+src+"' title='"+title+"' rel='fancy-group' class='fancy-ctn fancybox'><img src='"+src+"' width="+width+" height="+height+" title='"+title+"' alt='"+title+"'></a>");
+               imgArr.eq(i).replaceWith("<a class='fancy-ctn' href='"+src+"' title='"+title+"' data-fancybox='images' data-caption='"+src.substring(src.lastIndexOf("/")+1)+"'><img src='"+src+"' width="+width+" height="+height+" title='"+title+"' alt='"+title+"'></a>");
            }
-           $(".article-inner .fancy-ctn").fancybox({ type: "image" });
+           $(".article-inner .fancy-ctn").fancybox({
+             loop: true,
+             touch: false,
+             toolbar: true,
+             wheel: false,
+             buttons: [
+               "fullScreen",
+               "thumbs",
+               "close"
+             ],
+           });
        }
    })
}

修改 themes/yelee/source/css/style.styl,在最后面插入:

...
.fancybox-image
  background #ffffff

#live2dcanvas
  z-index 999 !important

我的配置

根目录配置 _config.yml:

...

highlight:
  enable: false
  line_number: false
  auto_detect: false
  tab_replace:

...

archive_generator:
  per_page: 25 ## 归档页面文章数量,如果值为 0 不分页

# Extensions
## Plugins: https://hexo.io/plugins/
### hexo_all_minifier: https://github.com/chenzhutian/hexo-all-minifier
all_minifier: true
html_minifier:
  enable: true
  ignore_error: false
  silent: false
css_minifier:
  enable: true
  silent: false
  exclude:
    - '*.min.css'
js_minifier:
  enable: true
  mangle: true
  silent: false
  exclude:
    - '*.min.js'
image_minifier:
  enable: true
  interlaced: true
  multipass: true
  optimizationLevel: 3
  pngquant: true
  progressive: true
  silent: false

## sitemap
sitemap:
  path: sitemap.xml

## baidusitemap
baidusitemap:
  path: baidusitemap.xml

# RSS
feed:
  type: rss2
  path: atom.xml
  limit: false
  hub:
  content: true
  content_limit:
  content_limit_delim:

## https://github.com/huiwang/hexo-baidu-url-submit
baidu_url_submit:
  count: 10 ## 提交最新链接
  host: https://example.com ## 在百度站长平台中注册的域名
  token: XXXXXXXXXXX ## 请注意这是您的秘钥, 所以请不要把博客源代码发布在公众仓库里!
  path: baidu_urls.txt ## 文本文档的地址, 新链接会保存在此文本文档里

## Themes: https://hexo.io/themes/
theme: Yelee

# prism
# https://prismjs.com/#languages-list
prism_plugin:
  mode: 'preprocess'   # realtime/preprocess
  theme: 'a11y-dark'   # https://github.com/PrismJS/prism-themes#available-themes
  line_number: true    # default false
  custom_css:          # optional

## live2d: https://github.com/EYHN/hexo-helper-live2d
live2d:
  enable: true
  scriptFrom: local
  pluginRootPath: live2dw/
  model:
    use: live2d_models/live2d-widget-model-izumi
    scale: 1
    hHeadPos: 0.5
    vHeadPos: 0.618
  display:
    position: right
    width: 100
    height: 200
    hOffset: -50
    vOffset: -85
  mobile:
    show: false
  react:
    opacityDefault: 0.9
    opacityOnHover: 0.3

主题配置 _config.yml:

...

# Link to your avatar | 填写头像地址
avatar: //example.com/avatar.jpg

# 左栏背景
left_col_bg: //example.com/yule.jpg

# >>> Conments 评论系统 <<<
# Chose ONE as your comment system and keep others disable.
# 选一个作为网站评论系统,其他保持禁用。

preload_comment: false
## false: 当点击评论条等区域时再加载评论模块
## false: load comment's section until u click/hover on the bar/icon

show_count: false
## 是否在主页文章标题旁显示评论数(多说、Disqus)
## Add comment count after article title

disqus: 
  on: false
  shortname: wangriyu
  # https://help.disqus.com/customer/en/portal/articles/466208-what-s-a-shortname-
  # It is unnecessary to enable disqus here if 
  # you have set "disqus_shortname" in your site's "_config.yml" 

duoshuo: 
  on: false
  domain: 
  # 是否开启多说评论,http://duoshuo.com/create-site/
  # 使用上面网址登陆你的多说,然后创建站点,在 domain 中填入你设定的域名前半部分
  # http://<要填的部分>.duoshuo.com (domain只填上<>里的内容,不要填整个网址)

youyan:
  on: false
  id: 
  # 是否开启友言评论,http://www.uyan.cc/index.php
  # id 中填写你的友言用户数字ID,注册后进入后台管理即可查看
  # 友言服务在 Web 环境下运行,普通本地环境无法查看,请部署后在线上测试。

valine:
  on: true
  appid: XXXXXXXXX
  appkey: XXXXXXXXXX
  verify: false #验证码
  notify: true #评论回复提醒
  avatar: retro #评论列表头像样式:''/mm/identicon/monsterid/wavatar/retro/hide
  placeholder: Just do it ! #评论框占位符

wildfire:
  on: false

# >>> Style Customisation 样式自定义 <<<

# Background | 背景
## "5": show images form bg-1.jpg to bg-5.jpg in `/yelee/source/background/`
## "5": 显示`/yelee/source/background/`文件夹中 bg-1.jpg 到 bg-5.jpg 这5张图片
## "0": white-gray background | 淳朴灰白背景
background_image: 4

# Base Font Size | 字号调节
base_font_size: 16  #px, 16 - 24

# 自定义文章「引用部分」的样式
blockquote_style:
  on: true
  blockquote: 3  # Value: 0 - 7 可选

# Headings Style | 标题风格
## 0-Yelee, 1-Yilia, 2-GitHub
heading_style: 0 # Value: 0 - 2

## List style type (ul) | 无序列表项标记样式
list_style: 2  # value: 0 - 12 可选

# 左边栏宽度 px
left_col_width: 300

# Copyright info. of post | 文末版权信息
copyright: true

# Table of contents | 文章目录
toc:
  on: true
  list_number: false # 目录序号
  max_depth: 6  # 1 - 6 (h1-h6) 目录最大级数
  nowrap: true # Keep title on same line | 目录标题不换行

# Load jQuery UI to style tooltips
# 工具提示框样式美化
jquery_ui: false

# Max width of right cloumn | 限制右侧内容的宽带 
limit_article_width: 
  on: false
  max_width: 60 # em

# >>> Small features | 小功能设置 <<<

# 是否开启边栏多标签切换
# Birdhouse button in left column
tagcloud: true

# Open link in a new tab | 是否在新窗口打开链接
## `global` 0: Set separately, 1: Open all in new 2: Open all in current
## `global` 0: 分开设置, 1: 全部在新标签打开, 2: 全部在"当前"标签打开
open_in_new:
  global: 0 # 0-2
  title: false # article title in homepage 主页文章标题
  post: true # link within post/page 正文中的链接
  article_nav: false # 导航
  mini_archives: fasle # 迷你归档
  tags: false # 标签
  categories: false # 分类
  archives: false # 归档
  menu: false # 边栏菜单
  friends: true  # 友情链接
  socail: true # 社交图标

# >>> Vendors | 第三方工具 & 服务 <<<

# Local Site Search | 本地站内搜索
## Insatall below plugin to take effect | 使用搜索需先安装对应插件
## https://github.com/PaicHyperionDev/hexo-generator-search
search:
  on: true
  onload: false
  ## true: get search.xml file when the page has loaded
  ## false: get the file when search box gets focus

# images viewer | 图片浏览器
## http://www.fancyapps.com/fancybox/
fancybox: true

# Display Math(LaTeX, MathML...) | 数学公式支持
## https://www.mathjax.org/
mathjax: true

# Social Share | 是否开启分享
share: 
  on: true
  spongebob: true
  baidu: false
  addthis: false
  addthis_pubid: "ra-5992865c5fd0cfd1"
  ## Go to www.addthis.com/dashboard to get your pubid (in src of Code)
  ## and customize AddThis share buttons

# 不蒜子网站计数设置
# http://ibruce.info/2015/04/04/busuanzi/
visit_counter:
  on: true
  site_visit: true
  page_visit: true

# GitHub Repo Widget
# https://github.com/hustcc/GitHub-Repo-Widget.js
github_widget: false

# Progress Bar | 页面加载进度条
# Demo: http://github.hubspot.com/pace/docs/welcome/
# type: barber-shop|big-counter|bounce|center-atom|center-circle|
#       center-radar|center-simple|corner-indicator|flash|flat-top|
#       loading-bar|mac-osx|minimal
# color: black|blue|green|orange|pink|purple|red|silver|white|yellow|
progressBar:
  on: false
  type: "minimal"  # Keep Quotes | 保留引号避免出错
  color: orange

# 打赏
# 将on改为false去掉打赏
donate:
  on: true
  multipay: //example.com/multipay.png

CDN:
  jquery: //cdn.bootcss.com/jquery/2.2.4/jquery.min.js
  require: //cdn.bootcss.com/require.js/2.2.0/require.min.js
  fontawesome: //cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css
  fancybox_js: //cdn.bootcss.com/fancybox/3.3.5/jquery.fancybox.min.js
  fancybox_css: //cdn.bootcss.com/fancybox/3.3.5/jquery.fancybox.min.css
  jquery_ui_js: //cdn.bootcss.com/jqueryui/1.10.4/jquery-ui.min.js
  jquery_ui_css: //cdn.bootcss.com/jqueryui/1.10.4/css/jquery-ui.min.css
  pace_js: //cdn.bootcss.com/pace/1.0.2/pace.min.js
  mathjax: //cdn.bootcss.com/mathjax/2.7.4/MathJax.js
  aplayer: //example.com/lib/Aplayer/1.10.1/APlayer.min.js
  aplayer_css: //example.com/lib/Aplayer/1.10.1/APlayer.min.css
  aos_css: //example.com/lib/aos/2.2.0/aos.css
  aos_js: //example.com/lib/aos/2.2.0/aos.js
  bubbly_bg: //example.com/lib/bubbly-bg/1.0.0/bubbly-bg.js
  valine: //example.com/lib/Valine/1.2.0/Valine.min.js

package.json 依赖项:

{
  "dependencies": {
    "hexo": "^3.2.0",
    "hexo-all-minifier": "^0.5.2",
    "hexo-baidu-url-submit": "0.0.5",
    "hexo-deployer-git": "^0.3.0",
    "hexo-generator-archive": "^0.1.4",
    "hexo-generator-category": "^0.1.3",
    "hexo-generator-feed": "^1.2.2",
    "hexo-generator-index": "^0.2.0",
    "hexo-generator-search": "^2.2.5",
    "hexo-generator-sitemap": "^1.2.0",
    "hexo-generator-tag": "^0.2.0",
    "hexo-helper-live2d": "^3.0.2",
    "hexo-prism-plugin": "^2.2.0",
    "hexo-renderer-ejs": "^0.2.0",
    "hexo-renderer-marked": "^0.2.10",
    "hexo-renderer-stylus": "^0.3.1",
    "hexo-server": "^0.2.0",
    "hexo-wordcount": "^3.0.2"
  },
  "devDependencies": {
    "hexo-generator-baidu-sitemap": "^0.1.2"
  }
}

全部的目录结构:

├── _config.yml
├── live2d_models
│   └── live2d-widget-model-izumi
│       ├── assets
│       │   ├── exp/
│       │   ├── izumi.model.json
│       │   ├── izumi.physics.json
│       │   ├── moc
│       │   │   ├── izumi_illust.1024
│       │   │   │   ├── texture_00.png
│       │   │   │   ├── texture_01.png
│       │   │   │   ├── texture_02.png
│       │   │   │   └── texture_03.png
│       │   │   └── izumi_illust.moc
│       │   ├── mtn/
│       │   └── snd/
│       ├── package-lock.json
│       └── package.json
├── node_modules
├── package-lock.json
├── package.json
├── public
├── source
│   ├── 404
│   │   └── index.md
│   ├── _posts
│   │   └── HellWorld.md
│   ├── about
│   │   ├── index.md
│   │   └── resume.pdf
│   ├── CNAME
│   ├── images
│   ├── pages
│   │   ├── donate
│   │   │   └── index.html
│   │   ├── wildfire.html
│   │   └── yun.html
│   ├── robots.txt
│   └── tags
│       └── index.md
└── themes
    └── yelee
        ├── _config.yml
        ├── languages/
        ├── layout/
        ├── package.json
        ├── README.md
        └── source
            ├── apple-touch-icon.png
            ├── background/
            ├── css/
            ├── favicon.ico
            ├── img/
            ├── js/
            └── share/

我的所有修改都可以在仓库里找到:wangriyu/hexo-theme-yelee

文章目录
  1. 前言
  2. 代码块前后留白问题:
  3. 取消搜索结果跳转新标签页:
  4. 取消迷你文章在新窗口打开
  5. 添加文章字数统计
  6. 添加音乐播放器
  7. 简化添加音乐的方法
  8. Aplayer Fixed
  9. 修改头像动画
  10. 添加多合一打赏
  11. 修改移动端背景
  12. 修改列表显示的 bug
  13. 添加 404 文字特效
  14. 压缩优化静态文件
  15. 添加谷歌站长并提交站点地图
  16. 使用七牛云图床
  17. 使用 Sentry.io 的错误收集服务
  18. 添加二次元人物
  19. 更换图片背景为动态背景
  20. 添加 Valine 评论系统
  21. 添加隐藏侧边栏按钮
  22. 使用 prism 代码高亮
  23. 升级 fancybox 图片浏览插件
  24. 我的配置