Loading

hexo+github搭建个人博客

一、环境准备

1. 安装 Node.js

  • 直接到官网上下载安装即可

    • Node.js (Node.js 版本需不低于 10.13,建议使用 Node.js 12.0 及以上版本)

    • Node 自带 npm

  • npm 换源(选择一个即可)

# 淘宝
npm config set registry https://registry.npmmirror.com
# 阿里云
npm config set registry https://npm.aliyun.com
# 腾讯云
npm config set registry http://mirrors.cloud.tencent.com/npm/
# 华为云
npm config set registry https://mirrors.huaweicloud.com/repository/npm/

2. 安装 Git

  • Windows:下载并安装 git.

二、安装 Hexo

  1. 安装 hexo
npm install -g hexo-cli
  1. 输入hexo -v查看版本
  1. 初始化 hexo,新建存储博客的文件夹
hexo init myblog

  1. 进入文件夹,安装 npm
cd myblog
npm install
  1. hexo 安装成功
  1. 启动服务站点
hexo g  或  hexo generate
hexo s  或  hexo server
  1. 本地访问:http://localhost:4000/

三、GitHub 上建站访问

1. 新建仓库

  • 仓库名称限制为:用户名+.github.io

2. 安装 hexo 上传插件

npm install hexo-deployer-git --save

3. 修改 hexo 配置文件指定仓库路径

  • 修改_config.yml,找到# Deploymentdeploy
deploy:
  type: git
  repo: 你的github仓库路径
  branch: master

4. 推送站点到 github

hexo d
  • 推送过程中需要输入你的 github 用户名和密码,密码需要用官方的 token 或者采用 ssh 公私钥访问

  • 解决方式:

    • 创建一个新 token,把它当密码输入即可

      • setting->Developer Settings->Prosonal access tokens
    • 采用 ssh 公私钥访问

  • 上传成功

5. 输入网址访问

四、更换主题

本文使用的主题是:shako

1. 下载主题并配置

cd myblog
git clone https://github.com/amehime/hexo-theme-shoka.git ./themes/shoka
  • 下载完成后,文件会出现在theme目录下
  • 编辑博客根目录下的_config.yml , 搜索theme,修改为:
theme: shoka
  • 删除_config.landscape.yml,这是 hexo 默认主题的配置文件,建议删除

2. 切换语言包

  • 编辑博客根目录下的_config.yml , 搜索language
language: zh-CN

3. 安装插件

3.1 hexo-renderer-multi-markdown-it

  1. 卸载掉默认的 hexo-renderer-marked ,以及别的 markdown 文件渲染器。
npm un hexo-renderer-marked --save
  1. 在博客根目录下用如下命令进行插件安装
npm i hexo-renderer-multi-markdown-it --save
  1. 编辑_config.yml, 配置插件参数,在合适位置添加
markdown:
  render: # 渲染器设置
    html: true # 过滤 HTML 标签
    xhtmlOut: true # 使用 '/' 来闭合单标签 (比如 <br />)。
    breaks: true # 转换段落里的 '\n' 到 <br>。
    linkify: true # 将类似 URL 的文本自动转换为链接。
    typographer:
    quotes: "“”‘’"
  plugins: # markdown-it 插件设置
    - plugin:
        name: markdown-it-toc-and-anchor
        enable: true
        options: # 文章目录以及锚点应用的 class 名称,shoka 主题必须设置成这样
          tocClassName: "toc"
          anchorClassName: "anchor"
    - plugin:
        name: markdown-it-multimd-table
        enable: true
        options:
          multiline: true
          rowspan: true
          headerless: true
    - plugin:
        name: ./markdown-it-furigana
        enable: true
        options:
          fallbackParens: "()"
    - plugin:
        name: ./markdown-it-spoiler
        enable: true
        options:
          title: "你知道得太多了"
  1. 继续编辑 _config.yml , 找到如下字段
highlight:
  enable: true
  line_number: true
  auto_detect: false
  tab_replace: ""
  wrap: true
  hljs: false

enable: true改成enable: false

3.2 hexo-autoprefixer

  1. 安装
npm install hexo-autoprefixer --save
  1. 编辑 _config.yml ,合适位置加入
autoprefixer:
  exclude:
    - "*.min.css"

3.3 hexo-lightning-minify

  1. 安装
npm install hexo-lightning-minify
  1. 编辑 _config.yml ,合适位置加入
minify:
  js:
    enable: false # ShokaX 自带 esbuild 优化,不建议开启,其他主题建议开启
    exclude: # 排除文件,接受 string[],需符合 micromatch 格式
  css:
    enable: true # 开启 CSS 优化
    options:
      targets: ">= 0.5%" # browserslist 格式的 target
    exclude: # 排除文件,接受 string[],需符合 micromatch 格式
  html:
    minifier: html-minifier
    enable: true # 开启 HTML 优化
    options:
      comments: false # 是否保留注释内容
    exclude: # 排除文件,接受 string[],需符合 micromatch 格式
  image:
    enable: true # 开启图片预处理和自动 WebP 化
    options:
      avif: false
      webp: true # 预留配置项,现版本无作用
      quality: 80 # 质量,支持1-100的整数、lossless或nearLossless
      effort: 2 # CPU 工作量,0-6之间的整数(越低越快)
      replaceSrc: true # 自动替换生成html中的本地图片链接为webp链接
      # 我们更建议使用 Service Worker 来在用户侧实现 replaceSrc 的功能,这将能够以一种侵入式更小的方式实现链接替换
    exclude:
  1. 修改hexo-lightning-minify/lib/img.js,这样转换格式之后会覆盖原图片
// transformImage函数
if (!(await promises_1.default.access(webpImagePath).catch(() => false))) {
	await (0, sharp_1.default)(sourceImagePath)
		.webp(sharpOptions)
		.toFile(webpImagePath)
		.then(info => {
			this.log.info(`Converted ${imagePath} to WebP (${info.size} bytes)`);
			// 添加如下内容
			// ----------
			// 检查转换后的图片是否成功写入文件系统
			if (info.size > 0) {
				// 如果成功,删除原始图片
				return promises_1.default.unlink(sourceImagePath);
			}
			// ----------
		})
		.catch(err => {
			if (err.toString().indexOf("Input file is missing") !== -1) {
				firstRun = true;
			} else {
				this.log.error(`Error converting ${imagePath} to WebP:`, err);
			}
		});
}

3.4 hexo-asset-image

在 Hexo 中引入本地图片而不用图床

  1. 安装
npm install https://github.com/xcodebuild/hexo-asset-image.git --save
  1. 修改项目根目录下的_config.yml文件参数post_asset_folder值为true
# 开始使用本地静态资源
post_asset_folder: true
  1. 完成上述配置后,在使用命令hexo new post新建文章时,将会在source/_posts目录下创建一个与文章同名的目录。
hexo new post "测试文章"
|____scaffolds
|____source
| |_____posts
| | |____测试文章.md
| | |____测试文章 # 与文章同名的目录,用于保存在文章中引入的本地图片资源
|____themes
  1. 引用
![本地图片](测试文章/本地图片.jpg)

Hexo 上 markdown 图片路径与 Typora 保持一致

  1. 安装
npm install hexo-image-link --save
  1. 打开 Hexo 资源管理配置开关
# _config.yml
post_asset_folder: true

3.6 hexo-symbols-count-time

统计页面或者站点的单词以及阅读所需时间

  1. 安装
npm install hexo-symbols-count-time
  1. 编辑_config.yml,修改以下内容
footer:
  # Specify the date when the site was setup. If not defined, current year will be used.
  since: 2010
  icon:
    name: sakura rotate
    # Change the color of icon, using Hex Code.
    color: "#ffc0cb"
  # Dependencies: https://github.com/theme-next/hexo-symbols-count-time
  count: true # 改为true
  powered: true

post:
  # Dependencies: https://github.com/theme-next/hexo-symbols-count-time
  count: true # 改为true

3.7 hexo-generator-searchdb

实现本地搜索

  1. 安装插件
npm install hexo-generator-searchdb
  1. 修改 page.js

将整个localSearch复制到主题的shoka/source/js/_app/page.js

const localSearch = function (pjax) {
	// 参考 hexo next 主题的配置方法
	// 参考 https://qiuyiwu.github.io/2019/01/25/Hexo-LocalSearch/ 博文
	if (CONFIG.localSearch === null) return;

	if (!siteSearch) {
		siteSearch = BODY.createChild("div", {
			id: "search",
			innerHTML:
				'<div class="inner"><div class="header"><span class="icon"><i class="ic i-search"></i></span><div class="search-input-container"><input class="search-input"autocompvare="off"placeholder="' +
				LOCAL.search.placeholder +
				'"spellcheck="false"type="text"id="local-search-input"></div><span class="close-btn"><i class="ic i-times-circle"></i></span></div><div class="results"id="search-results"><div class="inner"><div id="search-stats"></div><div id="search-hits"></div><div id="search-pagination"></div></div></div></div></div>',
		});
	}

	var isFetched = false;
	var datas;
	var isXml = true;
	var current_page = 0;
	var pageSize = parseInt(CONFIG.localSearch.pageSize, 10);
	if (isNaN(pageSize)) pageSize = 10;
	var total_pages = 0;
	var max_page_on_show = 7; // 一次最多显示 7 个页码
	var start_page = 0;
	var end_page = 0;
	var resultItems = [];

	// search DB path
	var searchPath = CONFIG.localSearch.path;
	if (searchPath.length == 0) {
		searchPath = "search.xml";
	} else if (searchPath.endsWith("json")) {
		isXml = false;
	}

	const input = $(".search-input"); // document.querySelector('.search-input');
	const resultContent = document.getElementById("search-hits");
	const paginationContent = document.getElementById("search-pagination");

	const getIndexByWord = function (word, text, caseSensitive) {
		if (CONFIG.localSearch.unescape) {
			var div = document.createElement("div");
			div.innerText = word;
			word = div.innerHTML;
		}
		var wordLen = word.length;
		if (wordLen === 0) {
			return [];
		}
		var startPosition = 0;
		var position = [];
		var index = [];
		if (!caseSensitive) {
			text = text.toLowerCase();
			word = word.toLowerCase();
		}

		while ((position = text.indexOf(word, startPosition)) > -1) {
			index.push({ position: position, word: word });
			startPosition = position + wordLen;
		}
		return index;
	};

	// Merge hits into slices
	const mergeIntoSlice = function (start, end, index, searchText) {
		var item = index[index.length - 1];
		var position = item.position;
		var word = item.word;
		var hits = [];
		var searchTextCountInSlice = 0;
		while (position + word.length <= end && index.length !== 0) {
			if (word === searchText) {
				searchTextCountInSlice++;
			}
			hits.push({
				position: position,
				length: word.length,
			});

			var wordEnd = position + word.length;

			// Move to next position of hit
			index.pop();
			while (index.length !== 0) {
				item = index[index.length - 1];
				position = item.position;
				word = item.word;
				if (wordEnd > position) {
					index.pop();
				} else {
					break;
				}
			}
		}
		return {
			hits: hits,
			start: start,
			end: end,
			searchTextCount: searchTextCountInSlice,
		};
	};

	// Highlight title and content
	const highlightKeyword = function (text, slice) {
		var result = "";
		var prevEnd = slice.start;
		slice.hits.forEach(function (hit) {
			result += text.substring(prevEnd, hit.position);
			var end = hit.position + hit.length;
			result += "<mark>" + text.substring(hit.position, end) + "</mark>";
			prevEnd = end;
		});
		result += text.substring(prevEnd, slice.end);
		return result;
	};

	const pagination = function () {
		const addPrevPage = function (current_page) {
			var classContent = "";
			var numberContent = "";
			if (current_page === 0) {
				classContent = "#search-pagination pagination-item disabled-item";
				numberContent = '<span class="#search-pagination page-number"><i class="ic i-angle-left"></i></span>';
			} else {
				classContent = "#search-pagination pagination-item";
				numberContent =
					'<a class="#search-pagination page-number" aria-label="Prev" href="#"><i class="ic i-angle-left"></i></a>';
			}
			var prevPage = '<li class="' + classContent + '" id="prev-page">' + numberContent + "</li>";
			return prevPage;
		};

		const addNextPage = function (current_page) {
			var classContent = "";
			var numberContent = "";
			if (current_page + 1 === total_pages) {
				classContent = "#search-pagination pagination-item disabled-item";
				numberContent = '<span class="#search-pagination page-number"><i class="ic i-angle-right"></i></span>';
			} else {
				classContent = "#search-pagination pagination-item";
				numberContent =
					'<a class="#search-pagination page-number"aria-label="Next"href="#"><i class="ic i-angle-right"></i></a>';
			}
			var nextPage = '<li class="' + classContent + '"id="next-page">' + numberContent + "</li>";
			return nextPage;
		};

		const addPage = function (index, current_page) {
			var classContent = "";
			var numberContent =
				'<a class="#search-pagination page-number"aria-label="' + (index + 1) + '"href="#">' + (index + 1) + "</a>";
			if (index === current_page) {
				classContent = "#search-pagination pagination-item current";
			} else {
				classContent = "#search-pagination pagination-item";
			}
			var page = '<li class="' + classContent + '" id="page-' + (index + 1) + '">' + numberContent + "</li>";
			return page;
		};

		const addPaginationEvents = function (start_page, end_page) {
			if (total_pages <= 0) {
				return;
			}
			const onPrevPageClick = function (event) {
				if (current_page > 0) {
					current_page -= 1;
				}
				if (current_page < start_page) {
					start_page = current_page;
					end_page = Math.min(end_page, start_page + max_page_on_show);
				}
				pagination();
			};
			const onNextPageClick = function (event) {
				if (current_page + 1 < total_pages) {
					current_page += 1;
				}
				if (current_page > end_page) {
					end_page = current_page;
					start_page = Math.max(0, end_page - max_page_on_show);
				}
				pagination();
			};
			const onPageClick = function (event) {
				var page_number = parseInt(event.target.ariaLabel);
				current_page = page_number - 1; // note minus 1 here
				pagination();
			};

			var prevPage = document.getElementById("prev-page");
			if (prevPage != null) prevPage.addEventListener("click", onPrevPageClick);

			var nextPage = document.getElementById("next-page");
			if (nextPage != null) nextPage.addEventListener("click", onNextPageClick);
			for (var i = start_page; i < end_page; i += 1) {
				var page = document.getElementById("page-" + (i + 1));
				if (page != null) page.addEventListener("click", onPageClick);
			}
		};

		paginationContent.innerHTML = ""; // clear

		var begin_index = Math.min(current_page * pageSize, resultItems.length);
		var end_index = Math.min(begin_index + pageSize, resultItems.length);

		resultContent.innerHTML = resultItems
			.slice(begin_index, end_index)
			.map(function (result) {
				return result.item;
			})
			.join("");

		start_page = Math.max(0, total_pages - max_page_on_show);
		end_page = start_page + Math.min(total_pages, max_page_on_show);
		var pageContent = '<div class="#search-pagination">';
		pageContent += '<div class="#search-pagination pagination">';
		pageContent += "<ul>";
		if (total_pages > 0) {
			// add prev page arrow, when no prev page not selectable
			pageContent += addPrevPage(current_page);
			for (var i = start_page; i < end_page; i += 1) {
				pageContent += addPage(i, current_page);
			}
			// add next page arrow, when no next page not selectable
			pageContent += addNextPage(current_page);
		}
		pageContent += "</ul>";
		pageContent += "</div>";
		pageContent += "</div>";
		paginationContent.innerHTML = pageContent;
		addPaginationEvents(start_page, end_page);
		resultContent.scrollTop = 0; // scroll to top
		window.pjax && window.pjax.refresh(resultContent);
	};

	const inputEventFunction = function () {
		if (!isFetched) {
			console.log("Data not fetched.");
			return;
		}

		var searchText = input.value.trim().toLowerCase();
		var keywords = searchText.split(/[-\s]+/);

		if (keywords.length > 1) {
			keywords.push(searchText);
		}

		resultItems = [];
		if (searchText.length > 0) {
			// Perform local searching
			datas.forEach(function (index) {
				var categories = index.categories,
					title = index.title,
					content = index.content,
					url = index.url;
				var titleInLowerCase = title.toLowerCase();
				var contentInLowerCase = content.toLowerCase();
				var indexOfTitle = [];
				var indexOfContent = [];
				var searchTextCount = 0;
				keywords.forEach(function (keyword) {
					indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
					indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
				});

				// Show search results
				if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
					var hitCount = indexOfTitle.length + indexOfContent.length;
					// Sort index by position of keyword
					[indexOfTitle, indexOfContent].forEach(function (index) {
						index.sort(function (itemLeft, itemRight) {
							if (itemRight.position !== itemLeft.position) {
								return itemRight.position - itemLeft.position;
							}
							return itemLeft.word.length - item.word.length;
						});
					});

					var slicesOfTitle = [];
					if (indexOfTitle.length !== 0) {
						var tmp = mergeIntoSlice(0, title.length, indexOfTitle, searchText);
						searchTextCount += tmp.searchTextCountInSlice;
						slicesOfTitle.push(tmp);
					}

					var slicesOfContent = [];
					while (indexOfContent.length !== 0) {
						var item = indexOfContent[indexOfContent.length - 1];
						var position = item.position;
						var word = item.word;
						// Cut out 100 characters
						var start = position - 20;
						var end = position + 30;
						if (start < 0) {
							start = 0;
						}
						if (end < position + word.length) {
							end = position + word.length;
						}
						if (end > content.length) {
							end = content.length;
						}
						var tmp = mergeIntoSlice(start, end, indexOfContent, searchText);
						searchTextCount += tmp.searchTextCountInSlice;
						slicesOfContent.push(tmp);
					}

					// Sort slices in content by search text's count and hits' count
					slicesOfContent.sort(function (sliceLeft, sliceRight) {
						if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
							return sliceRight.searchTextCount - sliceLeft.searchTextCount;
						} else if (sliceLeft.hits.length !== sliceRight.hits.length) {
							return sliceRight.hits.length - sliceLeft.hits.length;
						}
						return sliceLeft.start - sliceRight.start;
					});

					// Select top N slices in content
					var upperBound = parseInt(CONFIG.localSearch.pageSize, 10);
					if (upperBound >= 0) {
						slicesOfContent = slicesOfContent.slice(0, upperBound);
					}

					var resultItem = "";
					resultItem += '<div class="#search-hits item">';
					// resultItem += '<div class="#search-hits">';
					// resultItem += '<ol class="item">'
					resultItem += "<li>";
					// resultItem += '<li>';
					var cats =
						categories !== undefined
							? "<span>" + categories.join('<i class="ic i-angle-right"></i>') + "</span>"
							: "<span>No categories</span>";
					resultItem += '<a href="' + url + '">' + cats;
					if (slicesOfTitle.length !== 0) {
						// resultItem += '<li><a href="'+url}">'+highlightKeyword(title, slicesOfTitle[0])}</a>';
						resultItem += "<b>" + highlightKeyword(title, slicesOfTitle[0]) + "</b><br>";
					} else {
						// resultItem += '<li><a href="'+url}">'+title}</a>';
						resultItem += "<b>" + title + "</b><br>";
					}

					slicesOfContent.forEach(function (slice) {
						return (resultItem += '<li class="#search-hits subitem">' + highlightKeyword(content, slice) + " ...</li>");
					});
					// resultItem += '</li>';
					resultItem += "</a>";
					resultItem += "</li>";
					// resultItem += '</ol>';
					resultItem += "</div>";
					resultItems.push({
						item: resultItem,
						id: resultItems.length,
						hitCount: hitCount,
						searchTextCount: searchTextCount,
					});
				}
			});
		}

		if (keywords.length === 1 && keywords[0] === "") {
			resultContent.innerHTML = '<div id="no-result"><i></i></div>';
		} else if (resultItems.length === 0) {
			resultContent.innerHTML = '<div id="no-result"><i></i></div>';
		} else {
			resultItems.sort(function (resultLeft, resultRight) {
				if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
					return resultRight.searchTextCount - resultLeft.searchTextCount;
				} else if (resultLeft.hitCount !== resultRight.hitCount) {
					return resultRight.hitCount - resultLeft.hitCount;
				}
				return resultRight.id - resultLeft.id;
			});
		}

		// Do pagination
		total_pages = Math.ceil(resultItems.length / pageSize);
		pagination();
	};

	const fetchData = function () {
		fetch(CONFIG.root + searchPath)
			.then(function (response) {
				return response.text();
			})
			.then(function (res) {
				// Get the contents from search data
				isFetched = true;
				datas = isXml
					? [new DOMParser().parseFromString(res, "text/xml").querySelectorAll("entry")].map(function (element) {
							return {
								title: element.querySelector("title").textContent,
								content: element.querySelector("content").textContent,
								url: element.querySelector("url").textContent,
							};
					  })
					: JSON.parse(res);
				// Only match articles with not empty titles
				datas = datas
					.filter(function (data) {
						return data.title;
					})
					.map(function (data) {
						data.title = data.title.trim();
						data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, "") : "";
						data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, "/");
						return data;
					});
				// Remove loading animation
				document.getElementById("search-hits").innerHTML = "<i></i>";
				inputEventFunction();
			});
	};

	if (CONFIG.localSearch.preload) {
		console.log("fetch data.");
		fetchData();
	}

	if (CONFIG.localSearch.trigger === "auto") {
		input.addEventListener("input", inputEventFunction);
	} else {
		document.querySelector(".search-icon").addEventListener("click", inputEventFunction);
		input.addEventListener("keypress", function (event) {
			if (event.key === "Enter") {
				inputEventFunction();
			}
		});
	}

	// Handle and trigger popup window
	document.querySelectorAll(".popup-trigger").forEach(function (element) {
		element.addEventListener("click", function () {
			document.body.style.overflow = "hidden";
			document.querySelector(".search-pop-overlay").classList.add("search-active");
			input.focus();
			if (!isFetched) fetchData();
		});
	});

	// Handle and trigger popup window
	$.each(".search", function (element) {
		element.addEventListener("click", function () {
			document.body.style.overflow = "hidden";
			transition(siteSearch, "shrinkIn", function () {
				$(".search-input").focus();
			}); // transition.shrinkIn
		});
	});

	// Monitor main search box
	const onPopupClose = function () {
		document.body.style.overflow = "";
		transition(siteSearch, 0); // "transition.shrinkOut"
	};

	siteSearch.addEventListener("click", function (event) {
		if (event.target === siteSearch) {
			onPopupClose();
		}
	});

	$(".close-btn").addEventListener("click", onPopupClose);
	window.addEventListener("pjax:success", onPopupClose);
	window.addEventListener("keyup", function (event) {
		if (event.key === "Escape") {
			onPopupClose();
		}
	});
};
  1. 修改 script.js

shoka/scripts/generaters/script.js中主要是读取配置,添加如下代码

...........  // 省略若干代码
if(config.algolia) {
  siteConfig.search = {
    appID    : config.algolia.appId,
    apiKey   : config.algolia.apiKey,
    indexName: config.algolia.indexName,
    hits     : theme.search.hits
  }
}
// 以下为需要添加的代码
if(config.search) {
  siteConfig.localSearch = {
    enable: config.search.enable,
    path: config.search.path,
    field: config.search.field,
    format: config.search.format,
    limit: config.search.limit,
    content: config.search.content,
    unescape: config.search.unescape,
    preload:  config.search.preload,
    trigger: config.search.trigger,
    pageSize: config.search.pageSize
  }
}
  1. 修改 pjax.js

shoka/source/js/_app/pjax.js中是启动搜索功能的部分,这里在两个配置都有的情况下默认使用本地搜索而不是 Algolia

if (CONFIG.localSearch != null) {
	localSearch(pjax);
} else if (CONFIG.search != null) {
	algoliaSearch(pjax);
}
  1. 添加配置

最后在 hexo 配置(最外层的_config.yml)中添加 search 配置

search:
  enable: true
  path: search.json # search.xml
  field: post
  format: html
  limit: 10000
  content: true
  unescape: true
  preload: true
  trigger: "auto"
  pageSize: 10

4. 其他配置

4.1 修改图库

  • 默认的图片列表位于/themes/shoka/_images.yml

  • 在图片列表 yml 文件中,写上任意外链图片地址(至少六张)

  • 此处使用PICUI存储图片

- https://img.picui.cn/free/2024/08/21/66c4d0d10b26a.png
- https://img.picui.cn/free/2024/08/21/66c4d0a9ead74.png
- https://img.picui.cn/free/2024/08/21/66c4d0a5b6deb.jpg
- https://img.picui.cn/free/2024/08/21/66c4d0a4c555f.jpg
- https://img.picui.cn/free/2024/08/21/66c4d0a4a4f14.jpg
- https://img.picui.cn/free/2024/08/21/66c4d0a3d8384.jpg
- https://img.picui.cn/free/2024/08/21/66c4d0a2ef2fd.jpg
- https://img.picui.cn/free/2024/08/21/66c4d07600f66.jpg
- https://img.picui.cn/free/2024/08/21/66c4d07601b05.jpg
- https://img.picui.cn/free/2024/08/21/66c5702de9e58.jpg
- https://img.picui.cn/free/2024/08/21/66c5702be1c36.jpg

4.2 algolia 搜索功能

  • 使用 Github 账号登录Algolia

  • 进入 Dashboard - Search - Index 页面,选择上方 + Create Index 创建索引,索引名称建议为 shokaX

  • 进入 Dashboard - Settings - API Keys 页面,复制如下数据到上方配置中。

页面数据 对应配置
Application ID appId
Search-Only API Key apiKey
Admin API Key adminApiKey
创建的索引名 indexName
  • 在博客部署前运行 hexo algolia 上传索引,可在 Dashboard - Search - Index 页面中查看。

  • 执行hexo d更新博客,将所有文件更新到 github 即可实现搜索功能

五、问题汇总

1. GitHub 连接不上

  • 解决方法:取消代理
git config --global --unset http.proxy
git config --global --unset https.proxy
  • 查看代理
git config --global --get http.proxy
git config --global --get https.proxy

2. <br>换行符识别不了

  • 解决方法:
# _config.yml
markdown:
  render: # 渲染器设置
    html: true # 过滤 HTML 标签

3. 代码块渲染问题

  1. 修改 hexo 的 package.json 的文件内容,将 shoka 的 example 目录中的 package.json 文件的内容拷贝到 hexo 的 package.json 中即可

  2. 修改完 package.json 文件内容后执行下面的命令降级 hexo

npm install -g hexo@5.4.2
  1. 然后在 hexo 目录下更新依赖
npm install
  1. 然后重新生成即可
hexo clean && hexo g && hexo s

4. 取消目录自动编号

  • 目前的解决方式:通过 css 将其隐藏

  • 进入themes\shoka\source\css\_common\outline\sidebar,修改toc.styl文件

ol {
  padding: 0 .125rem .3125rem .625rem;
  text-align: left;

  >ol {
    padding-left: 0;
  }

  // 添加如下代码
  .toc-number {
    display: none;
  }
}

六、参考文章

posted @ 2024-12-05 09:09  iRuriCatt  阅读(3)  评论(0编辑  收藏  举报