安装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 添加配置内容
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');