Hexo博客搭建(重构版)
Hexo博客搭建(重构版)
王貔貅搭建Hexo
Hexo官方文档:https://hexo.io/zh-cn/docs/
- 安装 node.js
- 官网下载: https://nodejs.org/en/
- 安装后验证:
node -v
- 安装 git
- 官网下载: https://git-scm.com/download/win
- 安装后验证:
git -v
npm 换源
1 | npm config set registry https://registry.npm.taobao.org |
安装cnpm
1 | # 安装 |
安装
安装Hexo
1 | # 安装 |
Hexo初始化
1 | # 初始化hexo |
安装Anzhiyu主题
Anzhiyu:https://docs.anheyu.com/
1 | # dev |
安装插件
1 | # npm |
安装 hexo-deployer-git 插件 推送更新
项目地址:https://github.com/hexojs/hexo-deployer-git
1 | # 安装插件 |
修改_config.yml
文件
1 | deploy: |
安装 hexo-abbrlink 插件 生成永久链接
项目地址:https://github.com/Rozbo/hexo-abbrlink
1 | npm install hexo-abbrlink --save |
修改_config.yml
文件
1 | permalink: posts/:abbrlink.html # 此处可以自己设置 |
配置 Algolia search
algolia官网:https://dashboard.algolia.com
1 | # 安装插件 |
修改_config.yml
文件
1 | algolia: |
通过 hexo algolia
更新
安装 hexo-wordcount 字数统计
1 | # 安装 |
修改_config.anzhiyu.yml
文件
1 | wordcount: |
实现 RSS 订阅
安装插件hexo-generator-feed
1 | npm install --save hexo-generator-feed |
配置_config.yml
文件
1 | #订阅RSS |
修改_config.anzhiyu.yml
文件
1 | rss: /atom.xml |
Twikoo 部署
docker私有部署
1 | # docker |
朋友圈配置
朋友圈前端:https://github.com/anzhiyu-c/hexo-circle-of-friends-front
前端配置详见文档:https://docs.anheyu.com/page/fcircle.html
HEO前端配置:https://blog.zhheo.com/p/4e18a507.html
anzhiyu主题前端存在一定bug,修改使用MySQL数据库无法保存,可使用Heo前端进行更改
朋友后端项目地址:https://github.com/Rock-Candy-Tea/hexo-circle-of-friends
文档:https://fcircle-doc.yyyzyyyz.cn/#/
clone项目仓库
1 | git clone https://github.com/Rock-Candy-Tea/hexo-circle-of-friends |
提前下载镜像
1 | # docker |
编辑/hexo_circle_of_friends/fc_settings.yaml
文件
1 | LINK: [ |
切换到项目目录下,运行部署脚本
1 | 需 python 版本3.8 |
留言板(弹幕版)
1 | hexo new page comments |
comment.js
1 | class EasyDanmaku{constructor(t){this.container=this.checkParams(t),this.pathname=t.page||null,this.wrapperStyle=t.wrapperStyle||null,this.line=t.line||10,this.speed=t.speed||5,this.runtime=t.runtime||10,this.colourful=t.colourful||!1,this.loop=t.loop||!1,this.hover=t.hover||!1,this.coefficient=t.coefficient||1.38,this.originIndex=0,this.originList=null,this.offsetValue=this.container.offsetHeight/this.line,this.vipIndex=0,this.overflowArr=[],this.clearIng=!1,this.cleartimer=null,this.init(),this.handleEvents(t)}handleEvents(t){this.onComplete=t.onComplete||null,this.onHover=t.onHover||null}init(){this.runstatus=1,this.aisle=[],this.container.style.overflow="hidden",this.hover&&this.handleMouseHover(),"relative"!==Utils.getStyle(this.container,"position")&&"fixed"!==Utils.getStyle(this.container,"position")&&(this.container.style.position="relative");for(let t=0;t<this.line;t++)this.aisle.push({normalRow:!0,vipRow:!0})}checkParams(t){if(!document.querySelector(t.el))throw`Could not find the ${t.el} element`;if(t.wrapperStyle&&"string"!=typeof t.wrapperStyle)throw"The type accepted by the wrapperStyle parameter is string";if(t.line&&"number"!=typeof t.line)throw"The type accepted by the line parameter is number";if(t.speed&&"number"!=typeof t.speed)throw"The type accepted by the speed parameter is number";if(t.colourful&&"boolean"!=typeof t.colourful)throw"The type accepted by the colourful parameter is boolean";if(t.runtime&&"number"!=typeof t.runtime)throw"The type accepted by the runtime parameter is number";if(t.loop&&"boolean"!=typeof t.loop)throw"The type accepted by the loop parameter is boolean";if(t.coefficient&&"number"!=typeof t.coefficient)throw"The type accepted by the coefficient parameter is number";if(t.hover&&"boolean"!=typeof t.hover)throw"The type accepted by the hover parameter is boolean";if(t.onComplete&&"function"!=typeof t.onComplete)throw"The type accepted by the onComplete parameter is function";if(t.onHover&&"function"!=typeof t.onHover)throw"The type accepted by the onHover parameter is function";return document.querySelector(t.el)}send(t,e=null,i=null){if(0==this.runstatus)return void this.overflowArr.push({content:t,normalClass:e});if(t.length<1)return;let n=document.createElement("div"),r=0,s=this.speed,o=null,l=0;n.innerHTML=t,n.style.display="inline-block",n.classList.add("default-style"),(e||this.wrapperStyle)&&n.classList.add(e||this.wrapperStyle),function a(){if(r=Math.round(Math.random()*(this.line-1)),this.aisle[r].normalRow){this.aisle[r].normalRow=!1,this.container.appendChild(n),s+=n.offsetWidth/n.parentNode.offsetWidth*2,n.style.cssText=`\n text-align:center;\n min-width:130px;\n will-change: transform;\n position:absolute;\n right: -${n.offsetWidth+130}px;\n transition: transform ${s}s linear;\n transform: translateX(-${n.parentNode.offsetWidth+n.offsetWidth+130}px);\n top: ${r*this.offsetValue}px;\n line-height:${this.offsetValue}px;\n color:${this.colourful?"#"+("00000"+(16777216*Math.random()<<0).toString(16)).substr(-6):void 0}\n `;let t=(n.parentNode.offsetWidth+n.offsetWidth)/s/60;o=setInterval((()=>{l+=t,l>n.offsetWidth*this.coefficient&&(this.aisle[r].normalRow=!0,clearInterval(o))}),16.66),setTimeout((()=>{1!=n.getAttribute("relieveDel")&&(i&&i({runtime:s,target:n,width:n.offsetWidth}),n.remove())}),1e3*s)}else{this.aisle.some((t=>!0===t.normalRow))?a.call(this):(()=>{this.overflowArr.push({content:t,normalClass:e}),this.clearIng||this.clearOverflowDanmakuArray()})()}}.call(this)}batchSend(t,e=!1,i=null){let n=this.runtime||1.23*t.length;this.originList=t,this.hasAvatar=e,this.normalClass=i;let r=setInterval((()=>{location.pathname!=this.pathname&&clearInterval(r),this.originIndex>t.length-1?(clearInterval(r),this.originIndex=0,this.onComplete&&this.onComplete(),this.loop&&this.batchSend(this.originList,e,i)):(e?this.send(`${t[this.originIndex].url?'<a href="'+t[this.originIndex].url+'">':""}<img src=${t[this.originIndex].avatar}>\n <p>${t[this.originIndex].content}</p>${t[this.originIndex].url?"</a>":""}\n `,i||this.wrapperStyle):this.send(t[this.originIndex],i||this.wrapperStyle),this.originIndex++)}),n/t.length*1e3)}centeredSend(t,e,i=3e3,n=null){let r=document.createElement("div"),s=0;r.innerHTML=t,(e||this.wrapperStyle)&&r.classList.add(e||this.wrapperStyle),function t(){if(this.aisle[s].vipRow)this.container.appendChild(r),r.style.cssText=`\n position:absolute;\n left:50%;\n transform:translateX(-50%);\n top: ${s*this.offsetValue}px;\n `,this.aisle[s].vipRow=!1,setTimeout((()=>{n&&n({duration:i,target:r,width:r.offsetWidth}),r.remove(),this.aisle[s].vipRow=!0}),i);else{if(s++,s>this.line-1)return;t.call(this)}}.call(this)}play(){const t=this.container.children;for(let e=0;e<t.length;e++)this.controlDanmakurunStatus(t[e],1);this.runstatus=1,0!==this.overflowArr.length&&this.clearOverflowDanmakuArray()}pause(){const t=this.container.children;for(let e=0;e<t.length;e++)this.controlDanmakurunStatus(t[e],0);this.runstatus=0}controlDanmakurunStatus(t,e){const i=0,n=/-(\S*),/;if(e===1){clearTimeout(t.timer);const e=Utils.getStyle(t,"transform").match(n)[1];t.style.transition=`transform ${this.speed}s linear`,t.style.transform=`translateX(-${t.parentNode.offsetWidth+parseInt(e)+t.offsetWidth+130}px)`,t.timer=setTimeout((()=>{t.remove()}),1e3*this.speed)}else if(e===i){clearTimeout(t.timer);const e=Utils.getStyle(t,"transform").match(n)[1];t.style.transition="transform 0s linear",t.style.transform=`translateX(-${e}px)`,t.setAttribute("relieveDel",1)}}handleMouseHover(){Utils.eventDelegation(this.container,"default-style","mouseover",(t=>{t.style["z-index"]=1e3,this.controlDanmakurunStatus(t,0),this.onHover&&this.onHover(t)})),Utils.eventDelegation(this.container,"default-style","mouseout",(t=>{t.style.zIndex=1,1==this.runstatus&&this.controlDanmakurunStatus(t,1)}))}clearOverflowDanmakuArray(){clearInterval(this.cleartimer),this.clearIng=!0;let t=0;this.cleartimer=setInterval((()=>{0===this.overflowArr.length?(t++,t>20&&(clearInterval(this.cleartimer),this.clearIng=!1)):(this.send(this.overflowArr[0].content,this.overflowArr[0].normalClass||this.wrapperStyle),this.overflowArr.shift())}),500)}}class Utils{static getStyle(t,e){return window.getComputedStyle(t,null)[e]}static eventDelegation(t,e,i,n){t.addEventListener(i,(t=>{try{t.target.className.includes(e)?n(t.target):t.target.parentNode.className.includes(e)&&n(t.target.parentNode)}catch(t){}}))}} |
comments.js
1 | // todo:留言板地址,twikoo地址 |
修改_config.yml
文件
1 | bottom: |
在source/about/index.md
文件下添加如下内容
1 | <style> |
小风扇
在博客根目录执行以下命令
1 | hexo new page air-conditioner |
然后在会生成source/air-conditioner/index.md
,将以下内容替换原内容
1 | --- |
其他部署方式——代码嵌入网站
1 | <iframe height="740" src="https://loquacious-bienenstitch-58539b.netlify.app/"></iframe> |
自行构建部署
项目地址:https://github.com/anzhiyu-c/air-conditioner-vue
Hexo部署服务器(免密登录)
创建git用户
1 | 创建git用户 |
添加秘钥
**git**
用户下操作
1 | mkdir -p ~/.ssh |
测试本地连接服务器
1 | yourIp为远程服务器的ip地址 |
创建git仓库
1 | mkdir /www/git/ |
在 blog.git/hooks
文件夹下创建一个 post-receive
钩子
1 | cd blog.git/hooks |
在 post-receive
文件中输入以下代码
1 | !/bin/bash |
授予 post-receive
文件可执行权限
1 | chmod +x /www/git/blog.git/hooks/post-receive |
切换git
用户下操作
1 | sudo mkdir -p /www/wwwroot/hexo |
测试
1 | ssh -v git@你的 ip 地址 |
修改_config.yml
1 | deploy: |
hexo cl;hexo g;hexo d
服务器404页面开启
兰空图床部署
简介
Lsky Pro:一个开源图床,用来最终存放图片的地方,支持第三方云储存,本地、阿里云 OSS、腾讯云 COS、七牛云、又拍云、FTP。Lsky Pro 是一个用于在线上传、管理图片的图床程序,中文名:兰空图床,你可以将它作为自己的云上相册,亦可以当作你的写作贴图库。兰空图床始于 2017 年 10 月,最早的版本由 ThinkPHP 5 开发,后又经历了数个版本的迭代,在 2021 年末启动了新的重写计划并于 2022 年 3 月份发布全新的 2.0 版本。
项目地址:https://github.com/lsky-org/lsky-pro
文档:https://docs.lsky.pro/
特性:
- 支持本地等多种第三方云储存 AWS S3、阿里云 OSS、腾讯云 COS、七牛云、又拍云、SFTP、FTP、WebDav、Minio
- 多种数据库驱动支持,MySQL 5.7+、PostgreSQL 9.6+、SQLite 3.8.8+、SQL Server 2017+
- 支持配置使用多种缓存驱动,Memcached、Redis、DynamoDB、等其他关系型数据库,默认以文件的方式缓存
- 多图上传、拖拽上传、粘贴上传、动态设置策略上传、复制、一键复制链接
- 强大的图片管理功能,瀑布流展示,支持鼠标右键、单选多选、重命名等操作
- 自由度极高的角色组配置,可以为每个组配置多个储存策略,同时储存策略可以配置多个角色组
- 可针对角色组设置上传文件、文件夹路径命名规则、上传频率限制、图片审核等功能
- 支持图片水印、文字水印、水印平铺、设置水印位置、X/y 轴偏移量设置、旋转角度等
- 支持通过接口上传、管理图片、管理相册
- 支持在线增量更新、跨版本更新
- 图片广场
安装要求:
- PHP >= 8.0.2
- Mysql>=5.7
- BCMath PHP 扩展
- Ctype PHP 扩展
- DOM PHP 拓展
- Fileinfo PHP 扩展
- JSON PHP 扩展
- Mbstring PHP 扩展
- OpenSSL PHP 扩展
- PDO PHP 扩展
- Tokenizer PHP 扩展
- XML PHP 扩展
- Imagick 拓展
- exec、shell_exec 函数
- readlink、symlink 函数
- putenv、getenv 函数
安装
将安装包上传至站点目录然后解压,将站点的运行目录指向程序的 public 文件夹。
Nginx
设置伪静态规则。
1 | location / { |
安装fileinfo
,imagemagick
和exif
扩展。
取消禁用函数exec、shell_exec 、readlink、symlink 、putenv、getenv。
配置好域名以后,访问站点 首页 ,程序会自动跳转至安装页面,环境检测通过以后即可通过引导进行安装。
原图保护
原图保护的作用是隐藏图片的真实 url 路径,未开启前图片是通过运行环境输出并缓存的,开启后请求该图片会通过 PHP 接管,由 PHP 处理图片的输出。
开启原图保护功能后访问图片 404
这种情况是因为运行环境接管图片资源导致的,请更改 nginx 或 apache 的配置,例如 nginx:
1 | # ... |
将这段代码注释掉即可。
CDN加速
简单图床-EasyImage 部署
项目地址:https://github.com/icret/EasyImages2.0
宝塔软件商店 -> 一键部署 -> 简单图床