安装Hexo

1npm install hexo-cli -g
2hexo init
3npm install hexo-renderer-pug hexo-renderer-stylus --save
4npm install hexo-deployer-git --save

安装Solitude

1git clone -b main https://github.com/everfu/hexo-theme-solitude.git themes/solitude

在博客根目录运行bash命令,将主题的配置文件复制到根目录,以便方便配置主题。

{% tabs copy [index] %}

1cp -rf ./themes/solitude/_config.yml ./_config.solitude.yml
1copy .\themes\solitude\_config.yml .\_config.solitude.yml

{% endtabs %}

配套插件

Solitude外挂标签

1npm install hexo-solitude-tag

文章生成短链接

1npm install hexo-abbrlink --save

_config.yml找到#url修改以下:

 1# URL
 2## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
 3url: https://www.jinjun.top
 4#permalink: :year/:month/:day/:title/
 5#permalink_defaults:
 6permalink: posts/:abbrlink.html
 7# 文章链接转:https://github.com/rozbo/hexo-abbrlink
 8abbrlink:
 9  alg: crc16 #support crc16(default) and crc32
10  rep: hex   #support dec(default) and hex
11pretty_urls:
12  trailing_index: true # Set to false to remove trailing 'index.html' from permalinks
13  trailing_html: true # Set to false to remove trailing '.html' from permalinks

本地搜索

1npm install hexo-generator-search --save

algolia搜索

1npm install hexo-algoliasearch --save

申请Id/API地址:https://www.algolia.com/

_congif.yml添加配置内容:

 1# algolia搜索: https://github.com/LouisBarranqueiro/hexo-algoliasearch
 2algolia:
 3  appId: "Z7A3XW4R2I"
 4  apiKey: "12db1ad54372045549ef465881c17e743"
 5  adminApiKey: "40321c7c207e7f73b63a19aa24c4761b"
 6  chunkSize: 5000
 7  indexName: "my-hexo-blog"
 8  fields:
 9    - content:strip:truncate,0,500
10    - excerpt:strip
11    - gallery
12    - permalink
13    - photos
14    - slug
15    - tags
16    - title

Rss

1npm install hexo-generator-feed --save

Sitemap

1npm install hexo-generator-sitemap --save

_congig.yml添加配置内容:

1# https://github.com/hexojs/hexo-generator-sitemap
2sitemap:
3  path: sitemap.xml
4  tags: true
5  categories: true

BaiduSitemap

1npm install hexo-generator-baidu-sitemap --save

_congig.yml 添加配置内容

1baidusitemap: 
2  path: baidusitemap.xml

hexo-indexnow

Bing搜索引擎,IndexNow插件

1npm install hexo-indexnow --save

_config.yml 添加配置内容

1hexo_indexnow:
2  count: latest # 数字或者 "latest"(=1)
3  txt_name: indexnow.txt # 链接文件名
4  apikey: xxxxxx # 你的 apikey
5  server: bing # indexnow 服务器,可选值有:bing、yandex、indexnow
6
7deploy:
8  - type: indexnow_url_submitter

字数统计

1npm install hexo-wordcount --save

安全转跳插件

1npm install hexo-safego --save

_config.yml 添加配置内容:

 1# hexo-safego安全跳转插件
 2# see https://blog.liushen.fun/posts/1dfd1f41/
 3hexo_safego:
 4  enable: true  # 是否启用 hexo-safego 插件
 5  enable_base64_encode: true  # 是否启用 Base64 编码链接
 6  enable_target_blank: true  # 是否在跳转链接中添加 target="_blank"
 7  url_param_name: 'u'  # URL 参数名,用于生成跳转链接
 8  html_file_name: 'go.html'  # 跳转页面文件名
 9  ignore_attrs:  # 需要忽略的链接属性列表
10    - 'data-fancybox'
11  apply_containers:  # 容器 ID 列表,如果为空则匹配整个 body
12    - '#article-container'
13  domain_whitelist:  # 域名白名单列表,包含白名单中的域名的链接将被忽略
14    - 'jinjun.top'
15  apply_pages:  # 生效页面路径列表,只有在这些页面上才会对链接进行处理
16    - '/posts/'
17  avatar: /img/siteicon/manifest-icon-192.maskable.png  # 头像图片链接
18  title: "钧言极客"  # 标题
19  subtitle: "安全中心"  # 副标题
20  darkmode: true  # 是否启用夜间模式
21  debug: false  # 是否启用调试模式,开启后会输出详细的调试信息

Gulp

1npm install gulp compress gulp-clean-css gulp-html-minifier-terser gulp-htmlclean gulp-terser --save-dev

在博客根目录新建 gulpfile.js 文件

 1var gulp = require('gulp');
 2var cleanCSS = require('gulp-clean-css');
 3var htmlmin = require('gulp-html-minifier-terser');
 4var htmlclean = require('gulp-htmlclean');
 5var terser = require('gulp-terser');
 6// 压缩js
 7gulp.task('compress', () =>
 8gulp.src(['./public/**/*.js', '!./public/**/*.min.js'])
 9.pipe(terser())
10.pipe(gulp.dest('./public'))
11)
12//压缩css
13gulp.task('minify-css', () => {
14return gulp.src(['./public/**/*.css'])
15.pipe(cleanCSS({
16compatibility: 'ie11'
17}))
18.pipe(gulp.dest('./public'));
19});
20//压缩html
21gulp.task('minify-html', () => {
22return gulp.src('./public/**/*.html')
23.pipe(htmlclean())
24.pipe(htmlmin({
25removeComments: true, //清除html注释
26collapseWhitespace: true, //压缩html
27collapseBooleanAttributes: true,
28//省略布尔属性的值,例如:<input checked="true"/> ==> <input />
29removeEmptyAttributes: true,
30//删除所有空格作属性值,例如:<input id="" /> ==> <input />
31removeScriptTypeAttributes: true,
32//删除<script>的type="text/javascript"
33removeStyleLinkTypeAttributes: true,
34//删除<style>和<link>的 type="text/css"
35minifyJS: true, //压缩页面 JS
36minifyCSS: true, //压缩页面 CSS
37minifyURLs: true  //压缩页面URL
38}))
39.pipe(gulp.dest('./public'))
40});
41
42// 运行gulp命令时依次执行以下任务
43gulp.task('default', gulp.parallel(
44'compress', 'minify-css', 'minify-html'
45))

Swpp

1npm install hexo-swpp swpp-backends --save

在博客根目录新建 sw-rules.js 文件:

  1module.exports.config = {
  2  /** @type {?ServiceWorkerConfig|boolean} */
  3  serviceWorker: {
  4    escape: 0,
  5    cacheName: 'SolitudeCache',
  6    debug: false,
  7  },
  8  register: {
  9    onsuccess: undefined,
 10    onerror: () =>
 11      console.error(
 12        'Service Worker 注册失败!可能是由于您的浏览器不支持该功能!'
 13      ),
 14    builder: (root, framework, pluginConfig) => {
 15      const { onerror, onsuccess } = pluginConfig.register;
 16      return `
 17            <script>
 18                (() => {
 19                    const sw = navigator.serviceWorker;
 20                    const error = ${onerror && onerror.toString()};
 21                    if (!sw?.register('${new URL(root).pathname}sw.js')
 22                        ${onsuccess ? `?.then(${onsuccess.toString()})` : ""}
 23                        ?.catch(error)
 24                    ) error()
 25                })()
 26            </script>`;
 27    },
 28  },
 29  /** @type {?DomConfig|boolean} */
 30  dom: {
 31    /** @type {?VoidFunction} */
 32    onsuccess: () => {
 33      caches
 34        .match(location.href)
 35        .then((res) => {
 36          if (res)
 37            res.json().then((json) => {
 38              utils &&
 39                utils.snackbarShow(
 40                  `已刷新缓存,更新为${json.global + '.' + json.local
 41                  }版本最新内容`,
 42                  false,
 43                  2000
 44                );
 45            });
 46          else console.info('未找到缓存');
 47        })
 48        .catch((error) => console.error('缓存匹配出错', error));
 49    },
 50  },
 51  /** @type {?VersionJsonConfig|boolean} */
 52  json: {
 53    /** @type {number} */
 54    maxHtml: 15,
 55    /** @type {number} */
 56    charLimit: 1024,
 57    /** @type {string[]} */
 58    merge: [],
 59    exclude: {
 60      /** @type {RegExp[]} */
 61      localhost: [],
 62      /** @type {RegExp[]} */
 63      other: [],
 64    },
 65  },
 66  /** @type {?ExternalMonitorConfig|boolean} */
 67  external: {
 68    /** @type {number} */
 69    timeout: 5000,
 70    /** 拉取文件时地并发限制 */
 71    concurrencyLimit: 100,
 72    /** @type {({head: string, tail: string}|function(string):string[])[]} */
 73    js: [],
 74    /** @type {RegExp[]} */
 75    stable: [
 76      /^https:\/\/npm\.elemecdn\.com\/[^/@]+\@[^/@]+\/[^/]+\/[^/]+$/,
 77      /^https:\/\/cdn\.cbd\.int\/[^/@]+\@[^/@]+\/[^/]+\/[^/]+$/,
 78      /^https:\/\/cdn\.jsdelivr\.net\/npm\/[^/@]+\@[^/@]+\/[^/]+\/[^/]+$/,
 79    ],
 80    replacer: (srcUrl) => {
 81      if (srcUrl.startsWith('https://cdn.jsdelivr.net/npm/')) {
 82        const pathname = new URL(srcUrl).pathname;
 83        return [
 84          srcUrl,
 85          `https://cdn.cbd.int/${pathname}`,
 86          `https://npm.elemecdn.com/${pathname}`,
 87          `https://fastly.jsdelivr.net/npm/${pathname}`,
 88        ];
 89      } else {
 90        return srcUrl;
 91      }
 92    },
 93  },
 94};
 95
 96module.exports.cacheRules = {
 97  simple: {
 98    clean: true,
 99    search: false,
100    match: (url, $eject) =>
101      url.host === $eject.domain && ['/404.html'].includes(url.pathname),
102  },
103  cdn: {
104    clean: true,
105    match: (url) =>
106      [
107        'cdn.cbd.int',
108        'lf26-cdn-tos.bytecdntp.com',
109        'lf6-cdn-tos.bytecdntp.com',
110        'lf3-cdn-tos.bytecdntp.com',
111        'lf9-cdn-tos.bytecdntp.com',
112        'cdn.staticfile.org',
113        'npm.elemecdn.com',
114      ].includes(url.host) &&
115      url.pathname.match(/\.(js|css|woff2|woff|ttf|cur)$/),
116  },
117};
118
119module.exports.getSpareUrls = (srcUrl) => {
120  if (srcUrl.startsWith('https://npm.elemecdn.com')) {
121    return {
122      timeout: 3000,
123      list: [
124        srcUrl,
125        `https://fastly.jsdelivr.net/${new URL(srcUrl).pathname}`,
126      ],
127    };
128  }
129};
130
131module.exports.ejectValues = (hexo, rules) => {
132  return {
133    domain: {
134      prefix: 'const',
135      value: new URL(hexo.config.url).host,
136    },
137  };
138};
139
140module.exports.skipRequest = (request) => request.url.startsWith("https://i0.hdslb.com") ||
141  request.url.startsWith('https://meting.qjqq.cn') ||
142  request.url.startsWith('https://api.i-meto.com');