山顶的男人

山顶的男人 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织 www.baidu.com 编辑
编辑

感觉为时已晚的时候,恰恰是最早的时候!

个人动态

山顶的男人 关注了标签 · 2018-10-23

koa2

下一代 node.js web 开发框架

关注 83

山顶的男人 回答了问题 · 2018-10-22

解决阮一峰的网络日志(http://www.ruanyifeng.com/blog/)是如何检测广告被拦截的?

Adblock 使用 CSP 技术,不允许 inline-script 的执行,而阮老师的 blog 里面使用了 inline-script 去加载 css 文件。

function loadjscssfile(filename, filetype){
    if (filetype=="js"){ //if filename is a external JavaScript file
        var fileref=document.createElement('script')
        fileref.setAttribute("type","text/javascript")
        fileref.setAttribute("src", filename)
    }
    else if (filetype=="css"){ //if filename is an external CSS file
        var fileref=document.createElement("link")
        fileref.setAttribute("rel", "stylesheet")
        fileref.setAttribute("type", "text/css")
        fileref.setAttribute("href", filename)
    }
    if (typeof fileref!="undefined")
        document.getElementsByTagName("head")[0].appendChild(fileref)
}
//loadjscssfile("http://www.ruanyifeng.com/blog/styles.css", "css");
loadjscssfile('/static/themes/theme_scrapbook/theme_scrapbook.css', "css");

http://www.ruanyifeng.com/sta...,这个 css 文件没有加载,导致页面样式出现问题。

...
#container::before {
  display: none;
}
...

关注 4 回答 2

山顶的男人 回答了问题 · 2018-10-22

解决hexo发布文章,github更新上去了,但是博客不显示文章,不是网络更新延迟

检查一下

  1. 本地 hexo server,看看能不能跑
  2. 有没有 hexo generate后,再提交

关注 3 回答 2

山顶的男人 赞了回答 · 2018-10-17

var console let console 直接报错?

这是因为在控制台里,使用 letconst 声明变量的时候,遵循本身定义的规则,具有块级作用域(不允许声明重复的变量名),而且定义的变量并没有销毁掉。

关注 3 回答 2

山顶的男人 回答了问题 · 2018-10-17

解决react从state中获取的数组可以直接修改吗?

推荐使用 lodashcloneDeep 方法,深拷贝一份新的数组,然后在新数组上更改,最后将新数组 set 回去。

const { items } = this.state
// 深拷贝,得到一个新数组
const newItems = cloneDeep(items)
// 在新数组上更改
newItems[i].status = 'doing'
this.setState({  
    items: newItems, 
})

关注 4 回答 5

山顶的男人 发布了文章 · 2018-10-16

JavaScript 高级技巧 Memoization

memoization 来源于拉丁语 memorandum ("to be remembered"),不要与 memorization 混淆了。

首先来看一下维基百科的描述:

In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.

简单来说,memoization 是一种优化技术,主要用于通过存储昂贵的函数调用的结果来加速计算机程序,并在再次发生相同的输入时返回缓存的结果。

本文首先介绍一个简单的使用 memoization 优化技术的例子,然后解读 underscore 和 reselect 库中使用 memoization 的源码,加深理解。

阶乘

不使用 memoization

不假思索,我们会立即写下如下的代码:

const factorial = n => {
    if (n === 1) {
        return 1
    } else {
        return factorial(n - 1) * n
    }
};

使用 memoization

const cache = []
const factorial = n => {
    if (n === 1) {
        return 1
    } else if (cache[n - 1]) {
        return cache[n - 1]
    } else {
        let result = factorial(n - 1) * n
        cache[n - 1] = result
        return result
    }
};

使用 闭包 和 memoization

常见的方式是 闭包 和 memoization 一起搭配使用:

const factorialMemo = () => {
    const cache = []
    const factorial = n => {
        if (n === 1) {
            return 1
        } else if (cache[n - 1]) {
            console.log(`get factorial(${n}) from cache...`)
            return cache[n - 1]
        } else {
            let result = factorial(n - 1) * n
            cache[n - 1] = result
            return result
        }
    }
    return factorial
};
const factorial = factorialMemo();

继续变形,下面这种编写方式是最常见的形式。

const factorialMemo = func => {
    const cache = []
    return function(n) {
        if (cache[n - 1]) {
            console.log(`get factorial(${n}) from cache...`)
            return cache[n - 1]
        } else {
            const result = func.apply(null, arguments)
            cache[n - 1] = result
            return result
        }
    }
}

const factorial = factorialMemo(function(n) {
    return n === 1 ? 1 : factorial(n - 1) * n
});

从阶乘的这个例子可以知道 memoization 是一个空间换时间的方式,存储执行结果,下次再次发生相同的输入会直接输出结果,提高了执行的速度。

underscore 源码中的 memoization

// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
    var memoize = function(key) {
        var cache = memoize.cache;
        var address = '' + (hasher ? hasher.apply(this, arguments) : key);
        if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
        return cache[address];
    };
    memoize.cache = {};
    return memoize;
};

代码一目了然,使用 _.memoize 来实现阶乘如下:

const factorial = _.memoize(function(n) {
    return n === 1 ? 1 : factorial(n - 1) * n
});

参照这个源码,上面的阶乘继续可以变形如下:

const factorialMemo = func => {
    const memoize = function(n) {
        const cache = memoize.cache
        if (cache[n - 1]) {
            console.log(`get factorial(${n}) from cache...`)
            return cache[n - 1]
        } else {
            const result = func.apply(null, arguments)
            cache[n - 1] = result
            return result
        }
    }
    memoize.cache = []
    return memoize
}

const factorial = factorialMemo(function(n) {
    return n === 1 ? 1 : factorial(n - 1) * n
});

reselect 源码中的 memoization

export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
    let lastArgs = null
    let lastResult = null
    // we reference arguments instead of spreading them for performance reasons
    return function () {
        if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
            // apply arguments instead of spreading for performance.
            lastResult = func.apply(null, arguments)
        }

        lastArgs = arguments
        return lastResult
    }
};

从源码可以知道当 lastArgs 与 arguments 相同的时候,就不会再执行 func。

总结

memoization 是一种优化技术,避免一些不必要的重复计算,可以提高计算速度。

参考

  1. Memoization wiki
  2. Understanding JavaScript Memoization In 3 Minutes
  3. Underscore
  4. reselect
  5. Implementing Memoization in JavaScript
查看原文

赞 6 收藏 2 评论 0

山顶的男人 赞了文章 · 2018-08-07

hexo的next主题个性化配置教程


看到有些next主题的网站很炫酷,那么是怎么配置的呢?接下来我会讲一讲如何实现一些炫酷的效果

主要有以下33种:

  • 在右上角或者左上角实现fork me on github
  • 添加RSS
  • 添加动态背景
  • 实现点击出现桃心效果
  • 修改文章内链接文本样式
  • 修改文章底部的那个带#号的标签
  • 在每篇文章末尾统一添加“本文结束”标记
  • 修改作者头像并旋转
  • 博文压缩
  • 修改``代码块自定义样式
  • 侧边栏社交小图标设置
  • 主页文章添加阴影效果
  • 在网站底部加上访问量
  • 添加热度
  • 网站底部字数统计
  • 添加 README.md 文件
  • 设置网站的图标Favicon
  • 实现统计功能
  • 添加顶部加载条
  • 在文章底部增加版权信息
  • 添加网易云跟帖(跟帖关闭,已失效,改为来必力)
  • 隐藏网页底部powered By Hexo / 强力驱动
  • 修改网页底部的桃心
  • 文章加密访问
  • 添加jiathis分享
  • 博文置顶
  • 修改字体大小
  • 修改打赏字体不闪动
  • 侧边栏推荐阅读
  • 自定义鼠标样式
  • 为博客加上萌萌的宠物
  • DaoVoice 在线联系
  • 点击爆炸效果

1. 在右上角或者左上角实现fork me on github

实现效果图

具体实现方法

点击这里或者这里挑选自己喜欢的样式,并复制代码。 例如,我是复制如下代码:

然后粘贴刚才复制的代码到themes/next/layout/_layout.swig文件中(放在<div class="headband"></div>的下面),并把href改为你的github地址


2.添加RSS

实现效果图

具体实现方法

切换到你的blog(我是取名blog,具体的看你们的取名是什么)的路径,例如我是在/Users/chenzekun/Code/Hexo/blog这个路径上,也就是在你的根目录下

然后安装 Hexo 插件:(这个插件会放在node_modules这个文件夹里)

$ npm install --save hexo-generator-feed

接下来打开画红线的文件,如下图:

在里面的末尾添加:(请注意在冒号后面要加一个空格,不然会发生错误!)

# Extensions
## Plugins: http://hexo.io/plugins/
plugins: hexo-generate-feed

然后打开next主题文件夹里面的_config.yml,在里面配置为如下样子:(就是在rss:的后面加上/atom.xml,注意在冒号后面要加一个空格)

# Set rss to false to disable feed link.
# Leave rss as empty to use site's feed link.
# Set rss to specific value if you have burned your feed already.
rss: /atom.xml

配置完之后运行:

$ hexo g

重新生成一次,你会在 ./public 文件夹中看到 atom.xml 文件。然后启动服务器查看是否有效,之后再部署到 Github 中。


3. 添加动态背景

实现效果图

具体实现方法

这个我之前有一篇文章有讲过了,详情点击我的博客


4. 实现点击出现桃心效果

实现效果图

具体实现方法

在网址输入如下

http://7u2ss1.com1.z0.glb.clouddn.com/love.js

然后将里面的代码copy一下,新建love.js文件并且将代码复制进去,然后保存。将love.js文件放到路径/themes/next/source/js/src里面,然后打开\themes\next\layout\_layout.swig文件,在末尾(在前面引用会出现找不到的bug)添加以下代码:

<!-- 页面点击小红心 -->
<script type="text/javascript" data-original="/js/src/love.js"></script>

5. 修改文章内链接文本样式

实现效果图

具体实现方法

修改文件 themes\next\source\css\_common\components\post\post.styl ,在末尾添加如下css样式,:

// 文章内链接文本样式
.post-body p a{
  color: #0593d3;
  border-bottom: none;
  border-bottom: 1px solid #0593d3;
  &:hover {
    color: #fc6423;
    border-bottom: none;
    border-bottom: 1px solid #fc6423;
  }
}

其中选择 .post-body 是为了不影响标题,选择 p 是为了不影响首页“阅读全文”的显示样式,颜色可以自己定义。


6. 修改文章底部的那个带#号的标签

实现效果图

具体实现方法

修改模板/themes/next/layout/_macro/post.swig,搜索 rel="tag">#,将 # 换成 <i class="fa fa-tag"></i>


7. 在每篇文章末尾统一添加“本文结束”标记

实现效果图

具体实现方法

在路径 \themes\next\layout\_macro 中新建 passage-end-tag.swig 文件,并添加以下内容:

<div>
    {% if not is_index %}
        <div style="text-align:center;color: #ccc;font-size:14px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    {% endif %}
</div>

接着打开\themes\next\layout\_macro\post.swig文件,在post-body 之后, post-footer 之前添加如下画红色部分代码(post-footer之前两个DIV):

代码如下:

<div>
  {% if not is_index %}
    {% include 'passage-end-tag.swig' %}
  {% endif %}
</div>

然后打开主题配置文件(_config.yml),在末尾添加:

# 文章末尾添加“本文结束”标记
passage_end_tag:
  enabled: true

完成以上设置之后,在每篇文章之后都会添加如上效果图的样子。


8. 修改作者头像并旋转

实现效果图

具体实现方法

打开\themes\next\source\css\_common\components\sidebar\sidebar-author.styl,在里面添加如下代码:

.site-author-image {
  display: block;
  margin: 0 auto;
  padding: $site-author-image-padding;
  max-width: $site-author-image-width;
  height: $site-author-image-height;
  border: $site-author-image-border-width solid $site-author-image-border-color;

  /* 头像圆形 */
  border-radius: 80px;
  -webkit-border-radius: 80px;
  -moz-border-radius: 80px;
  box-shadow: inset 0 -1px 0 #333sf;

  /* 设置循环动画 [animation: (play)动画名称 (2s)动画播放时长单位秒或微秒 (ase-out)动画播放的速度曲线为以低速结束 
    (1s)等待1秒然后开始动画 (1)动画播放次数(infinite为循环播放) ]*/
 

  /* 鼠标经过头像旋转360度 */
  -webkit-transition: -webkit-transform 1.0s ease-out;
  -moz-transition: -moz-transform 1.0s ease-out;
  transition: transform 1.0s ease-out;
}

img:hover {
  /* 鼠标经过停止头像旋转 
  -webkit-animation-play-state:paused;
  animation-play-state:paused;*/

  /* 鼠标经过头像旋转360度 */
  -webkit-transform: rotateZ(360deg);
  -moz-transform: rotateZ(360deg);
  transform: rotateZ(360deg);
}

/* Z 轴旋转动画 */
@-webkit-keyframes play {
  0% {
    -webkit-transform: rotateZ(0deg);
  }
  100% {
    -webkit-transform: rotateZ(-360deg);
  }
}
@-moz-keyframes play {
  0% {
    -moz-transform: rotateZ(0deg);
  }
  100% {
    -moz-transform: rotateZ(-360deg);
  }
}
@keyframes play {
  0% {
    transform: rotateZ(0deg);
  }
  100% {
    transform: rotateZ(-360deg);
  }
}

9. 博文压缩

在站点的根目录下执行以下命令:

$ npm install gulp -g
$ npm install gulp-minify-css gulp-uglify gulp-htmlmin gulp-htmlclean gulp --save

在如下图所示,新建 gulpfile.js ,并填入以下内容:

var gulp = require('gulp');
var minifycss = require('gulp-minify-css');
var uglify = require('gulp-uglify');
var htmlmin = require('gulp-htmlmin');
var htmlclean = require('gulp-htmlclean');
// 压缩 public 目录 css
gulp.task('minify-css', function() {
    return gulp.src('./public/**/*.css')
        .pipe(minifycss())
        .pipe(gulp.dest('./public'));
});
// 压缩 public 目录 html
gulp.task('minify-html', function() {
  return gulp.src('./public/**/*.html')
    .pipe(htmlclean())
    .pipe(htmlmin({
         removeComments: true,
         minifyJS: true,
         minifyCSS: true,
         minifyURLs: true,
    }))
    .pipe(gulp.dest('./public'))
});
// 压缩 public/js 目录 js
gulp.task('minify-js', function() {
    return gulp.src('./public/**/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('./public'));
});
// 执行 gulp 命令时执行的任务
gulp.task('default', [
    'minify-html','minify-css','minify-js'
]);

生成博文是执行 hexo g && gulp 就会根据 gulpfile.js 中的配置,对 public 目录中的静态资源文件进行压缩。


10. 修改``代码块自定义样式

实现效果图

具体实现方法

打开\themes\next\source\css\_custom\custom.styl,向里面加入:(颜色可以自己定义)

// Custom styles.
code {
    color: #ff7600;
    background: #fbf7f8;
    margin: 2px;
}
// 大代码块的自定义样式
.highlight, pre {
    margin: 5px 0;
    padding: 5px;
    border-radius: 3px;
}
.highlight, code, pre {
    border: 1px solid #d6d6d6;
}

11. 侧边栏社交小图标设置

实现效果图

具体实现方法

打开主题配置文件(_config.yml),搜索social_icons:,在图标库找自己喜欢的小图标,并将名字复制在如下位置,保存即可


12. 主页文章添加阴影效果

实现效果图

具体实现方法

打开\themes\next\source\css\_custom\custom.styl,向里面加入:

// 主页文章添加阴影效果
 .post {
   margin-top: 60px;
   margin-bottom: 60px;
   padding: 25px;
   -webkit-box-shadow: 0 0 5px rgba(202, 203, 203, .5);
   -moz-box-shadow: 0 0 5px rgba(202, 203, 204, .5);
  }

13. 在网站底部加上访问量

实现效果图

具体实现方法
打开\themes\next\layout\_partials\footer.swig文件,在copyright前加上画红线这句话:

代码如下:

<script async data-original="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

然后再合适的位置添加显示统计的代码,如图:

代码如下:

<div class="powered-by">
<i class="fa fa-user-md"></i><span id="busuanzi_container_site_uv">
  本站访客数:<span id="busuanzi_value_site_uv"></span>
</span>
</div>

在这里有两中不同计算方式的统计代码:

  1. pv的方式,单个用户连续点击n篇文章,记录n次访问量
<span id="busuanzi_container_site_pv">
    本站总访问量<span id="busuanzi_value_site_pv"></span>次
</span>
  1. uv的方式,单个用户连续点击n篇文章,只记录1次访客数
<span id="busuanzi_container_site_uv">
  本站总访问量<span id="busuanzi_value_site_uv"></span>次
</span>

添加之后再执行hexo d -g,然后再刷新页面就能看到效果


14. 添加热度

实现效果图

具体实现方法

next主题集成leanCloud,打开/themes/next/layout/_macro/post.swig,在画红线的区域添加

然后打开,/themes/next/languages/zh-Hans.yml,将画红框的改为热度就可以了


15. 网站底部字数统计

实现效果图

具体方法实现
切换到根目录下,然后运行如下代码

$ npm install hexo-wordcount --save

然后在/themes/next/layout/_partials/footer.swig文件尾部加上:

<div class="theme-info">
  <div class="powered-by"></div>
  <span class="post-count">博客全站共{{ totalcount(site) }}字</span>
</div>

16. 添加 README.md 文件

每个项目下一般都有一个 README.md 文件,但是使用 hexo 部署到仓库后,项目下是没有 README.md 文件的。

在 Hexo 目录下的 source 根目录下添加一个 README.md 文件,修改站点配置文件 _config.yml ,将 skip_render 参数的值设置为

skip_render: README.md

保存退出即可。再次使用 hexo d 命令部署博客的时候就不会在渲染 README.md 这个文件了。


17. 设置网站的图标Favicon

实现效果图

具体方法实现

EasyIcon中找一张(32*32)的ico图标,或者去别的网站下载或者制作,并将图标名称改为favicon.ico,然后把图标放在/themes/next/source/images里,并且修改主题配置文件:

# Put your favicon.ico into `hexo-site/source/` directory.
favicon: /favicon.ico

18. 实现统计功能

实现效果图

具体实现方法

在根目录下安装 hexo-wordcount,运行:

$ npm install hexo-wordcount --save

然后在主题的配置文件中,配置如下:

# Post wordcount display settings
# Dependencies: https://github.com/willin/hexo-wordcount
post_wordcount:
  item_text: true
  wordcount: true
  min2read: true

19. 添加顶部加载条

实现效果图

具体实现方法

打开/themes/next/layout/_partials/head.swig文件,添加红框上的代码

代码如下:

<script data-original="//cdn.bootcss.com/pace/1.0.2/pace.min.js"></script>
<link href="//cdn.bootcss.com/pace/1.0.2/themes/pink/pace-theme-flash.css" rel="stylesheet">

但是,默认的是粉色的,要改变颜色可以在/themes/next/layout/_partials/head.swig文件中添加如下代码(接在刚才link的后面)

<style>
    .pace .pace-progress {
        background: #1E92FB; /*进度条颜色*/
        height: 3px;
    }
    .pace .pace-progress-inner {
         box-shadow: 0 0 10px #1E92FB, 0 0 5px     #1E92FB; /*阴影颜色*/
    }
    .pace .pace-activity {
        border-top-color: #1E92FB;    /*上边框颜色*/
        border-left-color: #1E92FB;    /*左边框颜色*/
    }
</style>
目前,博主的增加顶部加载条的pull request 已被Merge😀===>详情
现在升级最新版的next主题,升级后只需修改主题配置文件(_config.yml)将pace: false改为pace: true就行了,你还可以换不同样式的加载条,如下图:

20. 在文章底部增加版权信息

实现效果图

在目录 next/layout/_macro/下添加 my-copyright.swig

{% if page.copyright %}
<div class="my_post_copyright">
  <script data-original="//cdn.bootcss.com/clipboard.js/1.5.10/clipboard.min.js"></script>
  
  <!-- JS库 sweetalert 可修改路径 -->
  <script data-original="https://cdn.bootcss.com/jquery/2.0.0/jquery.min.js"></script>
  <script data-original="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
  <p><span>本文标题:</span><a href="{{ url_for(page.path) }}">{{ page.title }}</a></p>
  <p><span>文章作者:</span><a href="/" title="访问 {{ theme.author }} 的个人博客">{{ theme.author }}</a></p>
  <p><span>发布时间:</span>{{ page.date.format("YYYY年MM月DD日 - HH:mm") }}</p>
  <p><span>最后更新:</span>{{ page.updated.format("YYYY年MM月DD日 - HH:mm") }}</p>
  <p><span>原始链接:</span><a href="{{ url_for(page.path) }}" title="{{ page.title }}">{{ page.permalink }}</a>
    <span class="copy-path"  title="点击复制文章链接"><i class="fa fa-clipboard" data-clipboard-text="{{ page.permalink }}"  aria-label="复制成功!"></i></span>
  </p>
  <p><span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
</div>
<script> 
    var clipboard = new Clipboard('.fa-clipboard');
    $(".fa-clipboard").click(function(){
      clipboard.on('success', function(){
        swal({   
          title: "",   
          text: '复制成功',
          icon: "success", 
          showConfirmButton: true
          });
    });
    });  
</script>
{% endif %}

在目录next/source/css/_common/components/post/下添加my-post-copyright.styl

.my_post_copyright {
  width: 85%;
  max-width: 45em;
  margin: 2.8em auto 0;
  padding: 0.5em 1.0em;
  border: 1px solid #d3d3d3;
  font-size: 0.93rem;
  line-height: 1.6em;
  word-break: break-all;
  background: rgba(255,255,255,0.4);
}
.my_post_copyright p{margin:0;}
.my_post_copyright span {
  display: inline-block;
  width: 5.2em;
  color: #b5b5b5;
  font-weight: bold;
}
.my_post_copyright .raw {
  margin-left: 1em;
  width: 5em;
}
.my_post_copyright a {
  color: #808080;
  border-bottom:0;
}
.my_post_copyright a:hover {
  color: #a3d2a3;
  text-decoration: underline;
}
.my_post_copyright:hover .fa-clipboard {
  color: #000;
}
.my_post_copyright .post-url:hover {
  font-weight: normal;
}
.my_post_copyright .copy-path {
  margin-left: 1em;
  width: 1em;
  +mobile(){display:none;}
}
.my_post_copyright .copy-path:hover {
  color: #808080;
  cursor: pointer;
}

修改next/layout/_macro/post.swig,在代码

<div>
      {% if not is_index %}
        {% include 'wechat-subscriber.swig' %}
      {% endif %}
</div>

之前添加增加如下代码:

<div>
      {% if not is_index %}
        {% include 'my-copyright.swig' %}
      {% endif %}
</div>

如下:

修改next/source/css/_common/components/post/post.styl文件,在最后一行增加代码:

@import "my-post-copyright"

保存重新生成即可。
如果要在该博文下面增加版权信息的显示,需要在 Markdown 中增加copyright: true的设置,类似:

小技巧:如果你觉得每次都要输入copyright: true很麻烦的话,那么在/scaffolds/post.md文件中添加:

这样每次hexo new "你的内容"之后,生成的md文件会自动把copyright: 加到里面去
(注意:如果解析出来之后,你的原始链接有问题:如:http://yoursite.com/前端小项目:使用canvas绘画哆啦A梦.html,那么在根目录下_config.yml中写成类似这样:)
就行了。

21. 添加网易云跟帖(跟帖关闭,已失效,改为来必力)

实现效果图

具体方法实现
有两种实现方法:
①更新next主题,因为最新版本的主题已经支持这种评论。直接在主题配置文件_config.yml 文件中添加如下配置:

gentie_productKey: #your-gentie-product-key

②如果你不想更新的话,那么按下面步骤进行:
首先,还是在主题配置文件_config.yml 文件中添加如下配置:

gentie_productKey: #your-gentie-product-key

你的productKey就是下面画红线部分

然后在在 layout/_scripts/third-party/comments/ 目录中添加 gentie.swig,文件内容如下:

{% if not (theme.duoshuo and theme.duoshuo.shortname) and not theme.duoshuo_shortname and not theme.disqus_shortname and not theme.hypercomments_id %}
  {% if theme.gentie_productKey %}
    {% set gentie_productKey = theme.gentie_productKey %}
    <script>
      var cloudTieConfig = {
        url: document.location.href, 
        sourceId: "",
        productKey: "{{gentie_productKey}}",
        target: "cloud-tie-wrapper"
      };
    </script>
    <script data-original="https://img1.ws.126.net/f2e/tie/yun/sdk/loader.js"></script>
  {% endif %}
{% endif %}

然后在layout/_scripts/third-party/comments.swig文件中追加:

{% include './comments/gentie.swig' %}

最后,在 layout/_partials/comments.swig 文件中条件最后追加网易云跟帖插件引用的判断逻辑:

{% elseif theme.gentie_productKey %}
      <div id="cloud-tie-wrapper" class="cloud-tie-wrapper">
      </div>

具体位置如下:

可能你hexo s时可能看不到,直接hexo d就可以看到了


近日,我朋友发来消息,说网易云跟帖要关了,我网上查了一下,果然如此

😭都是泪,上次用了多说,结果多说关了,接着是网易云跟帖😷,这次直接用国外的来必力,应该不会这么容易关吧😏

方法其实还是跟上面差不多的

首先在 _config.yml 文件中添加如下配置:(注意!如果主题是最新版的,直接写你的liver_uid就行了)

# Support for LiveRe comments system.
# You can get your uid from https://livere.com/insight/myCode (General web site)
livere_uid: your uid

其中,livere_uid 就是画红线的部分

然后在 layout/_scripts/third-party/comments/ 目录中添加 livere.swig,文件内容如下:

{% if not (theme.duoshuo and theme.duoshuo.shortname) and not theme.duoshuo_shortname and not theme.disqus_shortname and not theme.hypercomments_id and not theme.gentie_productKey %}
  {% if theme.livere_uid %}
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  {% endif %}
{% endif %}

然后在 layout/_scripts/third-party/comments.swig 文件中追加:

{% include './comments/livere.swig' %}

最后,在 layout/_partials/comments.swig 文件中条件最后追加 LiveRe 插件是否引用的判断逻辑:

{% elseif theme.livere_uid %}
      <div id="lv-container" data-id="city" data-uid="{{ theme.livere_uid }}"></div>
{% endif %}


22. 隐藏网页底部powered By Hexo / 强力驱动

打开themes/next/layout/_partials/footer.swig,使用”<!-- -->”隐藏之间的代码即可,或者直接删除。位置如图:


23. 修改网页底部的桃心

还是打开themes/next/layout/_partials/footer.swig,找到:
,然后还是在图标库中找到你自己喜欢的图标,然后修改画红线的部分就可以了。


24. 文章加密访问

实现效果图

具体实现方法

打开themes->next->layout->_partials->head.swig文件,在以下位置插入这样一段代码:

代码如下:

<script>
    (function () {
        if ('{{ page.password }}') {
            if (prompt('请输入文章密码') !== '{{ page.password }}') {
                alert('密码错误!');
                if (history.length === 1) {
                    location.replace("http://xxxxxxx.xxx"); // 这里替换成你的首页
                } else {
                    history.back();
                }
            }
        }
    })();
</script>

然后在文章上写成类似这样:


25. 添加jiathis分享

注意:JiaThis 不支持 https

主题配置文件中,JiaThis为true,就行了,如下图

默认是这样子的:

如果你想自定义话,打开themes/next/layout/_partials/share/jiathis.swig修改画红线部分就可以了


26. 博文置顶

修改 hero-generator-index 插件,把文件:node_modules/hexo-generator-index/lib/generator.js 内的代码替换为:

'use strict';
var pagination = require('hexo-pagination');
module.exports = function(locals){
  var config = this.config;
  var posts = locals.posts;
    posts.data = posts.data.sort(function(a, b) {
        if(a.top && b.top) { // 两篇文章top都有定义
            if(a.top == b.top) return b.date - a.date; // 若top值一样则按照文章日期降序排
            else return b.top - a.top; // 否则按照top值降序排
        }
        else if(a.top && !b.top) { // 以下是只有一篇文章top有定义,那么将有top的排在前面(这里用异或操作居然不行233)
            return -1;
        }
        else if(!a.top && b.top) {
            return 1;
        }
        else return b.date - a.date; // 都没定义按照文章日期降序排
    });
  var paginationDir = config.pagination_dir || 'page';
  return pagination('', posts, {
    perPage: config.index_generator.per_page,
    layout: ['index', 'archive'],
    format: paginationDir + '/%d/',
    data: {
      __index: true
    }
  });
};

在文章中添加 top 值,数值越大文章越靠前,如

---
title: 解决Charles乱码问题
date: 2017-05-22 22:45:48
tags: 技巧
categories: 技巧
copyright: true
top: 100
---

27. 修改字体大小

打开\themes\next\source\css\ _variables\base.styl文件,将$font-size-base改成16px,如下所示:

$font-size-base            =16px

28. 修改打赏字体不闪动

修改文件next/source/css/_common/components/post/post-reward.styl,然后注释其中的函数wechat:hoveralipay:hover,如下:

/* 注释文字闪动函数
 #wechat:hover p{
    animation: roll 0.1s infinite linear;
    -webkit-animation: roll 0.1s infinite linear;
    -moz-animation: roll 0.1s infinite linear;
}
 #alipay:hover p{
   animation: roll 0.1s infinite linear;
    -webkit-animation: roll 0.1s infinite linear;
    -moz-animation: roll 0.1s infinite linear;
}
*/

29. 侧边栏推荐阅读

今天有位网友问推荐阅读是怎么弄,其实挺简单的,打开主题配置文件修改成这样就行了(links里面写你想要的链接):

# Blogrolls
links_title: 推荐阅读
#links_layout: block
links_layout: inline
links:
  优设: http://www.uisdc.com/
  张鑫旭: http://www.zhangxinxu.com/
  Web前端导航: http://www.alloyteam.com/nav/
  前端书籍资料: http://www.36zhen.com/t?id=3448
  百度前端技术学院: http://ife.baidu.com/
  google前端开发基础: http://wf.uisdc.com/cn/
  

30. 自定义鼠标样式

打开themes/next/source/css/_custom/custom.styl,在里面写下如下代码

// 鼠标样式
  * {
      cursor: url("http://om8u46rmb.bkt.clouddn.com/sword2.ico"),auto!important
  }
  :active {
      cursor: url("http://om8u46rmb.bkt.clouddn.com/sword1.ico"),auto!important
  }

其中 url 里面必须是 ico 图片,ico 图片可以上传到网上(我是使用七牛云图床),然后获取外链,复制到 url 里就行了


31.为博客加上萌萌的宠物

实现效果图

具体实现方法
在终端切换到你的博客的路径里,然后输入如下代码:

npm install -save hexo-helper-live2d

~~然后打开Hexo/blog/themes/next/layout
_layout.swig,将下面代码放到</body>之前:~~

(注意,由于官方更新了包,所以画删除线的不用弄)

然后在在 hexo_config.yml 中添加参数:(具体配置可以看官方文档

live2d:
  enable: true
  scriptFrom: local
  pluginRootPath: live2dw/
  pluginJsPath: lib/
  pluginModelPath: assets/
  model:
    use: live2d-widget-model-wanko
  display:
    position: right
    width: 150
    height: 300
  mobile:
    show: true

然后hexo clean ,hexo g ,hexo d 就可以看到了。

注意!如果你在 hexo d 的时候出现我下面这个问题

你可以这样,首先删除hexo 下面的.deploy_git文件夹,然后运行
git config --global core.autocrlf false
重新 hexo clean,hexo g,hexo d就行了

32.DaoVoice 在线联系

实现效果图

具体实现方法

首先在 daovoice 注册账号,邀请码0f81ff2f,注册完成后会得到一个 app_id :


记下这个 app_id的值,然后打开/themes/next/layout/_partials/head.swig,写下如下代码:

{% if theme.daovoice %}
  <script>
  (function(i,s,o,g,r,a,m){i["DaoVoiceObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.data-original=g;a.charset="utf-8";m.parentNode.insertBefore(a,m)})(window,document,"script",('https:' == document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/0f81ff2f.js","daovoice")
  daovoice('init', {
      app_id: "{{theme.daovoice_app_id}}"
    });
  daovoice('update');
  </script>
{% endif %}

接着打开主题配置文件,在最后写下如下代码:

# Online contact 
daovoice: true

daovoice_app_id: 这里填你的刚才获得的 app_id

重新 hexo g ,hexo s 就能看到效果了。

安装成功后可以在DaoVoice 控制台上的聊天设置里设置聊天窗口样式,附上我的设置


33.点击爆炸效果

实现效果图

实现方法

跟那个红心是差不多的,首先在themes/next/source/js/src里面建一个叫fireworks.js的文件,代码如下:

"use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)}"use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)};

打开themes/next/layout/_layout.swig,在</body>上面写下如下代码:

{% if theme.fireworks %}
   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
   <script type="text/javascript" data-original="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" data-original="/js/src/fireworks.js"></script>
{% endif %}

打开主题配置文件,在里面最后写下:

# Fireworks
fireworks: true

完😀


致谢

感谢大神们的文章,真的学到了许多,有些忘了记录下来,在这里由衷的感谢。虽然比较折腾,但是确实满满的成就感,Road endless its long and far, I will seek up and down!

欢迎访问我的博客


参考的文章:

  1. http://blog.csdn.net/MasterAn...
  2. http://zidingyi4qh.com/2017/0...
  3. https://fuyis.me/2017/01/25/H...
  4. http://www.vitah.net/posts/20...
  5. http://thief.one/2017/03/03/H...
  6. https://chanshiyu.com/2017/11...

查看原文

赞 72 收藏 98 评论 45

山顶的男人 赞了文章 · 2018-08-07

hexo的next主题个性化配置教程


看到有些next主题的网站很炫酷,那么是怎么配置的呢?接下来我会讲一讲如何实现一些炫酷的效果

主要有以下33种:

  • 在右上角或者左上角实现fork me on github
  • 添加RSS
  • 添加动态背景
  • 实现点击出现桃心效果
  • 修改文章内链接文本样式
  • 修改文章底部的那个带#号的标签
  • 在每篇文章末尾统一添加“本文结束”标记
  • 修改作者头像并旋转
  • 博文压缩
  • 修改``代码块自定义样式
  • 侧边栏社交小图标设置
  • 主页文章添加阴影效果
  • 在网站底部加上访问量
  • 添加热度
  • 网站底部字数统计
  • 添加 README.md 文件
  • 设置网站的图标Favicon
  • 实现统计功能
  • 添加顶部加载条
  • 在文章底部增加版权信息
  • 添加网易云跟帖(跟帖关闭,已失效,改为来必力)
  • 隐藏网页底部powered By Hexo / 强力驱动
  • 修改网页底部的桃心
  • 文章加密访问
  • 添加jiathis分享
  • 博文置顶
  • 修改字体大小
  • 修改打赏字体不闪动
  • 侧边栏推荐阅读
  • 自定义鼠标样式
  • 为博客加上萌萌的宠物
  • DaoVoice 在线联系
  • 点击爆炸效果

1. 在右上角或者左上角实现fork me on github

实现效果图

具体实现方法

点击这里或者这里挑选自己喜欢的样式,并复制代码。 例如,我是复制如下代码:

然后粘贴刚才复制的代码到themes/next/layout/_layout.swig文件中(放在<div class="headband"></div>的下面),并把href改为你的github地址


2.添加RSS

实现效果图

具体实现方法

切换到你的blog(我是取名blog,具体的看你们的取名是什么)的路径,例如我是在/Users/chenzekun/Code/Hexo/blog这个路径上,也就是在你的根目录下

然后安装 Hexo 插件:(这个插件会放在node_modules这个文件夹里)

$ npm install --save hexo-generator-feed

接下来打开画红线的文件,如下图:

在里面的末尾添加:(请注意在冒号后面要加一个空格,不然会发生错误!)

# Extensions
## Plugins: http://hexo.io/plugins/
plugins: hexo-generate-feed

然后打开next主题文件夹里面的_config.yml,在里面配置为如下样子:(就是在rss:的后面加上/atom.xml,注意在冒号后面要加一个空格)

# Set rss to false to disable feed link.
# Leave rss as empty to use site's feed link.
# Set rss to specific value if you have burned your feed already.
rss: /atom.xml

配置完之后运行:

$ hexo g

重新生成一次,你会在 ./public 文件夹中看到 atom.xml 文件。然后启动服务器查看是否有效,之后再部署到 Github 中。


3. 添加动态背景

实现效果图

具体实现方法

这个我之前有一篇文章有讲过了,详情点击我的博客


4. 实现点击出现桃心效果

实现效果图

具体实现方法

在网址输入如下

http://7u2ss1.com1.z0.glb.clouddn.com/love.js

然后将里面的代码copy一下,新建love.js文件并且将代码复制进去,然后保存。将love.js文件放到路径/themes/next/source/js/src里面,然后打开\themes\next\layout\_layout.swig文件,在末尾(在前面引用会出现找不到的bug)添加以下代码:

<!-- 页面点击小红心 -->
<script type="text/javascript" data-original="/js/src/love.js"></script>

5. 修改文章内链接文本样式

实现效果图

具体实现方法

修改文件 themes\next\source\css\_common\components\post\post.styl ,在末尾添加如下css样式,:

// 文章内链接文本样式
.post-body p a{
  color: #0593d3;
  border-bottom: none;
  border-bottom: 1px solid #0593d3;
  &:hover {
    color: #fc6423;
    border-bottom: none;
    border-bottom: 1px solid #fc6423;
  }
}

其中选择 .post-body 是为了不影响标题,选择 p 是为了不影响首页“阅读全文”的显示样式,颜色可以自己定义。


6. 修改文章底部的那个带#号的标签

实现效果图

具体实现方法

修改模板/themes/next/layout/_macro/post.swig,搜索 rel="tag">#,将 # 换成 <i class="fa fa-tag"></i>


7. 在每篇文章末尾统一添加“本文结束”标记

实现效果图

具体实现方法

在路径 \themes\next\layout\_macro 中新建 passage-end-tag.swig 文件,并添加以下内容:

<div>
    {% if not is_index %}
        <div style="text-align:center;color: #ccc;font-size:14px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    {% endif %}
</div>

接着打开\themes\next\layout\_macro\post.swig文件,在post-body 之后, post-footer 之前添加如下画红色部分代码(post-footer之前两个DIV):

代码如下:

<div>
  {% if not is_index %}
    {% include 'passage-end-tag.swig' %}
  {% endif %}
</div>

然后打开主题配置文件(_config.yml),在末尾添加:

# 文章末尾添加“本文结束”标记
passage_end_tag:
  enabled: true

完成以上设置之后,在每篇文章之后都会添加如上效果图的样子。


8. 修改作者头像并旋转

实现效果图

具体实现方法

打开\themes\next\source\css\_common\components\sidebar\sidebar-author.styl,在里面添加如下代码:

.site-author-image {
  display: block;
  margin: 0 auto;
  padding: $site-author-image-padding;
  max-width: $site-author-image-width;
  height: $site-author-image-height;
  border: $site-author-image-border-width solid $site-author-image-border-color;

  /* 头像圆形 */
  border-radius: 80px;
  -webkit-border-radius: 80px;
  -moz-border-radius: 80px;
  box-shadow: inset 0 -1px 0 #333sf;

  /* 设置循环动画 [animation: (play)动画名称 (2s)动画播放时长单位秒或微秒 (ase-out)动画播放的速度曲线为以低速结束 
    (1s)等待1秒然后开始动画 (1)动画播放次数(infinite为循环播放) ]*/
 

  /* 鼠标经过头像旋转360度 */
  -webkit-transition: -webkit-transform 1.0s ease-out;
  -moz-transition: -moz-transform 1.0s ease-out;
  transition: transform 1.0s ease-out;
}

img:hover {
  /* 鼠标经过停止头像旋转 
  -webkit-animation-play-state:paused;
  animation-play-state:paused;*/

  /* 鼠标经过头像旋转360度 */
  -webkit-transform: rotateZ(360deg);
  -moz-transform: rotateZ(360deg);
  transform: rotateZ(360deg);
}

/* Z 轴旋转动画 */
@-webkit-keyframes play {
  0% {
    -webkit-transform: rotateZ(0deg);
  }
  100% {
    -webkit-transform: rotateZ(-360deg);
  }
}
@-moz-keyframes play {
  0% {
    -moz-transform: rotateZ(0deg);
  }
  100% {
    -moz-transform: rotateZ(-360deg);
  }
}
@keyframes play {
  0% {
    transform: rotateZ(0deg);
  }
  100% {
    transform: rotateZ(-360deg);
  }
}

9. 博文压缩

在站点的根目录下执行以下命令:

$ npm install gulp -g
$ npm install gulp-minify-css gulp-uglify gulp-htmlmin gulp-htmlclean gulp --save

在如下图所示,新建 gulpfile.js ,并填入以下内容:

var gulp = require('gulp');
var minifycss = require('gulp-minify-css');
var uglify = require('gulp-uglify');
var htmlmin = require('gulp-htmlmin');
var htmlclean = require('gulp-htmlclean');
// 压缩 public 目录 css
gulp.task('minify-css', function() {
    return gulp.src('./public/**/*.css')
        .pipe(minifycss())
        .pipe(gulp.dest('./public'));
});
// 压缩 public 目录 html
gulp.task('minify-html', function() {
  return gulp.src('./public/**/*.html')
    .pipe(htmlclean())
    .pipe(htmlmin({
         removeComments: true,
         minifyJS: true,
         minifyCSS: true,
         minifyURLs: true,
    }))
    .pipe(gulp.dest('./public'))
});
// 压缩 public/js 目录 js
gulp.task('minify-js', function() {
    return gulp.src('./public/**/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('./public'));
});
// 执行 gulp 命令时执行的任务
gulp.task('default', [
    'minify-html','minify-css','minify-js'
]);

生成博文是执行 hexo g && gulp 就会根据 gulpfile.js 中的配置,对 public 目录中的静态资源文件进行压缩。


10. 修改``代码块自定义样式

实现效果图

具体实现方法

打开\themes\next\source\css\_custom\custom.styl,向里面加入:(颜色可以自己定义)

// Custom styles.
code {
    color: #ff7600;
    background: #fbf7f8;
    margin: 2px;
}
// 大代码块的自定义样式
.highlight, pre {
    margin: 5px 0;
    padding: 5px;
    border-radius: 3px;
}
.highlight, code, pre {
    border: 1px solid #d6d6d6;
}

11. 侧边栏社交小图标设置

实现效果图

具体实现方法

打开主题配置文件(_config.yml),搜索social_icons:,在图标库找自己喜欢的小图标,并将名字复制在如下位置,保存即可


12. 主页文章添加阴影效果

实现效果图

具体实现方法

打开\themes\next\source\css\_custom\custom.styl,向里面加入:

// 主页文章添加阴影效果
 .post {
   margin-top: 60px;
   margin-bottom: 60px;
   padding: 25px;
   -webkit-box-shadow: 0 0 5px rgba(202, 203, 203, .5);
   -moz-box-shadow: 0 0 5px rgba(202, 203, 204, .5);
  }

13. 在网站底部加上访问量

实现效果图

具体实现方法
打开\themes\next\layout\_partials\footer.swig文件,在copyright前加上画红线这句话:

代码如下:

<script async data-original="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

然后再合适的位置添加显示统计的代码,如图:

代码如下:

<div class="powered-by">
<i class="fa fa-user-md"></i><span id="busuanzi_container_site_uv">
  本站访客数:<span id="busuanzi_value_site_uv"></span>
</span>
</div>

在这里有两中不同计算方式的统计代码:

  1. pv的方式,单个用户连续点击n篇文章,记录n次访问量
<span id="busuanzi_container_site_pv">
    本站总访问量<span id="busuanzi_value_site_pv"></span>次
</span>
  1. uv的方式,单个用户连续点击n篇文章,只记录1次访客数
<span id="busuanzi_container_site_uv">
  本站总访问量<span id="busuanzi_value_site_uv"></span>次
</span>

添加之后再执行hexo d -g,然后再刷新页面就能看到效果


14. 添加热度

实现效果图

具体实现方法

next主题集成leanCloud,打开/themes/next/layout/_macro/post.swig,在画红线的区域添加

然后打开,/themes/next/languages/zh-Hans.yml,将画红框的改为热度就可以了


15. 网站底部字数统计

实现效果图

具体方法实现
切换到根目录下,然后运行如下代码

$ npm install hexo-wordcount --save

然后在/themes/next/layout/_partials/footer.swig文件尾部加上:

<div class="theme-info">
  <div class="powered-by"></div>
  <span class="post-count">博客全站共{{ totalcount(site) }}字</span>
</div>

16. 添加 README.md 文件

每个项目下一般都有一个 README.md 文件,但是使用 hexo 部署到仓库后,项目下是没有 README.md 文件的。

在 Hexo 目录下的 source 根目录下添加一个 README.md 文件,修改站点配置文件 _config.yml ,将 skip_render 参数的值设置为

skip_render: README.md

保存退出即可。再次使用 hexo d 命令部署博客的时候就不会在渲染 README.md 这个文件了。


17. 设置网站的图标Favicon

实现效果图

具体方法实现

EasyIcon中找一张(32*32)的ico图标,或者去别的网站下载或者制作,并将图标名称改为favicon.ico,然后把图标放在/themes/next/source/images里,并且修改主题配置文件:

# Put your favicon.ico into `hexo-site/source/` directory.
favicon: /favicon.ico

18. 实现统计功能

实现效果图

具体实现方法

在根目录下安装 hexo-wordcount,运行:

$ npm install hexo-wordcount --save

然后在主题的配置文件中,配置如下:

# Post wordcount display settings
# Dependencies: https://github.com/willin/hexo-wordcount
post_wordcount:
  item_text: true
  wordcount: true
  min2read: true

19. 添加顶部加载条

实现效果图

具体实现方法

打开/themes/next/layout/_partials/head.swig文件,添加红框上的代码

代码如下:

<script data-original="//cdn.bootcss.com/pace/1.0.2/pace.min.js"></script>
<link href="//cdn.bootcss.com/pace/1.0.2/themes/pink/pace-theme-flash.css" rel="stylesheet">

但是,默认的是粉色的,要改变颜色可以在/themes/next/layout/_partials/head.swig文件中添加如下代码(接在刚才link的后面)

<style>
    .pace .pace-progress {
        background: #1E92FB; /*进度条颜色*/
        height: 3px;
    }
    .pace .pace-progress-inner {
         box-shadow: 0 0 10px #1E92FB, 0 0 5px     #1E92FB; /*阴影颜色*/
    }
    .pace .pace-activity {
        border-top-color: #1E92FB;    /*上边框颜色*/
        border-left-color: #1E92FB;    /*左边框颜色*/
    }
</style>
目前,博主的增加顶部加载条的pull request 已被Merge😀===>详情
现在升级最新版的next主题,升级后只需修改主题配置文件(_config.yml)将pace: false改为pace: true就行了,你还可以换不同样式的加载条,如下图:

20. 在文章底部增加版权信息

实现效果图

在目录 next/layout/_macro/下添加 my-copyright.swig

{% if page.copyright %}
<div class="my_post_copyright">
  <script data-original="//cdn.bootcss.com/clipboard.js/1.5.10/clipboard.min.js"></script>
  
  <!-- JS库 sweetalert 可修改路径 -->
  <script data-original="https://cdn.bootcss.com/jquery/2.0.0/jquery.min.js"></script>
  <script data-original="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
  <p><span>本文标题:</span><a href="{{ url_for(page.path) }}">{{ page.title }}</a></p>
  <p><span>文章作者:</span><a href="/" title="访问 {{ theme.author }} 的个人博客">{{ theme.author }}</a></p>
  <p><span>发布时间:</span>{{ page.date.format("YYYY年MM月DD日 - HH:mm") }}</p>
  <p><span>最后更新:</span>{{ page.updated.format("YYYY年MM月DD日 - HH:mm") }}</p>
  <p><span>原始链接:</span><a href="{{ url_for(page.path) }}" title="{{ page.title }}">{{ page.permalink }}</a>
    <span class="copy-path"  title="点击复制文章链接"><i class="fa fa-clipboard" data-clipboard-text="{{ page.permalink }}"  aria-label="复制成功!"></i></span>
  </p>
  <p><span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
</div>
<script> 
    var clipboard = new Clipboard('.fa-clipboard');
    $(".fa-clipboard").click(function(){
      clipboard.on('success', function(){
        swal({   
          title: "",   
          text: '复制成功',
          icon: "success", 
          showConfirmButton: true
          });
    });
    });  
</script>
{% endif %}

在目录next/source/css/_common/components/post/下添加my-post-copyright.styl

.my_post_copyright {
  width: 85%;
  max-width: 45em;
  margin: 2.8em auto 0;
  padding: 0.5em 1.0em;
  border: 1px solid #d3d3d3;
  font-size: 0.93rem;
  line-height: 1.6em;
  word-break: break-all;
  background: rgba(255,255,255,0.4);
}
.my_post_copyright p{margin:0;}
.my_post_copyright span {
  display: inline-block;
  width: 5.2em;
  color: #b5b5b5;
  font-weight: bold;
}
.my_post_copyright .raw {
  margin-left: 1em;
  width: 5em;
}
.my_post_copyright a {
  color: #808080;
  border-bottom:0;
}
.my_post_copyright a:hover {
  color: #a3d2a3;
  text-decoration: underline;
}
.my_post_copyright:hover .fa-clipboard {
  color: #000;
}
.my_post_copyright .post-url:hover {
  font-weight: normal;
}
.my_post_copyright .copy-path {
  margin-left: 1em;
  width: 1em;
  +mobile(){display:none;}
}
.my_post_copyright .copy-path:hover {
  color: #808080;
  cursor: pointer;
}

修改next/layout/_macro/post.swig,在代码

<div>
      {% if not is_index %}
        {% include 'wechat-subscriber.swig' %}
      {% endif %}
</div>

之前添加增加如下代码:

<div>
      {% if not is_index %}
        {% include 'my-copyright.swig' %}
      {% endif %}
</div>

如下:

修改next/source/css/_common/components/post/post.styl文件,在最后一行增加代码:

@import "my-post-copyright"

保存重新生成即可。
如果要在该博文下面增加版权信息的显示,需要在 Markdown 中增加copyright: true的设置,类似:

小技巧:如果你觉得每次都要输入copyright: true很麻烦的话,那么在/scaffolds/post.md文件中添加:

这样每次hexo new "你的内容"之后,生成的md文件会自动把copyright: 加到里面去
(注意:如果解析出来之后,你的原始链接有问题:如:http://yoursite.com/前端小项目:使用canvas绘画哆啦A梦.html,那么在根目录下_config.yml中写成类似这样:)
就行了。

21. 添加网易云跟帖(跟帖关闭,已失效,改为来必力)

实现效果图

具体方法实现
有两种实现方法:
①更新next主题,因为最新版本的主题已经支持这种评论。直接在主题配置文件_config.yml 文件中添加如下配置:

gentie_productKey: #your-gentie-product-key

②如果你不想更新的话,那么按下面步骤进行:
首先,还是在主题配置文件_config.yml 文件中添加如下配置:

gentie_productKey: #your-gentie-product-key

你的productKey就是下面画红线部分

然后在在 layout/_scripts/third-party/comments/ 目录中添加 gentie.swig,文件内容如下:

{% if not (theme.duoshuo and theme.duoshuo.shortname) and not theme.duoshuo_shortname and not theme.disqus_shortname and not theme.hypercomments_id %}
  {% if theme.gentie_productKey %}
    {% set gentie_productKey = theme.gentie_productKey %}
    <script>
      var cloudTieConfig = {
        url: document.location.href, 
        sourceId: "",
        productKey: "{{gentie_productKey}}",
        target: "cloud-tie-wrapper"
      };
    </script>
    <script data-original="https://img1.ws.126.net/f2e/tie/yun/sdk/loader.js"></script>
  {% endif %}
{% endif %}

然后在layout/_scripts/third-party/comments.swig文件中追加:

{% include './comments/gentie.swig' %}

最后,在 layout/_partials/comments.swig 文件中条件最后追加网易云跟帖插件引用的判断逻辑:

{% elseif theme.gentie_productKey %}
      <div id="cloud-tie-wrapper" class="cloud-tie-wrapper">
      </div>

具体位置如下:

可能你hexo s时可能看不到,直接hexo d就可以看到了


近日,我朋友发来消息,说网易云跟帖要关了,我网上查了一下,果然如此

😭都是泪,上次用了多说,结果多说关了,接着是网易云跟帖😷,这次直接用国外的来必力,应该不会这么容易关吧😏

方法其实还是跟上面差不多的

首先在 _config.yml 文件中添加如下配置:(注意!如果主题是最新版的,直接写你的liver_uid就行了)

# Support for LiveRe comments system.
# You can get your uid from https://livere.com/insight/myCode (General web site)
livere_uid: your uid

其中,livere_uid 就是画红线的部分

然后在 layout/_scripts/third-party/comments/ 目录中添加 livere.swig,文件内容如下:

{% if not (theme.duoshuo and theme.duoshuo.shortname) and not theme.duoshuo_shortname and not theme.disqus_shortname and not theme.hypercomments_id and not theme.gentie_productKey %}
  {% if theme.livere_uid %}
    <script type="text/javascript">
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  {% endif %}
{% endif %}

然后在 layout/_scripts/third-party/comments.swig 文件中追加:

{% include './comments/livere.swig' %}

最后,在 layout/_partials/comments.swig 文件中条件最后追加 LiveRe 插件是否引用的判断逻辑:

{% elseif theme.livere_uid %}
      <div id="lv-container" data-id="city" data-uid="{{ theme.livere_uid }}"></div>
{% endif %}


22. 隐藏网页底部powered By Hexo / 强力驱动

打开themes/next/layout/_partials/footer.swig,使用”<!-- -->”隐藏之间的代码即可,或者直接删除。位置如图:


23. 修改网页底部的桃心

还是打开themes/next/layout/_partials/footer.swig,找到:
,然后还是在图标库中找到你自己喜欢的图标,然后修改画红线的部分就可以了。


24. 文章加密访问

实现效果图

具体实现方法

打开themes->next->layout->_partials->head.swig文件,在以下位置插入这样一段代码:

代码如下:

<script>
    (function () {
        if ('{{ page.password }}') {
            if (prompt('请输入文章密码') !== '{{ page.password }}') {
                alert('密码错误!');
                if (history.length === 1) {
                    location.replace("http://xxxxxxx.xxx"); // 这里替换成你的首页
                } else {
                    history.back();
                }
            }
        }
    })();
</script>

然后在文章上写成类似这样:


25. 添加jiathis分享

注意:JiaThis 不支持 https

主题配置文件中,JiaThis为true,就行了,如下图

默认是这样子的:

如果你想自定义话,打开themes/next/layout/_partials/share/jiathis.swig修改画红线部分就可以了


26. 博文置顶

修改 hero-generator-index 插件,把文件:node_modules/hexo-generator-index/lib/generator.js 内的代码替换为:

'use strict';
var pagination = require('hexo-pagination');
module.exports = function(locals){
  var config = this.config;
  var posts = locals.posts;
    posts.data = posts.data.sort(function(a, b) {
        if(a.top && b.top) { // 两篇文章top都有定义
            if(a.top == b.top) return b.date - a.date; // 若top值一样则按照文章日期降序排
            else return b.top - a.top; // 否则按照top值降序排
        }
        else if(a.top && !b.top) { // 以下是只有一篇文章top有定义,那么将有top的排在前面(这里用异或操作居然不行233)
            return -1;
        }
        else if(!a.top && b.top) {
            return 1;
        }
        else return b.date - a.date; // 都没定义按照文章日期降序排
    });
  var paginationDir = config.pagination_dir || 'page';
  return pagination('', posts, {
    perPage: config.index_generator.per_page,
    layout: ['index', 'archive'],
    format: paginationDir + '/%d/',
    data: {
      __index: true
    }
  });
};

在文章中添加 top 值,数值越大文章越靠前,如

---
title: 解决Charles乱码问题
date: 2017-05-22 22:45:48
tags: 技巧
categories: 技巧
copyright: true
top: 100
---

27. 修改字体大小

打开\themes\next\source\css\ _variables\base.styl文件,将$font-size-base改成16px,如下所示:

$font-size-base            =16px

28. 修改打赏字体不闪动

修改文件next/source/css/_common/components/post/post-reward.styl,然后注释其中的函数wechat:hoveralipay:hover,如下:

/* 注释文字闪动函数
 #wechat:hover p{
    animation: roll 0.1s infinite linear;
    -webkit-animation: roll 0.1s infinite linear;
    -moz-animation: roll 0.1s infinite linear;
}
 #alipay:hover p{
   animation: roll 0.1s infinite linear;
    -webkit-animation: roll 0.1s infinite linear;
    -moz-animation: roll 0.1s infinite linear;
}
*/

29. 侧边栏推荐阅读

今天有位网友问推荐阅读是怎么弄,其实挺简单的,打开主题配置文件修改成这样就行了(links里面写你想要的链接):

# Blogrolls
links_title: 推荐阅读
#links_layout: block
links_layout: inline
links:
  优设: http://www.uisdc.com/
  张鑫旭: http://www.zhangxinxu.com/
  Web前端导航: http://www.alloyteam.com/nav/
  前端书籍资料: http://www.36zhen.com/t?id=3448
  百度前端技术学院: http://ife.baidu.com/
  google前端开发基础: http://wf.uisdc.com/cn/
  

30. 自定义鼠标样式

打开themes/next/source/css/_custom/custom.styl,在里面写下如下代码

// 鼠标样式
  * {
      cursor: url("http://om8u46rmb.bkt.clouddn.com/sword2.ico"),auto!important
  }
  :active {
      cursor: url("http://om8u46rmb.bkt.clouddn.com/sword1.ico"),auto!important
  }

其中 url 里面必须是 ico 图片,ico 图片可以上传到网上(我是使用七牛云图床),然后获取外链,复制到 url 里就行了


31.为博客加上萌萌的宠物

实现效果图

具体实现方法
在终端切换到你的博客的路径里,然后输入如下代码:

npm install -save hexo-helper-live2d

~~然后打开Hexo/blog/themes/next/layout
_layout.swig,将下面代码放到</body>之前:~~

(注意,由于官方更新了包,所以画删除线的不用弄)

然后在在 hexo_config.yml 中添加参数:(具体配置可以看官方文档

live2d:
  enable: true
  scriptFrom: local
  pluginRootPath: live2dw/
  pluginJsPath: lib/
  pluginModelPath: assets/
  model:
    use: live2d-widget-model-wanko
  display:
    position: right
    width: 150
    height: 300
  mobile:
    show: true

然后hexo clean ,hexo g ,hexo d 就可以看到了。

注意!如果你在 hexo d 的时候出现我下面这个问题

你可以这样,首先删除hexo 下面的.deploy_git文件夹,然后运行
git config --global core.autocrlf false
重新 hexo clean,hexo g,hexo d就行了

32.DaoVoice 在线联系

实现效果图

具体实现方法

首先在 daovoice 注册账号,邀请码0f81ff2f,注册完成后会得到一个 app_id :


记下这个 app_id的值,然后打开/themes/next/layout/_partials/head.swig,写下如下代码:

{% if theme.daovoice %}
  <script>
  (function(i,s,o,g,r,a,m){i["DaoVoiceObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.data-original=g;a.charset="utf-8";m.parentNode.insertBefore(a,m)})(window,document,"script",('https:' == document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/0f81ff2f.js","daovoice")
  daovoice('init', {
      app_id: "{{theme.daovoice_app_id}}"
    });
  daovoice('update');
  </script>
{% endif %}

接着打开主题配置文件,在最后写下如下代码:

# Online contact 
daovoice: true

daovoice_app_id: 这里填你的刚才获得的 app_id

重新 hexo g ,hexo s 就能看到效果了。

安装成功后可以在DaoVoice 控制台上的聊天设置里设置聊天窗口样式,附上我的设置


33.点击爆炸效果

实现效果图

实现方法

跟那个红心是差不多的,首先在themes/next/source/js/src里面建一个叫fireworks.js的文件,代码如下:

"use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)}"use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)};

打开themes/next/layout/_layout.swig,在</body>上面写下如下代码:

{% if theme.fireworks %}
   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
   <script type="text/javascript" data-original="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" data-original="/js/src/fireworks.js"></script>
{% endif %}

打开主题配置文件,在里面最后写下:

# Fireworks
fireworks: true

完😀


致谢

感谢大神们的文章,真的学到了许多,有些忘了记录下来,在这里由衷的感谢。虽然比较折腾,但是确实满满的成就感,Road endless its long and far, I will seek up and down!

欢迎访问我的博客


参考的文章:

  1. http://blog.csdn.net/MasterAn...
  2. http://zidingyi4qh.com/2017/0...
  3. https://fuyis.me/2017/01/25/H...
  4. http://www.vitah.net/posts/20...
  5. http://thief.one/2017/03/03/H...
  6. https://chanshiyu.com/2017/11...

查看原文

赞 72 收藏 98 评论 45

山顶的男人 收藏了文章 · 2018-06-29

React性能优化-虚拟Dom原理浅析

本文译自《Optimizing React: Virtual DOM explained》,作者是Alexey IvanovAndy Barnov,来自Evil Martians’ team团队。

译者说:通过一些实际场景和demo,给大家描述React的Virtual Dom Diff一些核心的原理和规则,以及基于这些我们可以做些什么提高应用的性能,很棒的文章。


通过学习React的Virtual DOM的知识,去加速你们的应用吧。对框架内部实现的介绍,比较全面且适合初学者,我们会让JSX更加简单易懂,给你展示React是如何判断要不要重新render,解释如何找到应用的性能瓶颈,以及给大家一些小贴士,如何避免常见错误。

React在前端圈内保持领先的原因之一,因为它的学习曲线非常平易近人:把你的模板包在JSX,了解一下propsstate的概念之后,你就可以轻松写出React代码了。

如果你已经熟悉React的工作方式,可以直接跳至“优化我的代码”篇。

但要真正掌握React,你需要像React一样思考(think in React)。本文也会试图在这个方面帮助你。

下面看看我们其中一个项目中的React table:

"eBay上的一个巨大的React表格  用于业务。"

这个表里有数百个动态(表格内容变化)和可过滤的选项,理解这个框架更精细的点,对于保证顺畅的用户体验至关重要。


当事情出错时,你一定能感觉到。输入字段变得迟缓,复选框需要检查一秒钟,弹窗一个世纪后才出现,等等。


为了能够解决这些问题,我们需要完成一个React组件的整个生命旅程,从一开始的声明定义到在页面上渲染(再然后可能会更新)。系好安全带,我们要发车了!

JSX的背后

这个过程一般在前端会称为“转译”,但其实“汇编”将是一个更精确的术语。

React开发人员敦促你在编写组件时使用一种称为JSX的语法,混合了HTML和JavaScript。但浏览器对JSX及其语法毫无头绪,浏览器只能理解纯碎的JavaScript,所以JSX必须转换成JavaScript。这里是一个div的JSX代码,它有一个class name和一些内容:

<div className='cn'>
  Content!
</div>

以上的代码,被转换成“正经”的JavaScript代码,其实是一个带有一些参数的函数调用:

React.createElement(
  'div',
  { className: 'cn' },
  'Content!'
);

让我们仔细看看这些参数。

  • 第一个是元素的type。对于HTML标签,它将是一个带有标签名称的字符串。
  • 第二个参数是一个包含所有元素属性(attributes)的对象。如果没有,它也可以是空的对象。
  • 剩下的参数都可以认为是元素的子元素(children)。元素中的文本也算作一个child,是个字符串'Content!' 作为函数调用的第三个参数放置。

你应该可以想象,当我们有更多的children时会发生什么:

<div className='cn'>
  Content 1!
  <br />
  Content 2!
</div>
React.createElement(
  'div',
  { className: 'cn' },
  'Content 1!',              // 1st child
  React.createElement('br'), // 2nd child
  'Content 2!'               // 3rd child
)

我们的函数现在有五个参数:

  • 一个元素的类型
  • 一个属性对象
  • 三个子元素。

因为其中一个child是一个React已知的HTML标签(<br/>),所以它也会被描述为一个函数调用(React.createElement('br'))。

到目前为止,我们已经涵盖了两种类型的children:

  • 简单的String
  • 另一种会调用React.createElement

然而,还有其他值可以作为参数:

  • 基本类型 false, null, undefined, true
  • 数组
  • React Components

可以使用数组是因为可以将children分组并作为一个参数传递:

React.createElement(
  'div',
  { className: 'cn' },
  ['Content 1!', React.createElement('br'), 'Content 2!']
)

当然了,React的厉害之处,不仅仅因为我们可以把HTML标签直接放在JSX中使用,而是我们可以自定义自己的组件,例如:

function Table({ rows }) {
  return (
    <table>
      {rows.map(row => (
        <tr key={row.id}>
          <td>{row.title}</td>
        </tr>
      ))}
    </table>
  );
}

组件可以让我们把模板分解为多个可重用的块。在上面的“函数式”(functional)组件的例子里,我们接收一个包含表格行数据的对象数组,最后返回一个调用React.createElement方法的<table>元素,rows则作为children传进table。

无论什么时候,我们这样去声明一个组件时:

<Table rows={rows} />

从浏览器的角度来看,我们是这么写的:

React.createElement(Table, { rows: rows });

注意,这次我们的第一个参数不是String描述的HTML标签,而是一个引用,指向我们编写组件时编写的函数。组件的attributes现在是接收的props参数了。

把组件(components)组合成页面(a page)

所以,我们已经将所有JSX组件转换为纯JavaScript,现在我们有一大堆函数调用,它的参数会被其他函数调用的,或者还有更多的其他函数调用这些参数......这些带参数的函数调用,是怎么转化成组成这个页面的实体DOM的呢?

为此,我们有一个ReactDOM库及其它的render方法:

function Table({ rows }) { /* ... */ } // defining a component

// rendering a component
ReactDOM.render(
  React.createElement(Table, { rows: rows }), // "creating" a component
  document.getElementById('#root') // inserting it on a page
);

ReactDOM.render被调用时,React.createElement最终也会被调用,返回以下对象:

// There are more fields, but these are most important to us
{
  type: Table,
  props: {
    rows: rows
  },
  // ...
}

这些对象,在React的角度上,构成了虚拟DOM。


他们将在所有进一步的渲染中相互比较,并最终转化为 真正的DOM(virtual VS real, 虚拟DOM VS 真实DOM)。

下面是另一个例子:这次div有一个class属性和几个children:

React.createElement(
  'div',
  { className: 'cn' },
  'Content 1!',
  'Content 2!',
);

变成:

{
  type: 'div',
  props: {
    className: 'cn',
    children: [
      'Content 1!',
      'Content 2!'
    ]
  }
}

需要注意的是,那些除了typeattribute以外的属性,原本是单独传进来的,转换之后,会作为在props.children以一个数组的形式打包存在。也就是说,无论children是作为数组还是参数列表传递都没关系 —— 在生成的虚拟DOM对象的时候,它们最后都会被打包在一起的。

进一步说,我们可以直接在组件中把children作为一项属性传进去,结果还是一样的:

<div className='cn' children={['Content 1!', 'Content 2!']} />

在构建虚拟DOM对象完成之后,ReactDOM.render将会按下面的原则,尝试将其转换为浏览器可以识别和展示的DOM节点:

  • 如果type包含一个带有String类型的标签名称(tag name)—— 创建一个标签,附带上props下所有attributes
  • 如果type是一个函数(function)或者类(class),调用它,并对结果递归地重复这个过程。
  • 如果props下有children属性 —— 在父节点下,针对每个child重复以上过程。

最后,得到以下HTML(对于我们的表格示例):

<table>
  <tr>
    <td>Title</td>
  </tr>
  ...
</table>

重新构建DOM(Rebuilding the DOM)

在实际应用场景,render通常在根节点调用一次,后续的更新会有state来控制和触发调用。

请注意,标题中的“重新”!当我们想更新一个页面而不是全部替换时,React中的魔法就开始了。我们有一些实现它的方式。我们先从最简单的开始 —— 在同一个node节点再次执行ReactDOM.render

// Second call
ReactDOM.render(
  React.createElement(Table, { rows: rows }),
  document.getElementById('#root')
);

这一次,上面的代码的表现,跟我们已经看到的有所不同。React将启动其diff算法,而不是从头开始创建所有DOM节点并将其放在页面上,来确定节点树的哪些部分必须更新,哪些可以保持不变。

那么,它是怎样工作的呢?其实只有少数几个简单的场景,理解它们将对我们的优化帮助很大。请记住,现在我们在看的,是在React Virtual DOM里面用来代表节点的对象

场景1:type是一个字符串,type在通话中保持不变,props也没有改变。

// before update
{ type: 'div', props: { className: 'cn' } }

// after update
{ type: 'div', props: { className: 'cn' } }

这是最简单的情况:DOM保持不变。

场景2:type仍然是相同的字符串,props是不同的。

// before update:
{ type: 'div', props: { className: 'cn' } }

// after update:
{ type: 'div', props: { className: 'cnn' } }

type仍然代表HTML元素,React知道如何通过标准DOM API调用来更改元素的属性,而无需从DOM树中删除一个节点。

场景3:type已更改为不同的String或从String组件。

// before update:
{ type: 'div', props: { className: 'cn' } }

// after update:
{ type: 'span', props: { className: 'cn' } }

React看到的type是不同的,它甚至不会尝试更新我们的节点:old元素将和它的所有子节点一起被删除(unmounted卸载)。因此,将元素替换为完全不同于DOM树的东西代价会非常昂贵。幸运的是,这在现实世界中很少发生。

划重点,记住React使用===(triple equals)来比较type的值,所以这两个值需要是相同类或相同函数的相同实例。

下一个场景更加有趣,通常我们会这么使用React。

场景4:type是一个component

// before update:
{ type: Table, props: { rows: rows } }

// after update:
{ type: Table, props: { rows: rows } }

你可能会说,“咦,但没有任何变化啊!”,但是你错了。


如果type是对函数或类的引用(即常规的React组件),并且我们启动了tree diff的过程,则React会持续地去检查组件的内部逻辑,以确保render返回的值不会改变(类似对副作用的预防措施)。对树中的每个组件进行遍历和扫描 —— 是的,在复杂的渲染场景下,成本可能会非常昂贵!

值得注意的是,一个componentrender(只有类组件在声明时有这个函数)跟ReactDom.render不是同一个函数。

关注子组件(children)的情况

除了上述四种常见场景之外,当一个元素有多个子元素时,我们还需要考虑React的行为。现在假设我们有这么一个元素:

// ...
props: {
  children: [
      { type: 'div' },
      { type: 'span' },
      { type: 'br' }
  ]
},
// ...

我们想要交换一下这些children的顺序:

// ...
props: {
  children: [
    { type: 'span' },
    { type: 'div' },
    { type: 'br' }
  ]
},
// ...

之后会发生什么呢?

diffing的时候,如果React在检查props.children下的数组时,按顺序去对比数组内元素的话:index 0将与index 0进行比较,index 1和index 1,等等。对于每一次对比,React会使用之前提过的diff规则。在我们的例子里,它认为div成为一个span,那么就会运用到情景3。这样不是很有效率的:想象一下,我们已经从1000行中删除了第一行。React将不得不“更新”剩余的999个子项,因为按index去对比的话,内容从第一条开始就不相同了。

幸运的是,React有一个内置的方法(built-in)来解决这个问题。如果一个元素有一个key属性,那么元素将按key而不是index来比较。只要key是唯一的,React就会移动元素,而不是将它们从DOM树中移除然后再将它们放回(这个过程在React里叫mounting和unmounting)。

// ...
props: {
  children: [ // Now React will look on key, not index
    { type: 'div', key: 'div' },
    { type: 'span', key: 'span' },
    { type: 'br', key: 'bt' }
  ]
},
// ...

当state发生了改变

到目前为止,我们只聊了下React哲学里面的props部分,却忽视了另外很重要的一部分state。下面是一个简单的stateful组件:

class App extends Component {
  state = { counter: 0 }

  increment = () => this.setState({
    counter: this.state.counter + 1,
  })

  render = () => (<button onClick={this.increment}>
    {'Counter: ' + this.state.counter}
  </button>)
}

state对象里,我们有一个keycounter。点击按钮时,这个值会增加,然后按钮的文本也会发生相应的改变。但是,当我们这样做时,DOM中发生了什么?哪部分将被重新计算和更新?

调用this.setState会导致re-render(重新渲染),但不会影响到整个页面,而只会影响组件本身及其children组件。父母和兄弟姐妹都不会受到影响。当我们有一个层级很深的组件链时,这会让状态更新变得非常方便,因为我们只需要重绘(redraw)它的一部分。

把问题说清楚

我们准备了一个小demo,以便你可以在看到在“野蛮生长”的React编码方式下最常见的问题,后续我也告诉大家怎么去解决这些问题。你可以在这里看看它的源代码。你还需要React Developer Tools,请确保浏览器安装了它们。

我们首先要看看的是,哪些元素以及什么时候导致Virtual DOM的更新。在浏览器的开发工具中,打开React面板并选择“Highlight Updates”复选框:

"在Chrome中使用“突出显示更新”复选框选中DevTools"

现在尝试在表格中添加一行。如你所见,页面上的每个元素周围都会显示一个边框。这意味着每次添加一行时,React都在计算和比较整个虚拟DOM树。现在尝试点击一行内的counter按钮。你将看到state更新后虚拟DOM如何更新 —— 只有引用了state key的元素及其children受到影响。

React DevTools会提示问题出在哪里,但不会告诉我们有关细节的信息:特别是所涉及的更新,是由diffing元素引起的?还是被挂载(mounting)或者被卸载(unmounting)了?要了解更多信息,我们需要使用React的内置分析器(注意它不适用于生产模式)。

添加?react_perf到应用的URL,然后转到Chrome DevTools中的“Performance”标签。点击“录制”(Record)并在表格上点击。添加一些row,更改一下counter,然后点击“停止”(Stop)。

"React DevTools的“Performance”选项卡"

在输出的结果中,我们关注“User timing”这项指标。放大时间轴直到看到“React Tree Reconciliation”这个组及其子项。这些就是我们组件的名称,它们旁边都写着[update]或[mount]。


我们的大部分性能问题都属于这两类问题之一。


无论是组件(还是从它分支的其他组件)出于某种原因都会在每次更新时re-mounted(慢),又或者我们在大型应用上执行对每个分支做diff,尽管这些组件并没有发生改变,我们不希望这些情况的发生。

优化我们的代码:Mounting / Unmounting

现在,我们已经了解到当需要update Virtual Dom时,React是依据哪些规则去判断要不要更新,以及也知道了我们可以通过什么方式去追踪这些diff场景的背后发生了什么,我们终于准备好优化我们的代码了!首先,我们来看看mounts/unmounts。

如果你能够注意到当一个元素包含的多个children,他们是由array组成的话,你可以实现十分显著的速度优化。

我们来看看这个case:

<div>
  <Message />
  <Table />
  <Footer />
</div>

在我们的Virtual DOM里这么表示:

// ...
props: {
  children: [
    { type: Message },
    { type: Table },
    { type: Footer }
  ]
}
// ...

这里有一个简单的Message例子,就是一个div写着一些简单的文本,和以及一个巨大的Table,比方说,超过1000行。它们(MessageTable)都是顶级div的子组件,所以它们被放置在父节点的props.children下,并且它们key都不会有。React甚至不会通过控制台警告我们要给每个child分配key,因为children正在React.createElement作为参数列表传递给父元素,而不是直接遍历一个数组。

现在我们的用户已读了一个通知,Message(譬如新通知按钮)从DOM上移除。TableFooter是剩下的全部。

// ...
props: {
  children: [
    { type: Table },
    { type: Footer }
  ]
}
// ...

React会怎么处理呢?它会看作是一个array类型的children,现在少了第一项,从前第一项是Message现在是Table了,也没有key作为索引,比较type的时候又发现它们俩不是同一个function或者class的同一个实例,于是会把整个Tableunmount,然后在mount回去,渲染它的1000+行子数据。

因此,你可以给每个component添加唯一的key(但在目特殊的case下,使用key并不是最佳选择),或者采用更聪明的小技巧:使用短路求值(又名“最小化求值”),这是JavaScript和许多其他现代语言的特性。看:

// Using a boolean trick
<div>
  {isShown && <Message />}
  <Table />
  <Footer />
</div>

虽然Message会离开屏幕,父元素divprops.children仍然会拥有三个元素,children[0]具有一个值false(一个布尔值)。请记住true, false, null, undefined是虚拟DOM对象type属性的允许值,我们最终得到了类似的结果:

// ...
props: {
  children: [
    false, //  isShown && <Message /> evaluates to false
    { type: Table },
    { type: Footer }
  ]
}
// ...

因此,有没有Message组件,我们的索引值都不会改变,Table当然仍然会跟Table比较(当type是一个函数或类的引用时,diff比较的成本还是会有的),但仅仅比较虚拟DOM的成本,通常比“删除DOM节点”并“从0开始创建”它们要来得快。

现在我们来看看更多的东西。大家都挺喜欢用HOC的,高阶组件是一个将组件作为参数,执行某些操作,最后返回另外一个不同功能的组件:

function withName(SomeComponent) {
  // Computing name, possibly expensive...
  return function(props) {
    return <SomeComponent {...props} name={name} />;
  }
}

这是一种常见的模式,但你需要小心。如果我们这么写:

class App extends React.Component() {
  render() {
    // Creates a new instance on each render
    const ComponentWithName = withName(SomeComponent);
    return <SomeComponentWithName />;
  }
}

我们在父节点的render方法内部创建一个HOC。当我们重新渲染(re-render)树时,虚拟DOM是这样子的:

// On first render:
{
  type: ComponentWithName,
  props: {},
}

// On second render:
{
  type: ComponentWithName, // Same name, but different instance
  props: {},
}

现在,React会对ComponentWithName这个实例做diff,但由于此时同名引用了不同的实例,因此全等比较(triple equal)失败,一个完整的re-mount会发生(整个节点换掉),而不是调整属性值或顺序。注意它也会导致状态丢失,如此处所述。幸运的是,这很容易解决,你需要始终在render外面创建一个HOC:

// Creates a new instance just once
const ComponentWithName = withName(Component);

class App extends React.Component() {
  render() {
    return <ComponentWithName />;
  }
}

优化我的代码:Updating

现在我们可以确保在非必要的时候,不做re-mount的事情了。然而,对位于DOM树根部附近(层级越上面的元素)的组件所做的任何更改都会导致其所有children的diffing和调整(reconciliation)。在层级很多、结构复杂的应用里,这些成本很昂贵,但经常是可以避免的。


如果有一种方法可以告诉React你不用来检查这个分支了,因为我们可以肯定那个分支不会有更新,那就太棒了!


这种方式是真的有的哈,它涉及一个built-in方法叫shouldComponentUpdate,它也是组件生命周期的一部分。这个方法的调用时机:组件的render和组件接收到state或props的值的更新时。然后我们可以自由地将它们与我们当前的值进行比较,并决定是否更新我们的组件(返回truefalse)。如果我们返回false,React将不会重新渲染组件,也不会检查它的所有子组件。

通常来说,比较两个集合(set)propsstate一个简单的浅层比较(shallow comparison)就足够了:如果顶层的值不同,我们不必接着比较了。浅比较不是JavaScript的一个特性,但有很多小而美的库utilities)可以让我们用上那么棒的功能。

现在可以像这样编写我们的代码:

class TableRow extends React.Component {

  // will return true if new props/state are different from old ones
  shouldComponentUpdate(nextProps, nextState) {
    const { props, state } = this;
    return !shallowequal(props, nextProps)
           && !shallowequal(state, nextState);
  }

  render() { /* ... */ }
}

但是你甚至都不需要自己写代码,因为React把这个特性内置在一个类React.PureComponent里面。它类似于 React.Component,只是shouldComponentUpdate已经为你实施了一个浅的props/state比较。

这听起来很“不动脑”,在声明class继承(extends)的时候,把Component换成PureComponent就可以享受高效率。事实上,并不是这么“傻瓜”,看看这些例子:

<Table
    // map returns a new instance of array so shallow comparison will fail
    rows={rows.map(/* ... */)}
    // object literal is always "different" from predecessor
    style={ { color: 'red' } }
    // arrow function is a new unnamed thing in the scope, so there will always be a full diffing
    onUpdate={() => { /* ... */ }}
/>

上面的代码片段演示了三种最常见的反模式。尽量避免它们!


如果你能注意点,在render定义之外创建所有对象、数组和函数,并确保它们在各种调用间,不发生更改 —— 你是安全的。


你在updated demo,所有table的rows都被“净化”(purified)过,你可以看到PureComponent的表现了。如果你在React DevTools中打开“Highlight Updates”,你会注意到只有表格本身和新行在插入时会触发render,其他的行保持不变。

[译者说:为了便于大家理解purified,译者在下面插入了原文demo的一段代码]

class TableRow extends React.PureComponent {
  render() {
    return React.createElement('tr', { className: 'row' },
      React.createElement('td', { className: 'cell' }, this.props.title),
      React.createElement('td', { className: 'cell' }, React.createElement(Button)),
    );
  }
};

不过,如果你迫不及待地all in PureComponent,在应用里到处都用的话 —— 控制住你自己!

shallow比较两组propsstate不是免费的,对于大多数基本组件来说,甚至都不值得:shallowComparediffing算法需要耗费更多的时间。

使用这个经验法则:pure component适用于复杂的表单和表格,但它们通常会减慢简单元素(按钮、图标)的效率。


感谢你的阅读!现在你已准备好将这些见解应用到你的应用程序中。可以使用我们的小demo(用了没有用PureComponent)的仓库作为你的实验的起点。此外,请继续关注本系列的下一部分,我们计划涵盖Redux并优化你的数据,目标是提高整个应用的总体性能。

译者说

正如原文末所说,Alex和Andy后续会继续写一个关于整体性能的系列,包括核心React和Redux等,我也会继续跟踪这个系列的文章,到时po到我的个人博客和知乎专栏《集异璧》,感兴趣的同学们可以关注一下哈 :)

欢迎对本文的翻译质量、内容的各种讨论。若有表述不当,欢迎斧正。

2018.05.13,晴,杭州滨江
Yuying Wu


笔者 @Yuying Wu,前端爱好者 / 鼓励师 / 新西兰打工度假 / 铲屎官。目前就职于某大型电商的B2B前端团队。

感谢你读到这里。如果你和我一样喜欢前端,喜欢捣腾独立博客或者前沿技术,或者有什么职业疑问,欢迎关注我以及各种交流哈。

独立博客:wuyuying.com
知乎ID:@Yuying Wu
Github:Yuying Wu

查看原文

山顶的男人 收藏了文章 · 2018-04-06

程序员为什么值得写博客

Hire Great Writers

仿佛这是写给自己看的,不过这在其中也有着相当有趣的意义 。虽然自己算是一个能写的人,或许这算是一种不算才华的才华,写博文的意义通常不会在于去描述自己怎样,怎样。通常在某些时候对自己来说,可以回顾自己学习的过程,呈现其中的一些思路,并其学习的过程分享出来。

原文的意义在于说明,一个优秀的写手,其优点并不仅仅在于写作,文法清晰代表思路明晰。优秀的写手都懂得如何与人沟通,他们使事情变得易于理解,他们善于换位思考,懂得抓重点、砍枝节,这些都是合格的应聘者身上应具备的特点。
想想自己算是后面说的那一部分,至于前面那部分——一个优秀的写手,就有待商榷。写的人越来越想,阅读的人越来越多的这个信息冗余的年代,会写就代表会思考??(转载保留: 程序员为什么值得写博客)

为什么要写博文

写一篇博文意味着要花一定的时间,有时候可能是一个小时,有时候可能会更多,于是人们开始去copy。在这个Ctrl+C越来越盛行的年代,我们还是输了,于是乎在我们的国度里,我们的计算机书算是输给国外的精英了。我们也有优秀的程序员,有优秀的工程师,只是在其中能与大众沟通的又有多少。我们的最终用户可从来不会管你用的是什么技术,他只懂得什么是体验,什么是速度等等。至于你说的东西,他不知道,也不会在乎。

这也就是为什么大师可以成为大师的原因,而菜鸟却还是菜鸟,大师在心里写博文的时候学会了总结,比如,定义算法的集合,将各算法封装,使它们能够交换。利用Strategy模式,算法和利用这些算法的客户程序可以分别独立进行修改而不互相影响。这个就是你需要的方法,于是大师就和你说了,“你需要Strategy模式”。这就是你要的答案,GoF分享了他的东西给了你,我们就有了一本《Head First设计模式》或者是《设计模式解析》。

我们开始走上了成为大师的西天取经,为什么是西天呢,这个领域一直是西方比东方分享得多。《西游记》就这样成为了《西游记》,写下这个过程的到处是吴承恩,还是师徒五人?师徒五人从一个地痞无赖直至成仙成佛。(PS:一直觉得自己写的东西,比较像不是散文的散文,中心似乎一直很明确,只是看懂的仿佛不到。)简单点来说,就是他们写下了自己的那些点点滴滴,我们就知道怎么去“西天”(我的意思不是那个意思,我想你懂的。)

这个过程就是一个个为什么你会看到那么多本优秀的计算机书的原因,大师分享了他们的心得告诉我们如何去成为大师,不过我还不是。只是如果你要成为大师,就要去分享你的过程。至于为什么?简单的说几点:

  • 技艺的掌握在于重复。技术和游玩的相同之处在于技术玩得越多,也就越熟悉,当你试着去写一篇博文的时候,你也回顾了过程。游玩的回味可以再次欢乐,博文的书写可以再次熟悉。
  • 你的过程正是别人所需要的。不要以为你手上的那点点关于编码的小知识不是别人所需要的,有时候人们就需要像《七周七语言 理解多种编程范型》这种书。
  • 你的作品有可能因此重构。至于你对于重构是害怕还是享受,我就不得而知的,但是你写出来的时候,也许你会有更好的思路涌现出来。不好的一点是你还需要对这篇文章进行重构,不是么?
  • 别人的评价。别人的评价有时候是打击,不过我想更多的时候是一种建议,比如Linus在刚写Linux的第一个版本的时候,他也遇到了这样的问题。至于宏内核好还是微内核好,这个问题有点类似于先有鸡还是先有蛋,不过我想后者可能科学家会给出答案。至于前者,不同的领域可能是不同的,Python好还是C好?相同的领域也可能是不同的,Ruby强大,还是Python强大?
  • 最后一点就是,你想成为大师,不是么?如果你还甘愿......,我就不说了。

成为笔杆子

Copy与盗版

当我开始越来越频繁写博客的时候,同学开始复制,于是有一天他的排名对于我来说,已经遥不可及了,于是远远地排在了CSDN的前面。一步步的前进着,开始懂得怎样去试着推销自己的博客,这时候渐渐有趣了。又去鼓励另外一个同学去写博客,就如他所说的,“就算是你,写一篇博客也要一个小时”吧,或者对于我的打字速度来说,不算什么,半个小时可以达到三千,五笔加上机械键盘好的手感。

我们总会说别人写的说怎么怎么的烂,但是如果一本书上不是Copy过来的,那么他就是不错的,在版权的地位比代课老师还低的天朝。我想你就可以骂这本书烂,因为他是复制的,因为到了最后你没有找到出处。换到博文来说,你搜索到的结果一个个都是一样的,你找不到原版的文章,去问作者一些问题。

一开始的时候我试着去反抗那些复制,你花一个小时写的东西,可能在发布的瞬间就被抓取过去了。有趣的是,渐渐我发现这有利于我们去传播我们的思想。换句话说,这是一个信息时代,你写的东西有可能在一瞬间到了Obama的眼前。至于优缺点嘛,补充一句可以借此SEO。

天朝一直都有天朝特色,无论从哪些方面来说,计算机也是如此,中国特色的免费。至于付费,我想这就是为什么我不会考虑去做收费软件的原因了。 程序员害了程序员,自己害了自己有什么好说的。于是转战到了openSUSE,都挺好的fcitx的五笔很给力,bug也没有原来多,还有WPS For Linux下的此文,因为网络原因。

Copy对于读者来说,看到的都是千篇一律的东西,只会写的人失去兴趣。盗版对于用户来说,看到的都是免费的东西,只会让开发商失去动力。用户便看到了越来越多的广告,读者便只看一个门户的新闻。

如何去写博文

标题——必须重要,类名

对于写博文的人来说,重点的是如何清楚的去表达他们的想法,标题算是其中之一,这个也就是为什么标题党成为了标题党,而《设计模式》成为了经典。刚开始学编程的时候,更吸引你注意力的可能是《72小时学会Javascript》,而不是《Javascript 权威指南》,兴许让你买前者的原因是因为你能看懂前者,而后者不仅看不懂,而且价格更贵。只是一年以后,《72 小时学会Javascript》被你扔到了垃圾箱,而《Javascript 权威指南》却放在了原来放那本书的位置上。你定义的类难道仅仅应该是class class1么?

小标题——地图,method

小标题有点类似于sitemap.xml,只是他就是站点地图,一点就到了相应的地方。他应该直接了解的说这是开始菜单,标题栏,菜单栏,而不应该是简简单单的第一章,如果你真是那样写的话,你写的函数想必是

def fun1
end

如果你写的是get_data那么,我想你的函数名应该和你的文章一样,告诉人们,你要的是get_data。所以不要吝啬你鼠标的一下,它可以承受上百万字的点击。如果因为那样坏了,你可以告诉我,我可以帮你免费换一个欧姆龙的微动,前提是你的鼠标可以换。如果是HTML那么应该是h2 h3,markdown也就是用得比较多的github上的README.md的##或者是###

内容——函数体

这里可不是让你用一个让人难以理解的Magic Number,你写得越复杂,别人看的时间就越久,通俗易懂,就是一个很好的开始。你可以把一个个复杂的方法分解出来,或者提炼函数,或者重命名。当你相信你看不懂你的文章,正如你看不懂你写的hello,world我想你是时候去重构你的函数了。

复杂的部分,就用段落来解决,一个函数如同一个段落只应该表达一个思想,太长了就如同这篇文章一样没有多少人会认真去看。你需要给你写的一个精美的代码加一个注释,所以你也需要给你复杂的地方加上个(PS)。

引言——README

我想都会去看的,无论是在破解软件的时候,还是github上面的项目。简单的说说,这篇文章是干什么的,这个程序是干什么的。大家都会,不是么?

没有什么好写的?

说说你是怎么开始编程吧,然后写在你的博客上,你会发现你会爱不释手的。

小提醒

  • 代码,代码有时候会更清晰的表达你思路,太长的代码可能会影响阅读,通常不超过一屏就不算太长
  • 图表,耐心的画个UML图,或者程序框图也是不错的,很清晰的表达你的思想。
  • 美观,要知道C上是有indent,如果我看到别人让我帮他看的代码是一坨。。。WPS也有段落,如果你没有学好WORD,找本《72小时精通Office》吧,顺便找PPT、EXLS也学了。
  • 格式,记得好好用好手上的工具,如果你用的是CSDN用的editor,试着一个个探索,CODE应该要有CODE的格式,LIST应该要有LIST的格式

最后,耐心

在CSDN上的博文的话,可以按长尾理论来分析,这里说的通常是指——你的东西是原创的,写博文有些时间。SEO上,以谷歌为例,谷歌对其抓取是比较及时的,同时谷歌会排除掉部分专业的复制网站——就是拉到重复的搜索结果里。文章刚发表的时候的流量有可能会很低 ,但是有些文章时间一长就显示出来了,比如我写的东西中的《Android上使用GCC》算是一个很好的示例。一开始的时候我们写的东西访问量不会很大,特别是我们刚起步的时候,这时候就要一步步慢慢来。只要你写的东西是别人需要的话,那么就会一步步慢慢来。如果你写的刚好是热门的话题、技术的话,那就是好莱坞大篇《速度与激情1》《指环王2》《黑客帝国3》《纳尼亚传奇4》。。。直到《哈利波特7上》,《哈利波特7下》。于是作为迪斯尼的你,又推出了下一部分电影……

总结

现在的我们更多的技术是直接来源于Google、百度、CSDN或者其他,搜索得来的,我们并不去考虑别人在其中花费的时间和经历,有时候我们要试着去想我们是不是也应该分享给别人。这算是自己开始写博客的原因,受益于开源社区,我们自然而然的也要回顾给这个社区,只有分享才会使未来更美好。

我们都希望看到有一篇博文够清楚的对我们当前所遇到的那个问题进行好好的解析,问题是也许你解决过的那个问题正是别人所需要的,但是你并没有将它分享出来,仿佛是一个循环一样

for(;;){

}

于是我们又回到了一个起点,人都是自私的。我们都希望自己能更快的学习好一项技术,一门语言,别人也需要你手上的那项技术,那个语言。都你开始意识到别人需要你手上的东西的时候,你算懂得换位思考了。

写篇分享,写篇心得就是一个好的开始,或许我们已经被高中的语文所吓怕了。但是,是时候从新开始。如果你被C的指针吓坏了,被C++的模板吓到了,被Javascript的简陋吓到了,而你又需要拾起它,我想是时候重新开始了。

查看原文

认证与成就

  • 获得 35 次点赞
  • 获得 12 枚徽章 获得 0 枚金徽章, 获得 2 枚银徽章, 获得 10 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-11-16
个人主页被 372 人浏览