Hexo系列
[十万字图文教程]基于Hexo的matery主题搭建博客并深度优化完全一站式教程
- Hexo Docker环境与Hexo基础配置篇
- hexo博客自定义修改篇
- hexo博客网络优化篇
- hexo博客增强部署篇
- hexo博客个性定制篇
- hexo博客常见问题篇
- hexo博客博文撰写篇之完美笔记大攻略终极完全版
- Hexo Markdown以及各种插件功能测试
- markdown 各种其它语法插件,latex公式支持,mermaid图表,plant uml图表,URL卡片,bilibili卡片,github卡片,豆瓣卡片,插入音乐和视频,插入脑图,插入PDF,嵌入iframe
- 在 Hexo 博客中插入 ECharts 动态图表
- 使用nodeppt给hexo博客嵌入PPT演示
- GithubProfile美化与自动获取RSS文章教程
- Vercel部署高级用法教程
- webhook部署Hexo静态博客指南
- 在宝塔VPS上面采用docker部署waline全流程图解教程
- 自建Umami访问统计服务并统计静态博客UV/PV
独立博客相关组织
一些有趣的相关组织介绍。
一个人码文太孤单,但是,别忘了,你还有这么多同路人!
建立独立博客之后,可以试着加入一些博客联盟,相互学习,引流!
BlogFinder
发现优秀的个人博客
BlogWe
致敬还在写博客的我们!
笔墨迹
# 致敬还在写博客的我们
博客录
# 博客收录展示平台
博客之家」—— 一个专注收录优质个人博客的导航网站。
我们之所以做这件事,是因为相信个人博客是互联网世界里特别的存在。现在很多网站小编为了完成KPI,生产出大量“洗稿”或拼凑出来的内容。这些东西到底是在丰富互联网,还是在拉低整体内容质量?真的不好说。
但个人博客不一样。没有业绩压力,没有流量绑架,博主写的都是自己真实的想法和见解。它像是互联网的一片自留地,安静、干净,也更有灵魂。
博客圈
# 致力于个人独立博客,按地区分类,由段先森维护
博客志
优秀个人独立博客导航
博友圈
# 博客人的朋友圈
川流
川流,取川之意,流水之情
个站商店
# 一个精致的,带社交元素的个人网站发布平台,博客收录网站
积薪
致敬那些还在写博客的人
开往
# 让传统友链“活跃”,让网页相互接力,让流量相互流动,让网络开放起来
未来信息
希望在未来还能遇见你博客的信息
十年之约
十年之约,即从加入这个活动起,我们的博客十年不关闭,保持更新和活力!
中文博客列表导航项目
尝试链接几乎所有的中文博客
中文独立博客列表
一个自动合并
PR,CVS管理博客列表的Github项目加入方法,点击 Fork chinese-independent-blogs
- 在 ./blogs-original.csv 中填入博客的 名称、URL、RSS以及标签
- 提交 PR
- (自动) PR 被 merge 之后 README 通过 ./script.js 生成
中文独立博客文章聚合搜索
目标是做一个独立博客文章聚合搜索引擎
要不要加参考链接?
要加!一定要加!
本来就是白嫖了别人的成功,去掉署名声称是原创就要下做了。
不加引用后果:
- 本来加个署名可以和原作者成为朋友,不加就弄成了仇人!
- 别人不一定告你,告你,输是一定的。但违反人家发布协议,弄臭你很容易
- 创作圈不大,很快大家都知道了
- 文章人家原创,后续很可能还会更新、修改,留个链接好跟过去看看有没有更新
- 等等其它
出错了,怎么办?
首先,学会搜索,最好实用Google,准确率比X毒高几个数量级。你遇到过的问题,90%以上其他人也遇到过,网上能找到现成的解决方案。
- 个人独立博客文章质量最高,stackoverflow质量也很非常高,CSDN基本就是垃圾场,普通搜索文章能有50%准确率就非常不错了,独立博客和stackoverflow之类的80%以上的准确率!
如何调试博客
需要一定的web开发知识才能做。
学会一些简单调试及基本html语言很容易,网上教程很多。
按
F12进入调试模式,元素,网络,控制台,是常用的页面。


网站被克隆镜像了怎么办?
在 analytic 后台(如umami)发现一些来源异常的域名,惊奇的发现你的网站被镜像了,下面是一些处理方法
JS 域名检测
代码如下**(插入到页面 head 或 foot)**:
<script>document.location.hostname!='blog.17lai.site'&&(location.href=new URL(location.href).hostname='blog.17lai.site')</script>展开上面脚本
<script>
if (document.location.hostname !== 'blog.17lai.site') {
// 获取当前页面的 URL 对象
var url = new URL(document.location.href);
// 修改 hostname 为目标值
url.hostname = 'blog.17lai.site';
// 跳转到修改后的 URL
window.location.href = url.href;
}
</script>如果对方不讲武德的通过反代把文件内的域名全部替换为了他的域名,就出现了死循环。
后来通过 String.fromCharCode 混淆的方式,把网址给混淆了,这样就防止了域名被修改。
代码如下:
<script>
if (document.location.hostname !== decryptHostname()) {
// 获取当前页面的 URL 对象
var url = new URL(document.location.href);
// 修改 hostname 为解密后的值
url.hostname = decryptHostname();
// 跳转到修改后的 URL
window.location.href = url.href;
}
function decryptHostname() {
return String.fromCharCode(
98, 108, 111, 103, 46, 49, 55, 108, 97, 105, 46, 115, 105, 116, 101
); // 对应 'blog.17lai.site' 的 ASCII 码
}
</script>允许多个域名来源
<script>
(function() {
var allowedHostnames = [
'blog.17lai.site',
'example.com',
'another-allowed-site.com'
];
var currentHostname = document.location.hostname;
if (!isHostnameAllowed(currentHostname)) {
// 获取当前页面的 URL 对象
var url = new URL(document.location.href);
// 修改 hostname 为允许的域名数组中的第一个
url.hostname = allowedHostnames[0];
// 跳转到修改后的 URL
window.location.href = url.href;
}
function isHostnameAllowed(hostname) {
return allowedHostnames.includes(hostname);
}
})();
</script>使用 fromCharCode 来完善
<script>
(function() {
var allowedHostnames = [
decryptHostname([98, 108, 111, 103, 46, 49, 55, 108, 97, 105, 46, 115, 105, 116, 101]), // 'blog.17lai.site'
decryptHostname([101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109]), // 'example.com'
decryptHostname([97, 110, 111, 116, 104, 101, 114, 45, 97, 108, 108, 111, 119, 101, 100, 45, 115, 105, 116, 101, 46, 99, 111, 109]) // 'another-allowed-site.com'
];
var currentHostname = document.location.hostname;
if (!isHostnameAllowed(currentHostname)) {
// 获取当前页面的 URL 对象
var url = new URL(document.location.href);
// 修改 hostname 为允许的域名数组中的第一个
url.hostname = allowedHostnames[0];
// 跳转到修改后的 URL
window.location.href = url.href;
}
function isHostnameAllowed(hostname) {
return allowedHostnames.includes(hostname);
}
function decryptHostname(charCodes) {
return String.fromCharCode.apply(null, charCodes);
}
})();
</script>使用弹窗提示跳转
<script>
(function() {
var allowedHostnames = [
decryptHostname([98, 108, 111, 103, 46, 49, 55, 108, 97, 105, 46, 115, 105, 116, 101]), // 'blog.17lai.site'
decryptHostname([101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109]), // 'example.com'
decryptHostname([97, 110, 116, 111, 104, 101, 114, 45, 97, 108, 108, 111, 119, 101, 100, 45, 115, 105, 116, 101, 46, 99, 111, 109]) // 'another-allowed-site.com'
];
var currentHostname = document.location.hostname;
if (!isHostnameAllowed(currentHostname)) {
showAlertAndRedirect();
}
function isHostnameAllowed(hostname) {
return allowedHostnames.includes(hostname);
}
function decryptHostname(charCodes) {
return String.fromCharCode.apply(null, charCodes);
}
function showAlertAndRedirect() {
var countdown = 5;
var alertDiv = document.createElement('div');
alertDiv.style.position = 'fixed';
alertDiv.style.width = '50%';
alertDiv.style.height = '50%';
alertDiv.style.top = '50%';
alertDiv.style.left = '50%';
alertDiv.style.transform = 'translate(-50%, -50%)';
alertDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
alertDiv.style.color = 'white';
alertDiv.style.display = 'flex';
alertDiv.style.justifyContent = 'center';
alertDiv.style.alignItems = 'center';
alertDiv.style.fontSize = '24px';
alertDiv.style.textAlign = 'center';
alertDiv.style.zIndex = '10000';
alertDiv.innerHTML = '<div>您访问的网页为镜像地址,<span id="countdown">' + countdown + '</span>秒后会跳转到源地址</div>';
document.body.appendChild(alertDiv);
var countdownInterval = setInterval(function() {
countdown--;
document.getElementById('countdown').innerText = countdown;
if (countdown <= 0) {
clearInterval(countdownInterval);
window.location.href = new URL(document.location.href).href.replace(currentHostname, allowedHostnames[0]);
}
}, 1000);
}
})();
</script>
通过hexo g -d部署时报Error: Spawn failed错误:
这是由于 git 本地记录的提交版本号与 github 上不一致导致的,通过
git reset --hard commitCode即可解决。
- 检查本地最近提交记录,获取最后一次提交记录的更新时间及标识,如
280a7fdd46fcfd7d34e652aec15523dcd247fac8
cd .deploy_git
cat .git/logs/HEAD- 获取 github pages 服务所关联分支的最近一次提交记录,获取更新时间及标识。地址一般为:
https://github.com/用户名/仓库名/commits/分支名,如https://github.com/lxl80/blog/commits/gh-pages - 如果发现提交最新的提交时间/标识不一致,通过以下命令即可解决:
git reset --hard f085038efdf79546c09641d37b2a2429c1ae8e60 #github上最新的提交标识hexo docker 本地访问内容没有更新
可能是node内部PM2模块的缓存机制导致的。pm2模块重启一下就可以了。
pm2 restart /hexo_run.js
fsevents报错
npm ERR! code EBADPLATFORM npm ERR! notsup Unsupported platform for
fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current:
{"os":"win32","arch":"x64"}) npm ERR! notsup Valid OS: darwin npm
ERR! notsup Valid Arch: any npm ERR! notsup Actual OS: win32 npm
ERR! notsup Actual Arch: x64解决方法:修改package.json
"optionalDependencies": {
"fsevents": "^2.3.2"
},关于百度无法爬取GitHub内容解决方案
- 使用coding搭建一个可以被百度爬取到的代码托管平台
- 使用vps搭建一个hexo 镜像访问,专门针对搜索引擎。
Tips: 博主用了第二种方法,2个域名,前端域名用dnspod,采用cloudflare parter cname接入。dnspos有选路选择,针对搜索引擎进入vps搭建的blog,对于其它线路到cloudflare parter 加速过的github pages cname。后端域名用cloudflare parter 以及免费版cloudflare管理。
既然百度无法爬取GitHub,那么我们只需要找个可以被百度爬取到的代码托管平台即可(并且还提供pages服务),基本只有国内的平台了:Gitee和Coding!Gitee自定义域名要花钱(九十多,都可以买服务器了),而Coding是可以免费自定义域名的。这里推介大家使用企业版的Coding,因为企业版的Coding仓库服务器是在香港的,而普通版的服务器是在新加坡。地理原因,理论上企业版的更快一些!
将博客同时部署到两个仓库:GitHub和Coding
deploy: - type: git repository: git@github.com:xxx/xxx.github.io.git branch: master - type: git repository: git@e.coding.net:xxx/xxx.git branch: master在域名那里,配置两个解析
线路类型作用:如果该值填“国内”,国内的IP就会去访问此项对应的
记录值地址同理,如果该值填写“国外”,国外的IP就会去访问“国外”对应的
记录值地址线路类型为百度或者国内,记录值为Coding仓库的地址线路类型为默认或者国外,记录值为GitHub仓库地址

这样来自
百度的spider就会去爬国内Coding的地址,而两个仓库的内容又都是一样的,如果请求IP来自国外,它又会去访问国外的GitHub,这样还有利于外国华侨和那些科学上网的用户访问,一石二鸟!(我真他妈天才!)如果你只用Coding仓库,那就不需要这么麻烦了
测试百度Spider能不能爬你的域名
在任意目录下执行以下命令(将“你的域名”换成你的域名)
curl -A "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" -o example.html 你的域名执行完命令,在该目录下会生成一个文件:
example.html,打开它,如果显示Moved Permanently,说明301,被重定向了Found,也就是爬到了如果打开是你的首页,说明爬取到的内容就是你的首页HTML内容。
如果显示:

说明域名是解析到GitHub的,403Forbidden访问禁止。
busuanzi不蒜子计数显示异常,一闪就没
原因可能有两种live2d看板娘和busuanzi不蒜子计数冲突
busuanzi不蒜子网络访问异常。
不在leancloud安全名单
不是正常域名访问
各种网络访问错误等等
近日安装了live2d看板娘插件,github项目地址,安装后却意外发现busuanzi不蒜子计数失效了,在页面中不显示,但强制刷新后出现,再刷新又消失。经排查,未发现问题,但事实是网站源码出现了变化。
正常时
<div id="busuanzi_container_page_pv" class="info-break-policy" style="display: inline;">
<i class="far fa-eye fa-fw"></i>阅读次数:
<span id="busuanzi_value_page_pv">433</span>异常时
<div id="busuanzi_container_page_pv" class="info-break-policy" style="display: none;">
<i class="far fa-eye fa-fw"></i>阅读次数:
<span id="busuanzi_value_page_pv">434</span>对比发现出现了多余的style="display: none;。
经过搜索主题源码,发现这个文件themes\matery\source\libs\others\busuanzi.pure.mini.js控制显示。
可以直接下载下面这个地址的js替换,来自个人blog的js busuanzi.pure.mini.js,直接下载这个js替换即可。下面源码经过了展开美化,原始文件是压缩去空格版本的。
注意: 这是一种破坏性修复,没有解决根本问题,临时修复方案。
var bszCaller, bszTag;
! function () {
var c, d, e, a = !1,
b = [];
ready = function (c) {
return a || "interactive" === document.readyState || "complete" === document.readyState ? c.call(document) :
b.push(function () {
return c.call(this)
}), this
}, d = function () {
for (var a = 0, c = b.length; c > a; a++) b[a].apply(document);
b = []
}, e = function () {
a || (a = !0, d.call(window), document.removeEventListener ? document.removeEventListener(
"DOMContentLoaded", e, !1) : document.attachEvent && (document.detachEvent("onreadystatechange",
e), window == window.top && (clearInterval(c), c = null)))
}, document.addEventListener ? document.addEventListener("DOMContentLoaded", e, !1) : document.attachEvent && (
document.attachEvent("onreadystatechange", function () {
/loaded|complete/.test(document.readyState) && e()
}), window == window.top && (c = setInterval(function () {
try {
a || document.documentElement.doScroll("left")
} catch (b) {
return
}
e()
}, 5)))
}(), bszCaller = {
fetch: function (a, b) {
var c = "BusuanziCallback_" + Math.floor(1099511627776 * Math.random());
window[c] = this.evalCall(b), a = a.replace("=BusuanziCallback", "=" + c), scriptTag = document.createElement(
"SCRIPT"), scriptTag.type = "text/javascript", scriptTag.defer = !0, scriptTag.src = a,
document.getElementsByTagName("HEAD")[0].appendChild(scriptTag)
},
evalCall: function (a) {
return function (b) {
ready(function () {
try {
a(b), scriptTag.parentElement.removeChild(scriptTag)
} catch (c) {
bszTag.hides()
}
})
}
}
}, bszCaller.fetch("//busuanzi.ibruce.info/busuanzi?jsonpCallback=BusuanziCallback", function (a) {
bszTag.texts(a), bszTag.shows()
}), bszTag = {
bszs: ["site_pv", "page_pv", "site_uv"],
texts: function (a) {
this.bszs.map(function (b) {
var c = document.getElementById("busuanzi_value_" + b);
c && (c.innerHTML = a[b])
})
},
hides: function () {
this.bszs.map(function (a) {
var b = document.getElementById("busuanzi_container_" + a);
b && (b.style.display = "")
})
},
shows: function () {
this.bszs.map(function (a) {
var b = document.getElementById("busuanzi_container_" + a);
b && (b.style.display = "inline")
})
}
};替换的人请操作其实就是把其中的b.style.display="none"中none去掉。
不蒜子 (busuanzi) 文章计数出错问题
出现这个原因,和 Chrome 85 版本 Referrer Policy 更改有关。什么是 Referrer,简单理解,就是请求 Web 服务器时,可以在 HTTP Request 的请求头 (header) 中加上当前页面的 URL,例如我们在浏览某个博客页面,需要加载一些图片,从服务器请求这些图片时,referrer 就是当前的博客页面 URL。从这里也可以看出,referrer 可能会暴露请求来源的某些信息或者隐私,有一定的隐私或安全风险。之前版本的 Chrome 浏览器,如果网站没有指定自己的 Referrer Policy,那么 Chrome 默认 policy 是 no-referrer-when-downgrade,在 Chrome 85 版本中,为了保护用户的隐私,默认的 Referrer Policy 则变成了 strict-origin-when-cross-origin。
- no-referrer-when-downgrade: 当两个网站的 http 协议安全等级相当,或者目的网站安全协议等级高于当前网站(HTTP –> HTTP, HTTPS –> HTTPS, HTTP –> HTTPS), referer 将会包含源网站的域名,路径,查询字符串;如果目的网站安全协议等级低于源网站 (HTTPS –> HTTP),将不会发送这些信息。
- strict-origin-when-cross-origin: 只有当做同一域名请求时 (源网址和目标网址是同一域名),才发送域名,路径和请求字符串,当两个网站安全协议相当时,发送源网站的域名(没有具体路径信息和查询字符串),如果目标网站安全协议等级低于源网站,不发送 header 信息。
不蒜子统计博客文章访问量就是通过 referer 来计算的,通过上面的分析,如果 Referrer Policy 是 strict-origin-when-cross-origin,不蒜子接收到的只有博客的域名,没有文章的具体路径,所以具体某个文章的 PV 统计会出现错误。
修复方法:
在主题文件夹下/layout/_partial/head.ejs中添加
<meta name="referrer" content="no-referrer-when-downgrade">如何多个域名映射同一个github pages
可能由于某种原因,换了一个域名,之前又有一些被搜索引擎收录,但是又不想让原来的链接失效,就需要让两个域名都映射到github pages中。本文介绍几种当前可能的方法。欢迎提出更多有效方法。
直接映射有什么问题?
你可能会想,两个域名都映射到github pages不就可以了?然而事实并非如此。首先当前github 的CNAME中只支持一个域名。因此CNAME文件中只能有一个域名,而如果在域名映射中将两个域名都映射到username.github.io,那么其中有一个会出现404错误。
前提
以下方法的前提是你已经明白如何为自己的github pages添加自定义域名。
需要注意什么?
- 需要给各大网站提交新的域名网址,重新被收录
- 域名变换前面网站的内容结构不能变,否则重定向也没有意义
- 当前单纯的域名没有办法进行备案
- 注意修改配置文件里的主域名为新的域名
- 由于更换了新的域名,导致原来leancloud统计的访问数据需要重新计算,也就是网站访问量被清零了,leancloud也需要更新域名
- 新的com域名可申请免费的SSL证书,保证https可访问,而不会提示不安全
- 301重定向会将旧地址的权重转义到新地址上
- 百度收录速度较慢
方法一:域名托管平台重定向
有的域名服务商提供重定向功能,以阿里云为例,在域名映射添加记录的时候,可以选择显性URL或隐性URL。但是自己在尝试这种方法的时候,会提示我URL备案异常。可能是由于这个时候已经用新的域名映射了博客地址,但是新的域名实际上是没有备案的。更无奈的是,目前貌似没有办法单独对域名进行备案。因此本人放弃了该方法。
如果你的博客也是部署在github上的,那么这种方法就不用尝试了,如果你的博客是部署在自己的服务器上的,那么网上都很多方法,这里就不介绍了。
方法二:部署两个仓库
我们注意到,除了github pages,还有coding.net可用。它与github类似。原来的域名映射到这个地址就可以了。而在部署hexo的时候,是可以添加两个仓库地址的:
deploy:
- type: git
repository: git@github.com:username/username.github.io.git
branch: master
- type: git
repository: git@git.coding.net:username/username.git
branch: coding-pages这两个仓库内容唯一的差别就是域名不一样,即CNAME中的记录值不一样。这样当访问两个不同的域名的时候,是访问不同的两个平台仓库。但是原来域名的权重不会转到新的域名中去。
方法三:新增项目重定向
假如你已经有username.github.io项目,新建一个名为blog(名字自定义)的项目,在项目的setting中,开启github pages服务,并且将旧的域名映射到username.github.io。
实际上,github pages并不是只能有一个,例如,你新创建的仓库访问地址为:username.github.io/blog。
由于旧的域名映射会导致404错误,那么在我们的新项目中增加一个404.html,在页面中进行跳转即可:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<script language="javascript">
var domain = "换成你自己的新域名";
var src = window.location.href;
var prtc = src.substring(0, src.indexOf(':'));
var target = src.substring(src.indexOf('/', src.indexOf(':') + 3));
window.location.href = prtc + "://" + domain + target;
location.href=prtc + "://" + domain + target;
</script>
<body>
<h1></h1>
</body>
</html>另外,需要利用google的地址更改功能,使得旧网址的权重往新网址转移。
找到search console中的地址更改工具(设置按钮中找到)。
除了增加404页面外,还需要增加CNAME文件,里面的内容是你原先的域名。
Vercel流量超标怎么办?
最近查看了一下Vercel流量使用情况,发现RSS订阅流量太大了,占了30GB流量!
免费Vercel账户每月只有100G免费流量,怎么办?
有两种解决方法:
- 使用 DNS 多线路分流,例如DNSPOD 多线路负载均衡
- 使用 Vercel 提供的 redirects 或者 rewrites功能,把rss访问重定向到其它部署方式,例如Cloudflare

由于个人blog也部署到Cloudflare上面了,https://cfblog.17lai.site,Cloudflare的流量目前看着没限制,如是,可以使用如下重定向方法
"redirects": [
{ "source": "/atom.xml", "destination": "https://cfblog.17lai.site/atom.xml" },
{ "source": "/rss.xml", "destination": "https://cfblog.17lai.site/rss.xml" }
]更多Vercel部署用法,戳vercel部署
其它主题
博主从这些主题学到了很多有用的技巧,大大丰富了个人定制
参考&感谢
编写整个系列教程花费时间和精力很长,如有遗漏,可以联系添加!
性能
- 图片 lazyload 的学问和在 Hexo 上的最佳实践
- “web资源加载优先级”原来能这么精准控制
- 使 Disqus 不再拖累性能和页面加载
- hexo个人网站优化探索
- lazy-load-iframes
- Hexo架构与优化–Hexo,分区解析,CDN,图像压缩等 hexo-theme-Claudia
- HTTP/3:过去,现在,还有未来
- 前端性能优化-网络篇 - webfem
- 插件 anzhiyu-c/hexo-theme-anzhiyu 安知鱼主题标签 Tag Plugins Tag Plugins Plus | Akilarの糖果屋
本教程还有其它五大部分,更多内容请见Hexo系列教程
系列教程
Hexo系列
[十万字图文教程]基于Hexo的matery主题搭建博客并深度优化完全一站式教程
- Hexo Docker环境与Hexo基础配置篇
- hexo博客自定义修改篇
- hexo博客网络优化篇
- hexo博客增强部署篇
- hexo博客个性定制篇
- hexo博客常见问题篇
- hexo博客博文撰写篇之完美笔记大攻略终极完全版
- Hexo Markdown以及各种插件功能测试
- markdown 各种其它语法插件,latex公式支持,mermaid图表,plant uml图表,URL卡片,bilibili卡片,github卡片,豆瓣卡片,插入音乐和视频,插入脑图,插入PDF,嵌入iframe
- 在 Hexo 博客中插入 ECharts 动态图表
- 使用nodeppt给hexo博客嵌入PPT演示
- GithubProfile美化与自动获取RSS文章教程
- Vercel部署高级用法教程
- webhook部署Hexo静态博客指南
- 在宝塔VPS上面采用docker部署waline全流程图解教程
- 自建Umami访问统计服务并统计静态博客UV/PV
笔记系列
- 完美笔记进化论
- hexo博客博文撰写篇之完美笔记大攻略终极完全版
- Joplin入门指南&实践方案
- 替代Evernote免费开源笔记Joplin-网盘同步笔记历史版本Markdown可视化
- Joplin 插件以及其Markdown语法。All in One!
- Joplin 插件使用推荐
- 为知笔记私有化Docker部署
Gitbook使用系列
- GitBook+GitLab撰写发布技术文档-Part1:GitBook篇
- GitBook+GitLab撰写发布技术文档-Part2:GitLab篇
- 自己动手制作电子书的最佳方式(支持PDF、ePub、mobi等格式)

