惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Hacker News
The Hacker News
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
雷峰网
雷峰网
人人都是产品经理
人人都是产品经理
Recent Announcements
Recent Announcements
D
DataBreaches.Net
P
Proofpoint News Feed
V
Visual Studio Blog
J
Java Code Geeks
Recorded Future
Recorded Future
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
F
Full Disclosure
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
C
Cybersecurity and Infrastructure Security Agency CISA
V
Vulnerabilities – Threatpost
罗磊的独立博客
Jina AI
Jina AI
博客园 - 【当耐特】
C
CERT Recently Published Vulnerability Notes
G
GRAHAM CLULEY
Y
Y Combinator Blog
L
LangChain Blog
L
LINUX DO - 热门话题
宝玉的分享
宝玉的分享
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
H
Help Net Security
云风的 BLOG
云风的 BLOG
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园_首页
A
About on SuperTechFans
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Latest news
Latest news
T
Threatpost
T
Tenable Blog
有赞技术团队
有赞技术团队
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Stack Overflow Blog
Stack Overflow Blog
C
Cisco Blogs
C
Check Point Blog
T
Tor Project blog
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
美团技术团队
I
Intezer
S
Securelist
AWS News Blog
AWS News Blog

atpX

自建 S3 兼容对象存储服务 Garage 听风的歌 偶尔是深夜就好了 新玩具 AirPods Pro 3 公交车上的时间 从 WHOIS 到 RDAP 通过 WireGuard 访问 NFS 共享文件 自建音乐串流服务探索 我停止了探索 Fediverse 互联网背景噪音 我的数字生活降级 小城与确定性的墙 一只特立独行的猪 Fediverse 与社交 承认的勇气 mediaX - 轻量书影音记录管理工具 我的博客写作流程 网站新增 Misc 页面 擅长对线的鲍勃 在 Chroot 环境下使用 Rsync 同步 再见 JavaScript 当我玩博客时我在玩什么 为什么我的博客没有友链页面 小熊猫与大熊猫 (HDR 照片测试) 是时候为网站开启 HTTP/3 支持了吗 使用 AdGuard Home 搭建自用 DoH 服务 谈谈读书与消遣 2023 年终总结 西安两日游 从 AirPods「升级」到 EarPods Docker 搭建去中心化的微博客平台 Mastodon 聊聊 iPhone 15 Pro 使用感受 能否将 TOTP 二次验证存放在密码管理器里 认识史蒂夫·乔布斯 与“锤哥”克里斯·海姆斯沃斯一起养生 逃离爆炸的信息 你想活出怎样的人生 重温《龙珠》动画 为什么关闭评论 从域名注册商到 DNS 服务,找到自己的组合 如何提高用户网页阅读体验 个人博客的最终归宿是静态网站吗 我选择了放弃 jQuery 关于我的小破站折腾速度优化这件事 记一次家庭网络折腾 贴一些 SmokePing 记录 我的域名邮箱选择 不存在的语录 Typecho 评论验证插件 CaptchaPlus 谈谈写字这件事 新的网站 Logo 探索 曲线解决 Typecho 图片占位抖动问题 自建 vaultwarden / bitwarden_rs 密码管理器 使用 Plausible 自建网站流量统计分析工具 如何优雅地徒手剥开火腿肠 简单的集中隔离生活记录 新玩具 M2 MacBook Air 使用 acme.sh 申请 Google 公共证书 Docker 搭建去中心化的微博客平台 Misskey 搭建 Lsky Pro 兰空图床 折腾树莓派系统的一天 我的吉卜力之旅 使用 Uptime Kuma 自建服务器/网站在线率监控 Spartan Host Review - Best Hosting & Service Support Debian 下 Nginx 配合 Fail2Ban 减少恶意扫描和攻击 升级 Typecho 到最新开发版本 v1.2.0 Linux rm 命令详解 Debian 系统安装 Docker 教程 BandwagonHost - Reliable Blog and Business VPS Hosting 使用 Isso 为 Hugo/Hexo 等静态网站添加评论功能 推荐 5 款免费开源的网站流量分析统计工具 搭建 Shynet 网站流量统计分析工具 聊聊 DDoS 攻击那些事 Hugo 使用 Fancybox 实现图片灯箱/放大功能 从 Debian 10 升级到 Debian 11 教程 使用 Umami 自建网站流量统计分析工具 Linux 一次执行多个命令的 3 种方式 Typecho 修改永久链接后旧链接 301 跳转到新链接 从不同需求推荐几家稳定可靠的 VPS 服务商 Build EchoIP service with Docker 浅谈一下这些年折腾过的 VPS 单线复用解决一根网线同时 IPTV 和宽带问题 不完全吃灰的树莓派 开发一个自己的 Telegram Bot 使用 Nginx 实现 TCP 四层反向代理 画画真难 我读村上春树 使用 acme.sh 自动签发和更新证书 卡拉马佐夫式悲剧 安装 File Browser 轻量网盘工具 新玩具树莓派到手 Linux 定时自动备份数据到 OneDrive/Google Drive 记一次服务器崩溃 使用 rsync 同步文件 短信的消失 幻灭的艺术家 新玩具戴尔 S2721DGF 开箱 蝴蝶 用 Docker 整合 SeafilePro 搭建私人云盘 搭建自己的 kms 服务器
从 Typecho 迁移到 Hugo
ATP · 2023-03-06 · via atpX
typecho-to-hugo

在上一篇文章 个人博客的最终归宿是静态网站吗 决定从 Typecho 迁移到 Hugo 后,为了保持博客样式不变,从 hugo new theme 开始,从零写了一个主题,经过这一周来 50+ 小时的努力,终于能 rm -rf typecho 后将网站重新上线了。

总体来说,样式还原度 98%,功能还原度 90%,整个过程主要分为数据迁移和主题制作两大部分,本文将简单记录下实现过程。

首先是数据迁移。包括文章和评论的迁移,整个过程得益于插件的帮助“比较”顺利。

转移文章

由于一开始考虑得不够全面,导致转移文章上有点费时。

1. 导出文章

使用的是前端大佬怡红公子的文章导出插件。进入到 Tyepcho 目录插件文件夹:

cd /path/to/typecho/usr/plugins
git clone https://github.com/lizheming/typecho-export-hugo.git Export2Hugo

启用插件后,上方菜单栏-控制台-导出至Hugo,开始导出。但下载后无法解压,尝试修改 Action.php 里第 69-73 行,改为 tar 打包:

$filename = "hugo.".date('Y-m-d').".tar.gz";
$outputFile = $dir."/".$filename;
exec("cd $dir && tar -czf $outputFile content"); 

header("Content-Type:application/tar+gzip");

但仍然提示文件错误,最后在服务器临时文件目录下 /tmp/Export2Hugo 直接下载回了本地,可以正常解压。

这里友情提醒下,建议提前想好你的 Hugo 文章 Front Matter 中需要哪些参数,插件中只包含一些默认的基本参数,最好提前修改下插件再导出。比如我的文章中需要用到的参数较多,还包括 lastmod 最后修改日期,toc 是否目录显示,copyright 版权信息,images 头部图片等等,我一开始没想好,导致后面想到一个添加一次,“手动遍历”了好几次 😖。

同时插件中默认导出的日期和 Hugo 默认 ISO8601 日期格式不一致,缺失了时区信息,虽然并不影响并且可以通过设置解决,但我认为还是尽量按照默认规范来,避免后期各种麻烦。显然,这个小问题也让我又“手动遍历”了一次文章… 简单的修改 Action.php 里第 44 行:

$time = date('c', $content["created"]);

即可和 Hugo 默认 .Date 时间格式一致。

2. 导入文章

导入文章就很简单了,直接导出的 Markdown 文件复制到 Hugo 站点 Content 目录下自定义文件夹中即可。

转移评论

上一篇文章中提到过,这里选择的评论工具是 Artalk,Artalk 的文档介绍得非常详细。因此迁移过程比较丝滑,主要时间花在配置参数和重写评论样式上。

1. 导出评论

直接使用 Artalk 提供的 Typecho 评论导出插件,按照文档导出即可,获得到一份 Artrans 格式的文件。

2. 部署 Artalk

这里我选择使用 Docker Compose 进行部署,在合适的位置新建一个 artalk 文件夹用来存放配置文件和数据。

编写 artalk-go.yml 配置文件,这一步可以忽略直接在前端配置(推荐),但我比较习惯后端配置文件(可能会覆盖前端)的形式:

cd /path/to/artalk && mkdir data
wget -O data/artalk-go.yml https://raw.githubusercontent.com/ArtalkJS/Artalk/master/conf/artalk.example.zh-CN.yml

参照配置说明按照自己的实际需求修改好后创建一个 docker-compose.yaml 文件:

version: "3.5"
services:
  artalk:
    container_name: artalk
    image: artalk/artalk-go
    restart: always
    ports:
      - 8080:23366
    volumes:
      - ./data:/data

启动 Artalk:

docker compose up -d

接下来配置 Nginx 反代,解析到二级域名:

server_name artalk.example.com;
...
location / {
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://localhost:23366/;
}

或者直接反代到二级目录:

server_name www.example.com;
...
location ^~ /artalk/ {
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://localhost:8233/;
}

3. 导入评论

将 Artalk 置入 Hugo,可以参照文档

登录 https://artalk.example.com 或者 https://www.example.com/artalk 进入后台,在「控制中心」找到「迁移」选项,按提示输入站点名称和地址后导入即可。

至此,网站的数据转移大致就处理完成了。


接着是主题制作。因为完全可以把现有 Typecho 主题的 CSS 照搬过来,因此在样式上其实工程量并不大,舍得花时间就行。最重要的是提前构思好整个框架,搭建好每个模板的结构。

主要精力花在一些功能的实现上,整个过程中感受到 Hugo 社区还是很活跃的,但中文文档相对较少并且大多数都过时了,大概情况是用中文搜索可以解决 30% 的问题,用英文搜索可以解决 90% 的问题。

主要功能实现

如今的静态网站已经和我记忆中的手撸 HTML 不一样了,其实更准确的说是动态编写、静态生成,得益于 Hugo 丰富的功能,可以“轻松”的实现一些“伪动态”功能。

1. 解决图片占位抖动 CLS 问题

图片占位防止 CLS 抖动这个问题我在用 Typecho 的时候研究过,因此还是沿用以前 曲线解决 Typecho 图片占位抖动问题 这篇文章中的思路,将 Markdown 图片写为下面的形式:

![alt text](/images/atpx.com.png "1920/1080")

也不用在额外进行其它处理,直接通过在 Hugo 使用 Fancybox 实现图片灯箱/放大功能 中提到过的重写 Markdown 图片解释,顺便加入目前用的图片灯箱工具 PhotoSwipe,将 render-image.html 文件编辑以下内容:

<div class="img-wrapper" id="gallery--individual">
    {{ $caption := "" }}
    {{ with .Title }}
    {{ $caption = . | safeHTML }}
    {{ end }}
    <a href="{{ .Destination | safeURL }}" data-pswp-width="{{ index (split $caption "/") 0 }}" data-pswp-height="{{ index (split $caption "/") 1 }}">
        <img style="aspect-ratio: {{ $caption | safeCSS }}" src="{{ .Destination | safeURL }}" alt="{{ .Text }}" width="{{ index (split $caption "/") 0 }}" loading="lazy" />
    </a>
</div>

2. 随机文章和搜索功能

这两个功能一直是静态网站的痛点,只能依靠 JS 或者第三方工具实现,还是用以前用过的方法,通过检索一个包含网站所有文章的 JSON 文件来实现。

a. 编辑站点配置文件 config.toml ,添加以下内容用来输出一个 JSON 地图:

[outputs]
    home = ["HTML", "RSS", "JSON"]

b. 在主题 layouts 目录下新建一个 index.json 文件,加入以下内容:

[ {{- $i := 0 -}}
{{- range where .Site.RegularPages "Section" "blog" -}}
   {{- if not .Params.Draft -}}
      {{- if gt $i 0 }},{{ end -}}
      {"date":"{{ .Date.Unix }}", "url":"{{ .Permalink }}", "title":{{ .Title | jsonify  }}, "summary":{{ .Summary | plainify | jsonify }} }
      {{- $i = add $i 1 -}}
   {{- end -}}
{{- end -}} ]

上面的代码中只加入了日期,链接,标题和摘要数据,你可以根据自己的需求修改。当然,要不要加入全文内容 .Conent 对象是一个值得纠结的问题,加入可以让搜索更加全面精准,但如果文章量很多的话也会导致性能问题,同时也相当于给采集站和垃圾内容农场拱手送上自己的劳动成果虽然和等它们直接爬区别不大

c. 随机文章功能通过 JS 来实现,简单来说就是得到文章的数量,生成几个不重复的随机数然后取对应的值,具体可以参照以下代码:

/* 
* Get random posts
* Written by ATP on 2023-03-01
* Website: https://atpx.com 
 */
function randomPosts(n) {
    const addList = document.querySelector('.random-posts h2');
    let postList = [],
        postIdSet = [],
        postInfo,
        id;
    fetch('/index.json')
        .then(res => res.json())
        .then((postsJSON) => {
            let allPostLength = Object.keys(postsJSON).length;
            for ( let i = 0; i < n; i++) {
                id = Math.floor(Math.random()*allPostLength);
                if (postIdSet.indexOf(id) == -1){
                    postIdSet.push(id);
                    postInfo = '<li><a href="' + postsJSON[id].url + '">' + postsJSON[id].title + '</li>';
                    postList.push(postInfo);
                } else {
                    i--;
                }
            }
            addList.insertAdjacentHTML('afterend', postList.join(''));
        }).catch(err => console.error(err)
    );
}

在需要展示随机文章的地方调用,例如随机展示 5 篇文章:

<div class="random-posts">
    <h2>Random Posts</h2>
    <script>randomPosts(5)</script>
</div>

我这里只展示了标题,你可以在本文底部看到效果。

搜索功能和上面的思路一致,并且官方也提供了很多更好的方法,迫于篇幅,这里不再赘述。

3. 文章目录滚动高亮

这也是一个在前面的文章 我选择了放弃 jQuery 中解决过的问题,同时 Hugo 内置了文章目录功能,可以直接调用,因此只需要照搬并稍作修改即可。

a. 在 single.html 中合适的地方添加文章目录:

<nav class="post-toc">
    <h2>Table of Contents</h2>
    {{ .TableOfContents }}
</nav>

b. 修改 CSS 文件,将目录固定在页面右方并添加高亮样式:

.post-toc {
    position: fixed;
    top: 50px;
    right: 50px
}
.post-toc a.active {
    color: blue
    transition: all .25s ease-in-out
}

c. 添加 JS 函数,这里写的是当 <h2><h3> 滚动到页面上方 1/4 处则高亮显示:

/* 
* Highlight ToC on scrolling
* Written by ATP on 2023-03-05
* Website: https://atpx.com 
 */
const postTOC = document.querySelector('.post-toc'),
    headingObserver = new IntersectionObserver(headings => {
    headings.forEach(heading => {
        const id = heading.target.getAttribute('id');
        if (heading.isIntersecting) {					
            inactive();
            document.querySelector(`.post-toc a[href="#${id}"]`).classList.add('active');
        }
    });
}, {
    rootMargin: '0px 0px -75%'
});		
document.querySelectorAll('.post-content h2[id],h3[id]').forEach((heading) => {
    headingObserver.observe(heading);
});
    
function inactive() {
    document.querySelectorAll('.post-toc a').forEach((a) => {
        a.classList.remove('active');
    });
}

遇到的一些问题

这里也简单记录一些在写主题中遇到的问题,方便后面的朋友避免踩坑。

1. 代码块字体大小

Hugo 现在自带代码高亮功能,直接将样式生成到静态文件中,不用在前端 JS 修改,效率更高,于是直接抛弃 PrismJS,使用自带的功能。

但发现有代码块字体大小不一的问题,尝试修改代码高亮主题、!important 字体大小都没有用,让我一度认为是 Hugo 使用的 Chroma 的问题,最后一番探索后,在一个 issue 中才知道是存在于 iOS Safari 的一个远古问题,在代码块样式中添加以下 CSS 解决:

code,
pre {
    text-size-adjust: 100%;
    -ms-text-size-adjust: 100%;
    -moz-text-size-adjust: 100%;
    -webkit-text-size-adjust: 100%;
}
hugo-code-size-issue-on-ios
Hugo 代码块中字体在 iOS Safari 上大小不一致。

2. Markdown 渲染

Hugo 默认使用 Goldmark 作为 Markdown 解释器,但在处理 CJK 字符时会有一些问题,这里用使用删除线 ~~删除线~~ 举例,如果语法块头尾中出现了全角符号(例如: ~~删除线。~~尾巴)则会导致解释失败,需要在语法块后面的内容前加上空格(例如:~~删除线。~~ 尾巴)才能正常解释,但排版就不好看了。这个问题也好解决,花时间把所有文章改一遍,避免该情况。

整个过程中还遇到了很多问题,不过突然不想写了。


总的来说,这折腾一番迁移到 Hugo 上还是值得的,有种回归大自然的奇妙感觉,之后应该不会再折腾迁移到其它平台了?

甚至一眼看到了这个博客的终点,最后丢到某个「靠谱」的静态网站托管平台上不再维护,在漫长岁月中逐渐消失在互联网上。但在那之前,还有一段努力写字的时间。