Hexo博客优化,如何加快你的博客访问速度(精)

摘要

很多人辛辛苦苦的搭建了Hexo博客,并且漫无目的加载很多插件功能,在访问页面时候总是感觉慢,特别是在于一些将博客统一部署在Coding或者GitHub的用户。相信很多用户解决慢的问题第一反应是“CDN优化静态文件”“压缩静态文件”、等,没错这些方法如果合理使用确实起到优化速度的作用。但是不能盲目使用,以下是我的优化经验,考虑的比较细节。大家可以借鉴参考。

HEXO-建站系列
HEXO-优化系列
HEXO-进阶系列


正文

前言

在进行优化前,我们先来看看有哪些因素可以引起网站访问慢,在这里假如你已经实现了Coding和Github统一部署,并且你的过内网站与国外网站的域名不一样,国内www.yourdmain.com,国外blog.yourdomain.com,关于为什么或者怎么设置针对国内网的域名,请继续往下阅读(DNSPOD实现)。

因素:
1.静态文件加载导致网页速度慢(静态文件主要包括js、css等文件,存在某些较大的静态文件记载慢-慢)
2.网页引用不合理 (最明显的就是国内网站引用国外资源-慢)
3.域名解析不合理 (国外注册的域名在国内解析-慢)

上面就是我个人觉得最影响访问速度的因素,针对以上的各个因素,我讲讲我的一些优化例子。大家分享和互相学习一下

静态文件加载

静态文件就是一些Js,css等文件,通常网站都会调用一大堆这些文件,我们可以合理的将静态资源放到CDN服务器(七牛CDN等),如果你的一些静态资源文件是一些通用的文件,如jquery、fancybox等js或者css,你完全可以使用一些外部免费的开源项目CDN网站,如BootCDN ,但是个人觉得在CDN你的静态文件,你应该合理考虑哪些文件才应该进行CDN加速,因为你要CDN加速静态文件的原因是,CDN是以合理的服务器节点返回相关静态资源给你。或许有些情况,你的网站本身的服务器的速度或许比你的CDN更要快速(特别是一些免费的CDN服务)。

外部CDN

在版本 5.0.1 之后的Next,具有一个方便的功能可以实现修改主题配置文件,实现常用插件进行外部CDN链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Script Vendors.
# Set a CDN address for the vendor you want to customize.
# For example
# jquery: https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js
# Be aware that you should use the same version as internal ones to avoid potential problems.
vendors:
# Internal path prefix. Please do not edit it.
_internal: vendors

# Internal version: 2.1.3
jquery: #//你的CDN资源 如//cdn.jsdelivr.net/jquery/2.1.3/jquery.min.js

# Internal version: 2.1.5
# http://fancyapps.com/fancybox/
fancybox:
fancybox_css:

# Internal version: 1.0.6
# https://github.com/ftlabs/fastclick
fastclick:

# Internal version: 1.9.7
# https://github.com/tuupola/jquery_lazyload
lazyload:

# Internal version: 1.2.1
# http://VelocityJS.org
velocity:

# Internal version: 1.2.1
# http://VelocityJS.org
velocity_ui:

# Internal version: 0.7.9
# https://faisalman.github.io/ua-parser-js/
ua_parser:

# Internal version: 4.4.0
# http://fontawesome.io/
fontawesome:
#custom js
customjs:

从以上看出,很多Next插件已经可以自定义CDN链接,但是如果存在一些没有在上面列出的呢,也有办法,如果想要了解操作方式,请访问NEXT 加载您自定以的静态文件,如果你还需要进行图片的CDN,点击CDN图片加速,实现图片慢加载,以上都可以实现CDN。

网页引用

这里所指的页面引用,通常是指一些外部js的引用,由于其反应速度不太好,就导致了网页的加载也变慢了,所以你需要合理安排引用文件的所存放的位置。特别是国内与国外网站间的文件引用。

hexo博客哪些引用会影响速度?
其实绝大多数的引用都来自于本身的插件,我这里以影响比较大的来介绍一下:

  1. Google类引用
  2. 搜索插件引用
  3. 其他插件引用

Google类引用

Google类引用:

  1. fonts.googleapis.com (css字体引用)-国内网站无法访问Google,导致使用Google的引用链接失败,网站变慢。
  2. google-analytics (js Google分析)-道理一样

Google引用

无论国内还是国内,使用Google字体还是必须的,所以如果要解决国内引用国外的Google文件,由于GFW,直接使用Google的引用是不行了,解决方法就是引用一些免费的开源项目CDN网站这里介绍360CDN
实现方法:
首先你需要了解你的网站所使用的字体情况,通常你可以查看你的网站源代码,找到fonts.googleapis.comcss字体样式引用代码的?family=L......的代码
如下:

1
2
                          #代码位置
<link href='http://fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&amp;subset=latin,latin-ext' rel='stylesheet'>

替换为360CDN的字体库,css?....的部分需要与Google一致

1
<link href='http://fonts.useso.com/css?family=Lato:300,300italic,400,400italic,700,700italic&amp;subset=latin,latin-ext' rel='stylesheet'>

通过以上解决国内无法连接Google字体导致网站链接慢的问题,同时360CDN的字体国内外都可以访问,速度还算稳定。

搜索插件引用

相信大家使用的搜搜插件都是swiftype,但是有一个问题:国内访问swiftype是非常慢的,导致搜索插件调用慢,搜索结果显示慢,同时网站就慢,所以我的做法是:国外使用swiftype,国内使用其他搜索(如Hexo自带的)

实现方法:
我的目的是国外网站使用的是swiftype,国内使用(hexo自带的搜索)的是localsearch并存,国内访问网站不会显示swiftype,国外访问也不会显示localSearch
修改的文件主要有以下:

  1. themes\next\layout\_partials\search\swiftype.swig - swiftype的功能调用JS
  2. themes\next\layout\_partials\header.swig -“搜索”按钮的样式设置,主要是影响弹出框
  3. 主站点文件_config.yml -设置启用localSearch
  4. themes\next\layout\_partials\search.swig -判断是否使引入swiftype或者localSearch功能
  5. themes\next\layout\_scripts\third-party\localsearch.swig -localsearch的功能调用JS
  6. node_modules\hexo-generator-search\search.ejs -此为Hexo插件模块代码,你需要先安装hexo-generator-search

安装LocalSearch

在使用localSearch时,你需要使用以下命令安装localSearch

1
npm install hexo-generator-search --save

启用LocalSearch

修改主站点配置文件_config.yml添加以下代码:

1
2
3
search:
path: search.xml
field: post

到此,你的localSearch已经启用成功,如果您的swiftype没有启用,则localSearch生效,如果localSearch启用和swiftype也启用,只有swiftype生效,但是不是我的需要,我的要求是swiftype国外网站显示,localSearch国内能访问,如果要实现,需要修改上面提到的文件。

修改swiftype.swig

修改swiftype.swig,目的:插入代码实现国外才引用swiftype的官方js代码
原始代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form class="site-search-form">
<input type="text" id="st-search-input" class="st-search-input st-default-search-input" />
</form>

<script type="text/javascript">

(function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
(w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
})(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

_st('install', '{{ theme.swiftype_key }}','2.0.0');

</script>

修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<form class="site-search-form">
<input type="text" id="st-search-input" class="st-search-input st-default-search-input" />
</form>

<script type="text/javascript">

hostans = window.location.href;
chinadomainas = "你的国外网站域名应如:www.yourblog.org";
if (hostans.indexOf(chinadomainas)!=-1) {
(function(w,d,t,u,n,s,e){w['SwiftypeObject']=n;w[n]=w[n]||function(){
(w[n].q=w[n].q||[]).push(arguments);};s=d.createElement(t);
e=d.getElementsByTagName(t)[0];s.async=1;s.src=u;e.parentNode.insertBefore(s,e);
})(window,document,'script','//s.swiftypecdn.com/install/v2/st.js','_st');

_st('install', '{{ theme.swiftype_key }}','2.0.0');
}
</script>

修改header.swig

就如之前提到,如果localSearch和swiftype同时启用,优先以swiftype为搜索框,导致国内外都是swiftype的搜索款,为了保证LocalSearch和swiftype同时启用,修改如下代码,此代码的目的是解决首页”搜索“按钮的弹出式款被swiftype弹出式框给覆盖,我们希望localSearch和swiftype都有自己的弹出式框。互相不会影响到
原始代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    {% if hasSearch %}
<li class="menu-item menu-item-search">
{% if theme.swiftype_key %}
<a href="#" class="st-search-show-outputs"> #你需要修改的位置
{% elseif config.search %}
<a href="#" class="popup-trigger">
{% endif %}
{% if theme.menu_icons.enable %}
<i class="menu-item-icon fa fa-search fa-fw"></i> <br />
{% endif %}
{{ __('menu.search') }}
</a>
</li>
{% endif %}
</ul>
{% endif %}

修改后代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    {% if hasSearch %}
<li class="menu-item menu-item-search">
{% if theme.swiftype_key %}
<a href="#" class="popup-trigger"> #修改的位置
{% elseif config.search %}
<a href="#" class="popup-trigger">
{% endif %}
{% if theme.menu_icons.enable %}
<i class="menu-item-icon fa fa-search fa-fw"></i> <br />
{% endif %}
{{ __('menu.search') }}
</a>
</li>
{% endif %}
</ul>
{% endif %}

修改search.swig

此部分修改也是为了解决localSearch和swiftype的加载的优先覆盖问题
原始代码:

1
2
3
4
5
6
7
{% if theme.swiftype_key %}
{% include 'search/swiftype.swig' %}
{% elseif theme.tinysou_Key %}
{% include 'search/tinysou.swig' %}
{% elseif config.search.path %}
{% include 'search/localsearch.swig' %}
{% endif %}

修改后的代码:

1
2
3
4
5
6
7
8
{% if theme.swiftype_key %}
{% include 'search/swiftype.swig' %}
{% elseif theme.tinysou_Key %}
{% include 'search/tinysou.swig' %}
{% endif %}
{% if config.search.path %}
{% include 'search/localsearch.swig' %}
{% endif %}

修改localsearch.swig

此文件为LocalSearch功能实现代码,修改方法和swiftype雷同,但是这里的判断应该是你的国内网站
原始代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
{% if config.search.path %}
<script type="text/javascript">
// Popup Window;
var isfetched = false;
// Search DB path;
var search_path = "{{ config.search.path }}";
if (search_path.length == 0) {
search_path = "search.xml";
}
var path = "{{ config.root }}" + search_path;
// monitor main search box;

function proceedsearch() {
$("body").append('<div class="popoverlay">').css('overflow', 'hidden');
$('.popup').toggle();

}
// search function;
var searchFunc = function(path, search_id, content_id) {
'use strict';
$.ajax({
url: path,
dataType: "xml",
async: true,
success: function( xmlResponse ) {
// get the contents from search data
isfetched = true;
$('.popup').detach().appendTo('.header-inner');
var datas = $( "entry", xmlResponse ).map(function() {
return {
title: $( "title", this ).text(),
content: $("content",this).text(),
url: $( "url" , this).text()
};
}).get();
var $input = document.getElementById(search_id);
var $resultContent = document.getElementById(content_id);
$input.addEventListener('input', function(){
var matchcounts = 0;
var str='<ul class=\"search-result-list\">';
var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
$resultContent.innerHTML = "";
if (this.value.trim().length > 1) {
// perform local searching
datas.forEach(function(data) {
var isMatch = true;
var content_index = [];
var data_title = data.title.trim().toLowerCase();
var data_content = data.content.trim().replace(/<[^>]+>/g,"").toLowerCase();
var data_url = data.url;
var index_title = -1;
var index_content = -1;
var first_occur = -1;
// only match artiles with not empty titles and contents
if(data_title != '' && data_content != '') {
keywords.forEach(function(keyword, i) {
index_title = data_title.indexOf(keyword);
index_content = data_content.indexOf(keyword);
if( index_title < 0 && index_content < 0 ){
isMatch = false;
} else {
if (index_content < 0) {
index_content = 0;
}
if (i == 0) {
first_occur = index_content;
}
}
});
}
// show search results
if (isMatch) {
matchcounts += 1;
str += "<li><a href='"+ data_url +"' class='search-result-title'>"+ data_title +"</a>";
var content = data.content.trim().replace(/<[^>]+>/g,"");
if (first_occur >= 0) {
// cut out 100 characters
var start = first_occur - 20;
var end = first_occur + 80;
if(start < 0){
start = 0;
}
if(start == 0){
end = 50;
}
if(end > content.length){
end = content.length;
}
var match_content = content.substring(start, end);
// highlight all keywords
keywords.forEach(function(keyword){
var regS = new RegExp(keyword, "gi");
match_content = match_content.replace(regS, "<b class=\"search-keyword\">"+keyword+"</b>");
});

str += "<p class=\"search-result\">" + match_content +"...</p>"
}
str += "</li>";
}
})};
str += "</ul>";
if (matchcounts == 0) { str = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>' }
if (keywords == "") { str = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>' }
$resultContent.innerHTML = str;
});
proceedsearch();
}
});}

// handle and trigger popup window;
$('.popup-trigger').mousedown(function(e) {
e.stopPropagation();
if (isfetched == false) {
searchFunc(path, 'local-search-input', 'local-search-result');
} else {
proceedsearch();
};

});

$('.popup-btn-close').click(function(e){
$('.popup').hide();
$(".popoverlay").remove();
$('body').css('overflow', '');
});
$('.popup').click(function(e){
e.stopPropagation();
});
</script>

{% endif %}

修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
{% if config.search.path %}
<script type="text/javascript">
hostse = window.location.href; #代码修改位置
chinadomainse = "你的国内网站域名:如:yourname.coding.me";
if (hostse.indexOf(chinadomainse)!=-1) {
// Popup Window;
var isfetched = false;
// Search DB path;
var search_path = "{{ config.search.path }}";
if (search_path.length == 0) {
search_path = "search.xml";
}
var path = "{{ config.root }}" + search_path;
// monitor main search box;

function proceedsearch() {
$("body").append('<div class="popoverlay">').css('overflow', 'hidden');
$('.popup').toggle();

}
// search function;
var searchFunc = function(path, search_id, content_id) {
'use strict';
$.ajax({
url: path,
dataType: "xml",
async: true,
success: function( xmlResponse ) {
// get the contents from search data
isfetched = true;
$('.popup').detach().appendTo('.header-inner');
var datas = $( "entry", xmlResponse ).map(function() {
return {
title: $( "title", this ).text(),
content: $("content",this).text(),
url: $( "url" , this).text()
};
}).get();
var $input = document.getElementById(search_id);
var $resultContent = document.getElementById(content_id);
$input.addEventListener('input', function(){
var matchcounts = 0;
var str='<ul class=\"search-result-list\">';
var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
$resultContent.innerHTML = "";
if (this.value.trim().length > 1) {
// perform local searching
datas.forEach(function(data) {
var isMatch = true;
var content_index = [];
var data_title = data.title.trim().toLowerCase();
var data_content = data.content.trim().replace(/<[^>]+>/g,"").toLowerCase();
var data_url = data.url;
var index_title = -1;
var index_content = -1;
var first_occur = -1;
// only match artiles with not empty titles and contents
if(data_title != '' && data_content != '') {
keywords.forEach(function(keyword, i) {
index_title = data_title.indexOf(keyword);
index_content = data_content.indexOf(keyword);
if( index_title < 0 && index_content < 0 ){
isMatch = false;
} else {
if (index_content < 0) {
index_content = 0;
}
if (i == 0) {
first_occur = index_content;
}
}
});
}
// show search results
if (isMatch) {
matchcounts += 1;
str += "<li><a href='"+ data_url +"' class='search-result-title'>"+ data_title +"</a>";
var content = data.content.trim().replace(/<[^>]+>/g,"");
if (first_occur >= 0) {
// cut out 100 characters
var start = first_occur - 20;
var end = first_occur + 80;
if(start < 0){
start = 0;
}
if(start == 0){
end = 50;
}
if(end > content.length){
end = content.length;
}
var match_content = content.substring(start, end);
// highlight all keywords
keywords.forEach(function(keyword){
var regS = new RegExp(keyword, "gi");
match_content = match_content.replace(regS, "<b class=\"search-keyword\">"+keyword+"</b>");
});

str += "<p class=\"search-result\">" + match_content +"...</p>"
}
str += "</li>";
}
})};
str += "</ul>";
if (matchcounts == 0) { str = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>' }
if (keywords == "") { str = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>' }
$resultContent.innerHTML = str;
});
proceedsearch();
}
});}

// handle and trigger popup window;
$('.popup-trigger').mousedown(function(e) {
e.stopPropagation();
if (isfetched == false) {
searchFunc(path, 'local-search-input', 'local-search-result');
} else {
proceedsearch();
};

});

$('.popup-btn-close').click(function(e){
$('.popup').hide();
$(".popoverlay").remove();
$('body').css('overflow', '');
});
$('.popup').click(function(e){
e.stopPropagation();
});

} #代码修改位置 记得加}
</script>
{% endif %}

修改search.ejs

localSearch基于hexo-generator-search插件 就其原理其实就是通过Hexo的插件,编译过程自动搜索网站的所有文章链接并生成用于搜索的search.xml,但是在双域名的下,你的网站代码正常只有一个url(主站点配置文件的_config.yuml的url属性),但是如果你存在国外,或者国内网站的,你只能填写其中一个。这个是默认的模板规定,但是我这里做来修改,可以在_config.yml定义您对应的url,先讲回目的,我们的要求是希望localSearch只提供国内搜索,所以我希望search.xml的文件只有针对国内域名的文章搜搜链接,通过我的细查,发现search.ejs就是用来编译search.xml的文件,所以修改它来实现我们的目的。

首先为了避免与_config.yml的url冲突,定义一个guonei_url,修改_config.yml加入如下代码:

1
2
url: 你的国外网站域名
guonei_url: 你的国内网站域名

earch.ejs代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<search>
<% var url = config.url + config.root %> #修改位置:把config.url修改为config.guonei_url
<% if(posts){ %>
<% posts.each(function(post){ %>
<entry>
<title><%-: post.title | cdata %></title>
<url><%- encodeURI(url + post.path) %></url> #修改位置:修改为<%- encodeURI(url + post.path) %>
<content type="html"><%-: post.content | cdata %></content>
</entry>
<% }) %>
<% } %>
<% if(pages){ %>
<% pages.each(function(page){ %>
<entry>
<title><%-: page.title | cdata %></title>
<url><%- encodeURI(page.permalink) %></url>
<content type="html"><%-: page.content | cdata %></content>
</entry>
<% }) %>
<% } %>
</search>

小结

以上的代码修改,如果会看代码的人应该很容易看懂,其实就是为了避免localSearch和swiftype 加载的冲突,因为Hexo模板默认是只显示某个搜索引擎。通过以上的修改,你会发现国外网站,点击”搜索”显示的是localSearch,在国外访问的网站是swiftype。这样避免的加载搜索而导致慢的问题

其他插件引用

其他一些引用外部链接的插件都会影响到网站的速度,如多说discus插件,如果你能了解上面操作的原理,你也可以应用于多说discus,将你的网站优化的更好

域名解析

引言:
如果你没有注册个人的域名,优化的操作截至到上面部分,如果你有个人域名,你可以继续上面操作往下操作域名部分,域名的优化其实就是利用个人域名DNSPOD来实现国外和国内网站的分流,就是通过DNSPOD实现国内访问国内的域名,国外访问国外的域名,至于如何实现,请转致Dnspod+Namesilo域名结合实现域名选路解析(精),里面详解的介绍实现过程

结语

如果你还需要了解更多技术文章信息,请继续关注Jory博客

看一看,共同关注,共同分享与讨论!