引子
市面上有不少完善的个人笔记管理方案,但或多或少都不能满足笔者的需求:
- 笔记保存在本地:这样安全且稳定
- 使用markdown语法:兼容性强
- 可以便捷的发布写好的文章:从私有的混乱的笔记中整理出公开的成体系的文章无疑有利于个人对知识了如指掌 所以笔者便萌生了建立hexo+obsidian工具流搭建个人博客的想法,特此分享出来,以供参考。
观前提示:笔者也是边试错边写笔记,疏漏在所难免。如果绕了弯路,还望读者不吝赐教。
我的博客: andy's blog 最终效果图:
准备工作
安装 node.js 和 Git
windows 下正常安装即可:
输入:
node -v
npm -v
检测是否安装成功
安装 hexo
全局安装:
npm install -g hexo-cli
建站
选择一个文件夹,右键打开 gitbash:
hexo init
npm install
新建完成后,指定文件夹的目录如下:
.
├── _config.yml
├── package.json
├── scaffolds
├── source
| ├── _drafts
| └── _posts
└── themes
_config.yml: 网站的 配置 信息,您可以在此配置大部分的参数。scaffolds:模版文件夹。当您新建文章时,Hexo 会根据 scaffold 来创建文件。- Hexo 的模板是指在新建的文章文件中默认填充的内容。例如,如果您修改 scaffold/post.md 中的 Front-matter 内容,那么每次新建一篇文章时都会包含这个修改。
source: 资源文件夹是存放用户资源的地方。除_posts文件夹之外,开头命名为_(下划线)的文件 / 文件夹和隐藏的文件将会被忽略。Markdown 和 HTML 文件会被解析并放到 public 文件夹,而其他文件会被拷贝过去。themes: 主题 文件夹。Hexo 会根据主题来生成静态页面。package.json: 应用程序的信息。EJS, Stylus 和 Markdown 渲染引擎 已默认安装,您可以自由移除。package.json { "name": "hexo-site", "version": "0.0.0", "private": true, "hexo": { "version": "" }, "dependencies": { "hexo": "^7.0.0", "hexo-generator-archive": "^2.0.0", "hexo-generator-category": "^2.0.0", "hexo-generator-index": "^3.0.0", "hexo-generator-tag": "^2.0.0", "hexo-renderer-ejs": "^2.0.0", "hexo-renderer-stylus": "^3.0.0", "hexo-renderer-marked": "^6.0.0", "hexo-server": "^3.0.0", "hexo-theme-landscape": "^1.0.0" } }
再输入:
hexo g && hexo s
打开:localhost:4000 就可以看到成功部署的 hexo 博客啦。
安装 NexT 主题
NexT 主题是 hexo 的老牌强势主题。选它能在遇到问题时在网上搜到相当多的解决方案。
git 安装:
git clone https://github.com/theme-next/hexo-theme-next themes/next
npm安装:
npm install hexo-theme-next
这两种安装都可以,
git 安装后会在 themes/next 目录下存放源码,网上有不少直接修改源码进行美化的。
这显然==不利于后续版本升级==,因为会造成版本合并冲突。
更何况也不够优雅,笔者在之后的美化章节里会介绍如何使用官方推荐的方式优雅的调教主题
笔者这里选择npm安装。
再在 _config.yml里修改以启用 NexT 主题:
theme: next
将所需的NexT主题选项从默认主题配置文件复制到此配置文件中。通过以下命令复制整个默认主题配置文件:
cp node_modules/hexo-theme-next/_config.yml _config.next.yml
然后 _config.next.yml 就是next的主题配置
在 _config.next.yml 里设置:
darkmode: true #开启黑暗模式
scheme: Pisces #选择你想要的风格
NexT 主题的个性化设置
创建新的菜单
- 首先在 blog的根目录输入以下的命令:
hexo new page tags # tags可以是其他的 - 然后在这个
_config.yml的menu中把 tags这一项取消注释,然后重新构建即可menu: home: / || fa fa-home about: /about/ || fa fa-user tags: /tags/ || fa fa-tags categories: /categories/ || fa fa-th archives: /archives/ || fa fa-archive schedule: /schedule/ || fa fa-calendar sitemap: /sitemap.xml || fa fa-sitemap # commonweal: /404/ || fa fa-heartbeat
创建阅读时长
首先安装相应模块:
npm install hexo-symbols-count-time
将主题配置文件: ![[Hexo 搭建博客(新版)-20240501-3.png]] 打开
复制一下内容到 _config.yml:
symbols_count_time:
symbols: true
time: true
total_symbols: true
total_time: true
exclude_codeblock: false
awl: 2
wpm: 275
suffix: "mins."
友链
效果
设置文章自动折叠
在 nexT 主题中, 文章默认是全部展示 可以通过两种方式修改
- (Recommended)使用
<!-- more -->来手动打破文章<!-- more --> - 如果你已经添加了
description并将其值设置为你的文章摘要在头版,NexT摘录description作为序言文本在主页默认情况下。如果没有description,全部内容将是主页中的序言文本。 效果如下:
fellow me
在角落显示
本地搜索文章search
npm install hexo-generator-searchdb
_config.yml:
search:
path: search.xml
field: post
content: true
format: html
通过 custom file 添加鼠标爆炸效果
网上有不少让直接修改 nexT 主题的源代码以实现效果的 但正如之前所述: 这样==不利于后续版本升级==,会造成版本合并冲突
hexo 官方实际上提供了类似的接口 官方文档
下面是详细步骤:
在 next 的配置文件里打开 bodyEnd: source/_data/body-end.njk
custom_file_path:
#head: source/_data/head.njk
#header: source/_data/header.njk
#sidebar: source/_data/sidebar.njk
#postMeta: source/_data/post-meta.njk
#postBodyStart: source/_data/post-body-start.njk
#postBodyEnd: source/_data/post-body-end.njk
#footer: source/_data/footer.njk
bodyEnd: source/_data/body-end.njk
#variable: source/_data/variables.styl
#mixin: source/_data/mixins.styl
#style: source/_data/styles.styl
在如下文件夹下新增 _data/body-end.njk:
复制以下文件到里面
<canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas>
<script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script>
<script type="text/javascript">
"use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)}"use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)};
</script>
即可
部署到 GithubPage
最后当然是把我们的blog部署到GithubPage
这点官方文档说的很清楚了: 官方文档 照着操作就是了
结语
这是笔者系列笔记 [记录我搭建个人博客的全过程] 的第一篇。 这一篇里我们完成了博客的搭建,美化和部署。
下一篇里我们将改善博客糟糕的编辑体验,对obsidian进行深入个性化设置,敬请期待。
笔者今年大一,不懂的地方还有很多, 文章有什么不如意的地方还望读者海涵。