NexT主题集成Algolia搜索插件

版权声明:本文为博主原创文章,转载请注明出处,谢谢!

很长时间不整理是很容易忘记的

为什么要增加Algolia搜索插件

第一个原因当然是这样可以让博客显得更加炫酷;其次就是原来常用的swiftype插件不再免费,而且Algolia不仅免费,感觉上要比Swiftype要快。

版权声明:本文为博主原创文章,转载请注明出处:http://blog.jerkybible.com/2016/10/12/NexT主题集成Algolia搜索插件/

访问原文「NexT主题集成Algolia搜索插件

就没有别人有写相关博客吗

你可能会有这样的疑问。我的回答是当然有,例如Hexo集成Algolia搜索插件,但是可能是NexT主题版本造成的不同,不能完全照搬。

当然,我也会写一些重复的东西,这样也不用再到别人的的博客上去翻了,万一他们博客登不上去了呢。

准备工作

  1. 确定你的NexT版本号,查看的方式是在NexT主题文件夹下的_config.yml文件中的最末端,我的是5.0.1。见下图。
  2. Algolia官网,注册一个账号,当然你可以用Github账号,重新注册的话163这种邮箱是不能用的。
  3. 新建一个新的Index,当然用已有的也可以,再在新的Index上进行基础设置。

  4. 打开API Keys页面,里面的信息一会儿要用到。

上传数据到Algolia

  1. 在Hexo工程根目录下执行下面的语句。

    1
    npm install hexo-algolia --save
  2. 在Hexo工程根目录的_config.yml中加入如下配置,注意改成前面API Keys页面相应配置。

    1
    2
    3
    4
    5
    6
    algolia:
    applicationID: 'your applicationID'
    apiKey: 'your apiKey'
    adminApiKey: 'your adminApiKey'
    indexName: 'your indexName'
    chunkSize: 5000
  3. 执行下面语句,必要时先进行hexo clean,确保最后得到提交成功提示。

    1
    hexo algolia

修改NexT主题集成Algolia

  1. 在NexT主题文件夹下(这一部分不特殊指明都在这个文件夹中操作)找到_config.yml,增加aloglia配置项,如下。

    1
    algolia: true
  2. 打开layout/_partials/head.swig,找到下面的语句。

    1
    2
    3
    4
    5
    6
    7
    <script type="text/javascript" id="hexo.configuration">
    ...
    var CONFIG = {
    ...
    }
    };
    </script>
  3. CONFIG中增加下面的语句,相应的配置改为与API Keys页面相一致。

    1
    2
    3
    4
    5
    6
    7
    8
    root: '/',
    algolia: {
    applicationID: 'your applicationID',
    apiKey: 'your apiKey',
    indexName: ''your indexName',
    hits: {"per_page":10},
    labels: {"input_placeholder":"搜索...","hits_empty":"未发现与 「${query}」相关的内容","hits_stats":"${hits} 条相关条目,使用了 ${time} 毫秒"}
    }
  4. 打开layout/_partials/header.swig,找到相应代码并做以下修改。

    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
    ...
    <!-- {% set hasSearch = theme.swiftype_key || theme.tinysou_Key || config.search %} -->
    <!-- 改为 -->
    {% set hasSearch = theme.swiftype_key || theme.tinysou_Key || config.search || theme.algolia %}
    ...
    {% if theme.menu %}
    <ul id="menu" class="menu">
    ...
    {% if hasSearch %}
    <li class="menu-item menu-item-search">
    {% if theme.swiftype_key %}
    <a href="javascript:;" class="st-search-show-outputs">
    {% elseif config.search %}
    <a href="javascript:;" class="popup-trigger">
    <!-- 增加下面语句 -->
    {% elseif theme.algolia %}
    <a href="javascript:;" 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 %}
    ...
  5. layout/_partials/search文件夹下新增algolia.swig文件,内容如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <div class="algolia-popup popup">
    <div class="algolia-search">
    <div class="algolia-search-input-icon">
    <i class="fa fa-search"></i>
    </div>
    <div class="algolia-search-input" id="algolia-search-input"></div>
    </div>
    <div class="algolia-results">
    <div id="algolia-stats"></div>
    <div id="algolia-hits"></div>
    <div id="algolia-pagination" class="algolia-pagination"></div>
    </div>
    <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
    </span>
    </div>
  6. 打开layout/_partials/search.swig,做以下修改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {% if theme.swiftype_key %}
    {% include 'search/swiftype.swig' %}
    {% elseif theme.tinysou_Key %}
    {% include 'search/tinysou.swig' %}
    {% elseif config.search.path %}
    {% include 'search/localsearch.swig' %}
    <!-- 增加下面语句 -->
    {% elseif theme.algolia %}
    {% include 'search/algolia.swig' %}
    <!-- 增加结束 -->
    {% endif %}
  7. source/js/src下面新增文件algolia.js,内容如下。

    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
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    $(document).ready(function () {
    var algoliaSettings = CONFIG.algolia;
    var isAlgoliaSettingsValid = algoliaSettings.applicationID &&
    algoliaSettings.apiKey &&
    algoliaSettings.indexName;
    if (!isAlgoliaSettingsValid) {
    window.console.error('Algolia Settings are invalid.');
    return;
    }
    var search = instantsearch({
    appId: algoliaSettings.applicationID,
    apiKey: algoliaSettings.apiKey,
    indexName: algoliaSettings.indexName,
    searchFunction: function (helper) {
    var searchInput = $('#algolia-search-input').find('input');
    if (searchInput.val()) {
    helper.search();
    }
    }
    });
    // Registering Widgets
    [
    instantsearch.widgets.searchBox({
    container: '#algolia-search-input',
    placeholder: algoliaSettings.labels.input_placeholder
    }),
    instantsearch.widgets.hits({
    container: '#algolia-hits',
    hitsPerPage: algoliaSettings.hits.per_page || 10,
    templates: {
    item: function (data) {
    return (
    '<a href="' + CONFIG.root + data.path + '" class="algolia-hit-item-link">' +
    data._highlightResult.title.value +
    '</a>'
    );
    },
    empty: function (data) {
    return (
    '<div id="algolia-hits-empty">' +
    algoliaSettings.labels.hits_empty.replace(/\$\{query}/, data.query) +
    '</div>'
    );
    }
    },
    cssClasses: {
    item: 'algolia-hit-item'
    }
    }),
    instantsearch.widgets.stats({
    container: '#algolia-stats',
    templates: {
    body: function (data) {
    var stats = algoliaSettings.labels.hits_stats
    .replace(/\$\{hits}/, data.nbHits)
    .replace(/\$\{time}/, data.processingTimeMS);
    return (
    stats +
    '<span class="algolia-powered">' +
    ' <img src="' + CONFIG.root + 'images/algolia_logo.svg" alt="Algolia" />' +
    '</span>' +
    '<hr />'
    );
    }
    }
    }),
    instantsearch.widgets.pagination({
    container: '#algolia-pagination',
    scrollTo: false,
    showFirstLast: false,
    labels: {
    first: '<i class="fa fa-angle-double-left"></i>',
    last: '<i class="fa fa-angle-double-right"></i>',
    previous: '<i class="fa fa-angle-left"></i>',
    next: '<i class="fa fa-angle-right"></i>'
    },
    cssClasses: {
    root: 'pagination',
    item: 'pagination-item',
    link: 'page-number',
    active: 'current',
    disabled: 'disabled-item'
    }
    })
    ].forEach(search.addWidget, search);
    search.start();
    $('.popup-trigger').on('click', function(e) {
    e.stopPropagation();
    $('body').append('<div class="popoverlay">').css('overflow', 'hidden');
    $('.popup').toggle();
    $('#algolia-search-input').find('input').focus();
    });
    $('.popup-btn-close').click(function(){
    $('.popup').hide();
    $('.popoverlay').remove();
    $('body').css('overflow', '');
    });
    });
    $(document).ready(function () {
    if ( $('#local-search-input').size() === 0) {
    return;
    }
    // Popup Window;
    var isfetched = false;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length == 0) {
    search_path = "search.xml";
    }
    var path = "/" + 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();
    });
    });
  8. layout/_scripts/third_party文件夹下增加文件algolia.swig,内容如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {%
    set js_algolia = [
    'src/algolia.js'
    ]
    %}
    {% for common in js_algolia %}
    {# S: Include Algolia instantsearch.js library #}
    {% set algolia_instant_css = url_for(theme.vendors._internal + '/algolia-instant-search/instantsearch.min.css') %}
    {% if theme.vendors.algolia_instant_css %}
    {% set algolia_instant_css = theme.vendors.algolia_instant_css %}
    {% endif %}
    <link rel="stylesheet" href="{{ algolia_instant_css }}">
    {% set algolia_instant_js = url_for(theme.vendors._internal + '/algolia-instant-search/instantsearch.min.js') %}
    {% if theme.vendors.algolia_instant_js %}
    {% set algolia_instant_js = theme.vendors.algolia_instant_js %}
    {% endif %}
    <script src="{{ algolia_instant_js }}"></script>
    {# E: Include Algolia instantsearch.js library #}
    <script type="text/javascript" src="{{ url_for(theme.js) }}/{{ common }}?v={{ theme.version }}"></script>
    {% endfor %}
  9. layout/_scripts/_layout.swig文件的body标签内添加下面的语句。

    1
    2
    3
    {% if theme.algolia %}
    {% include '_scripts/third-party/algolia.swig' %}
    {% endif %}
  10. source/css下新建文件夹_algolia,新建文件algolia.styl,内容如下。

    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
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    ul.search-result-list {
    padding-left: 0px;
    margin: 0px 5px 0px 8px;
    }
    p.search-result {
    border-bottom: 1px dashed #ccc;
    padding: 5px 0;
    }
    a.search-result-title {
    font-weight: bold;
    }
    a.search-result {
    border-bottom: transparent;
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    }
    .search-keyword {
    border-bottom: 1px dashed #4088b8;
    font-weight: bold;
    }
    #local-search-result {
    height: 90%;
    overflow: auto;
    }
    .popup {
    display: none;
    position: fixed;
    top: 10%;
    left: 50%;
    width: 700px;
    height: 80%;
    margin-left: -350px;
    padding: 3px 0 0 10px;
    background: #fff;
    color: #333;
    z-index: 9999;
    border-radius: 5px;
    }
    @media (max-width: 767px) {
    .popup {
    padding: 3px;
    top: 0;
    left: 0;
    margin: 0;
    width: 100%;
    height: 100%;
    border-radius: 0px;
    }
    }
    .popoverlay {
    position: fixed;
    width: 100%;
    height: 100%;
    top: 0px;
    left: 0px;
    z-index: 2080;
    background-color: rgba(0,0,0,0.3);
    }
    #local-search-input {
    margin-bottom: 10px;
    width: 50%;
    }
    .popup-btn-close {
    position: absolute;
    top: 6px;
    right: 14px;
    color: #4ebd79;
    font-size: 14px;
    font-weight: bold;
    text-transform: uppercase;
    cursor: pointer;
    }
    #no-result {
    position: absolute;
    left: 44%;
    top: 42%;
    color: #ccc;
    }
    .busuanzi-count:before {
    content: " ";
    float: left;
    width: 260px;
    min-height: 25px;
    }
    @media (min-width: 768px) and (max-width: 991px) {
    .busuanzi-count {
    width: auto;
    }
    .busuanzi-count:before {
    display: none;
    }
    }
    @media (max-width: 767px) {
    .busuanzi-count {
    width: auto;
    }
    .busuanzi-count:before {
    display: none;
    }
    }
    .site-uv,
    .site-pv,
    .page-pv {
    display: inline-block;
    }
    .site-uv .busuanzi-value,
    .site-pv .busuanzi-value,
    .page-pv .busuanzi-value {
    margin: 0 5px;
    }
    .site-uv {
    margin-right: 10px;
    }
    .site-uv::after {
    content: "|";
    padding-left: 10px;
    }
    .algolia-popup {
    overflow: hidden;
    padding: 0;
    }
    .algolia-popup .popup-btn-close {
    padding-left: 15px;
    border-left: 1px solid #eee;
    top: 10px;
    }
    .algolia-popup .popup-btn-close .fa {
    color: #999;
    font-size: 18px;
    }
    .algolia-popup .popup-btn-close:hover .fa {
    color: #222;
    }
    .algolia-search {
    padding: 10px 15px 5px;
    max-height: 50px;
    border-bottom: 1px solid #ccc;
    background: #f5f5f5;
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
    }
    .algolia-search-input-icon {
    display: inline-block;
    width: 20px;
    }
    .algolia-search-input-icon .fa {
    font-size: 18px;
    }
    .algolia-search-input {
    display: inline-block;
    width: calc(90% - 20px);
    }
    .algolia-search-input input {
    padding: 5px 0;
    width: 100%;
    outline: none;
    border: none;
    background: transparent;
    }
    .algolia-powered {
    float: right;
    }
    .algolia-powered img {
    display: inline-block;
    height: 18px;
    vertical-align: middle;
    }
    .algolia-results {
    position: relative;
    overflow: auto;
    padding: 10px 30px;
    height: calc(100% - 50px);
    }
    .algolia-results hr {
    margin: 10px 0;
    }
    .algolia-results .highlight {
    font-style: normal;
    margin: 0;
    padding: 0 2px;
    font-size: inherit;
    color: #f00;
    }
    .algolia-hits {
    margin-top: 20px;
    }
    .algolia-hit-item {
    margin: 15px 0;
    }
    .algolia-hit-item-link {
    display: block;
    border-bottom: 1px dashed #ccc;
    transition-duration: 0.2s;
    transition-timing-function: ease-in-out;
    transition-delay: 0s;
    }
    .algolia-pagination .pagination {
    margin-top: 40px;
    border-top: none;
    padding: 0;
    }
    .algolia-pagination .pagination-item {
    display: inline-block;
    }
    .algolia-pagination .page-number {
    border-top: none;
    }
    .algolia-pagination .page-number:hover {
    border-bottom: 1px solid #222;
    }
    .algolia-pagination .disabled-item {
    visibility: hidden;
    }
  11. source/css/main.styl文件的最后新增下面语句。

    1
    @import "_algolia/algolia";
  12. 将下面的图片放置于Hexo根目录下source文件夹下的images文件夹中,命名为algolia_logo.svg

    效果图

    经过漫长的修改终于成功了,效果图如下。

写在最后

  1. 为NexT主题添加Algolia搜索是一件复杂的事情,好消息是现在已经有版本在支持,但是还在开发阶段,未来一定会像多说等等集成在NexT主题中,但是到那个时候,旧版本升级到新版本会不会有很多不适配什么的,所以如果你想用,不如按照我的方法自己添加。

  2. 如果按照我的方式不行的话,应该是在使用的NexT主题在哪个地方和我目前的版本不太一样,需要自己进行调整了。

Jerky Lu wechat
欢迎加入微信公众号