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

推荐订阅源

S
Schneier on Security
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
D
DataBreaches.Net
F
Full Disclosure
腾讯CDC
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
Stack Overflow Blog
Stack Overflow Blog
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
SegmentFault 最新的问题
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园 - 聂微东
雷峰网
雷峰网
J
Java Code Geeks
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy International News Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
A
Arctic Wolf
Scott Helme
Scott Helme
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tor Project blog
博客园 - 三生石上(FineUI控件)
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
G
Google Developers Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
CERT Recently Published Vulnerability Notes
O
OpenAI News
Project Zero
Project Zero
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Application and Cybersecurity Blog
Application and Cybersecurity Blog
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
MongoDB | Blog
MongoDB | Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Microsoft Security Blog
Microsoft Security Blog
Cisco Talos Blog
Cisco Talos Blog
P
Palo Alto Networks Blog
Schneier on Security
Schneier on Security

心记|Mood

给 blog-v3 添加一个侧边栏音乐播放器模块 | 心记|Mood 给 blog-v3 新建一个博友圈页面:聚合友链最新文章 | 心记|Mood 组件样式示例 | 心记|Mood
从 Typecho 迁移到 blog-v3:一次把博客搬进 Nuxt Content 的完整记录 | 心记|Mood
MoodLog, admin@moodlog.cn · 2026-05-19 · via 心记|Mood

记录从 Typecho 迁移到 blog-v3 / Clarity 的完整过程,包括备份、文章导出、Markdown 转换、图片迁移、链接保持、评论处理、本地预览和 Vercel 部署。

写在前面

用了很久 Typecho 之后,我最终还是把博客迁移到了基于 Nuxt 4 和 Nuxt Content 的 blog-v3

Typecho 很轻、很稳,也足够适合个人博客。它的优势很明显:部署简单、后台好用、插件丰富、服务器要求低。但随着博客内容越来越多,我对博客系统的需求也慢慢变了:

  • 想把文章变成 Git 仓库里可管理的 Markdown 文件;
  • 想用 VS Code / Cursor / 任意编辑器写文章,而不是登录后台;
  • 想把主题、组件、页面、样式都放进同一个工程里维护;
  • 想接入现代前端生态,比如 Nuxt、Vue、Vercel、自动构建;
  • 想让博客部署更接近“提交即发布”的工作流。

于是就有了这次迁移。

这篇文章不是单纯的“我换了个主题”,而是一次从传统 PHP + 数据库博客,迁移到 Markdown + Git + Nuxt Content 静态/服务端渲染博客的完整记录。下面会尽量详细写清楚每一步,包括备份、导出、转换、图片处理、链接保持、评论迁移和部署上线。

迁移前后架构对比

先看一下迁移前后的差异。

Typecho 时代

Typecho 的典型结构大概是这样:

txt
Nginx / Apache
  └─ PHP
      └─ Typecho
          ├─ MySQL / SQLite 数据库
          ├─ usr/themes/主题
          ├─ usr/plugins/插件
          └─ usr/uploads/附件

文章、页面、评论、分类、标签基本都在数据库里。图片等附件在 usr/uploads 目录。主题和插件则在 usr/themesusr/plugins

这种架构的优点是成熟稳定,后台操作方便;缺点是内容和程序耦合较深,迁移、版本管理、批量修改都不如纯文本文件直观。

blog-v3 时代

迁移后的结构更接近现代前端博客:

txt
blog-v3
├─ content/
│  ├─ posts/
│  │  └─ 2026/
│  │     └─ typecho-to-blog-v3.md
│  └─ link.md
├─ app/
│  ├─ pages/
│  ├─ components/
│  └─ app.config.ts
├─ blog.config.ts
├─ nuxt.config.ts
└─ package.json

文章直接放在 content/posts/年份/ 下面,每篇文章就是一个 Markdown 文件,顶部用 Front matter 写标题、日期、分类、标签等元信息。

比如本文的文章头部就是这样:

yaml
---
title: 从 Typecho 迁移到 blog-v3:一次把博客搬进 Nuxt Content 的完整记录
description: 记录从 Typecho 迁移到 blog-v3 / Clarity 的完整过程,包括备份、文章导出、Markdown 转换、图片迁移、链接保持、评论处理、本地预览和 Vercel 部署。
date: 2026-05-19 12:45:00
updated: 2026-05-19 12:45:00
categories: [技术]
tags: [Typecho, Nuxt, Nuxt Content, 博客迁移, Vercel]
---

从此以后,文章就是代码仓库的一部分,可以用 Git 记录变更,也可以通过 Vercel 自动构建发布。

第一步:迁移前一定要完整备份

迁移博客之前,最重要的不是转换工具,而是备份。

建议至少备份三类东西:

  1. 数据库;
  2. 附件目录;
  3. Typecho 配置、主题和插件。

备份数据库

如果你使用的是 MySQL,可以在服务器上执行:

bash
mysqldump -u 数据库用户 -p 数据库名 > typecho-backup.sql

例如:

bash
mysqldump -u typecho -p typecho > typecho-2026-05-19.sql

如果你不确定数据库名,可以查看 Typecho 根目录下的 config.inc.php

php
$db = new Typecho_Db('Mysql', 'typecho_');
$db->addServer(array (
  'host' => 'localhost',
  'user' => 'typecho',
  'password' => 'password',
  'charset' => 'utf8mb4',
  'port' => '3306',
  'database' => 'typecho',
), Typecho_Db::READ | Typecho_Db::WRITE);

里面的 database 就是数据库名,prefix 通常是 typecho_

如果使用 SQLite,则一般只需要备份对应的 .db 文件。

备份附件目录

Typecho 的图片和附件通常在:

txt
usr/uploads/

可以打包:

bash
tar -czf typecho-uploads.tar.gz usr/uploads

也可以直接用 rsync 下载到本地:

bash
rsync -avz root@你的服务器:/网站目录/usr/uploads ./typecho-uploads

备份主题和插件

虽然这次迁移到 blog-v3 后,Typecho 主题和插件基本不会继续使用,但它们里面可能有一些自定义代码、统计脚本、友链配置、备案信息、导航链接等内容。

建议一起备份:

bash
tar -czf typecho-theme-plugin.tar.gz usr/themes usr/plugins config.inc.php

备份完成后,再开始动手迁移,这样即使中间转换失败,也随时可以回滚。

如果还没有 blog-v3,可以先克隆项目:

bash
git clone https://gh-proxy.com/https://github.com/L33Z22L11/blog-v3.git
cd blog-v3

安装依赖:

bash
pnpm install

本地启动:

bash
pnpm dev

默认情况下,文章内容放在:

txt
content/posts/年份/

例如:

txt
content/posts/2026/hello-world.md

blog-v3 使用 blog.config.ts 管理站点基础信息,比如站点标题、作者、邮箱、站点地址、分类、文章类型等。迁移前建议先把这些基础配置改好。

例如:

ts
const basicConfig = {
  title: '心记|Mood',
  subtitle: '繁华已尽,空散云烟',
  description: '繁华已尽,空散云烟',
  author: {
    name: 'MoodLog',
    avatar: 'https://q1.qlogo.cn/g?b=qq&nk=80295940&s=640',
    email: 'admin@moodlog.cn',
    homepage: 'https://blog.moodlog.cn/',
  },
  url: 'https://blog.moodlog.cn/',
  timeZone: 'Asia/Shanghai',
  defaultCategory: '未分类',
}

这里建议优先确认三项:

  • title:站点名称;
  • url:正式域名;
  • timeZone:时区,国内站点一般用 Asia/Shanghai

第三步:从 Typecho 导出文章

Typecho 文章主要存储在数据库的 contents 表中,分类和标签关系在 relationshipsmetas 等表中。

如果只是少量文章,可以手动复制。但如果文章比较多,建议写脚本批量导出。

方式一:通过数据库导出

先确认表名。Typecho 默认表前缀一般是 typecho_,文章表通常叫:

txt
typecho_contents

可以查询文章:

sql
SELECT cid, title, slug, created, modified, text, type, status
FROM typecho_contents
WHERE type = 'post' AND status = 'publish'
ORDER BY created DESC;

其中几个字段很关键:

字段说明
cid内容 ID
title文章标题
slug文章缩略名,常用于固定链接
created创建时间,Unix 时间戳
modified修改时间,Unix 时间戳
text正文内容
typepost 是文章,page 是独立页面
statuspublish 是已发布

如果要导出分类和标签,还需要关联 relationshipsmetas 表。

一个常见查询大概是:

sql
SELECT
  c.cid,
  c.title,
  c.slug,
  c.created,
  c.modified,
  c.text,
  GROUP_CONCAT(m.name) AS metas
FROM typecho_contents c
LEFT JOIN typecho_relationships r ON c.cid = r.cid
LEFT JOIN typecho_metas m ON r.mid = m.mid
WHERE c.type = 'post' AND c.status = 'publish'
GROUP BY c.cid
ORDER BY c.created DESC;

不过 Typecho 的 metas 同时包含分类和标签,如果要区分,可以加上 m.type

sql
SELECT
  c.cid,
  c.title,
  c.slug,
  c.created,
  c.modified,
  c.text,
  GROUP_CONCAT(CASE WHEN m.type = 'category' THEN m.name END) AS categories,
  GROUP_CONCAT(CASE WHEN m.type = 'tag' THEN m.name END) AS tags
FROM typecho_contents c
LEFT JOIN typecho_relationships r ON c.cid = r.cid
LEFT JOIN typecho_metas m ON r.mid = m.mid
WHERE c.type = 'post' AND c.status = 'publish'
GROUP BY c.cid
ORDER BY c.created DESC;

如果你不方便直接访问数据库,也可以通过 RSS 获取文章列表。

Typecho 默认 RSS 地址可能是:

txt
https://你的域名/feed/
https://你的域名/feed/rss/
https://你的域名/feed/atom/

RSS 的优点是简单、安全,不用碰数据库;缺点是可能只导出最近几十篇文章,且分类、标签、原始 Markdown 信息不一定完整。

因此,完整迁移更推荐数据库导出。

第四步:把 Typecho 内容转换成 Markdown

Typecho 文章正文的格式取决于你当时用的编辑器:

  • 如果原来就是 Markdown,迁移会非常轻松;
  • 如果是 HTML,需要转换成 Markdown;
  • 如果混合了短代码、插件语法、图片懒加载标签,就需要额外清洗。

blog-v3 文章格式

blog-v3 里一篇文章的基本格式如下:

md
---
title: 文章标题
description: 文章摘要
date: 2026-05-19 12:00:00
updated: 2026-05-19 12:00:00
categories: [技术]
tags: [Typecho, Nuxt]
---

## 正文标题

这里是正文内容。

需要注意:

  • date 建议使用 YYYY-MM-DD HH:mm:ss
  • updated 可以使用 Typecho 的修改时间;
  • categoriestags 使用数组;
  • 文件名建议使用英文、拼音或原来的 slug
  • 如果想保持旧链接,可以使用 permalink 字段。

例如:

yaml
permalink: /archives/typecho-to-blog-v3/

这样可以尽量减少搜索引擎和外部链接的损失。

一个简单的转换脚本思路

假设我们已经把 Typecho 数据库导出成 JSON,例如 typecho-posts.json

json
[
  {
    "title": "第一篇文章",
    "slug": "first-post",
    "created": 1710000000,
    "modified": 1710003600,
    "text": "## Hello\n\n正文内容",
    "categories": "技术",
    "tags": "Typecho,博客"
  }
]

可以写一个 Node.js 脚本转换:

js
import fs from 'node:fs'
import path from 'node:path'

function formatDate(timestamp) {
  const date = new Date(Number(timestamp) * 1000)
  const pad = n => String(n).padStart(2, '0')
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}

function safeFileName(name) {
  return String(name || '')
    .trim()
    .replace(/[\\/:*?"<>|]/g, '-')
    .replace(/\s+/g, '-')
    .toLowerCase()
}

const posts = JSON.parse(fs.readFileSync('typecho-posts.json', 'utf8'))

for (const post of posts) {
  const date = formatDate(post.created)
  const updated = formatDate(post.modified || post.created)
  const year = date.slice(0, 4)
  const dir = path.join('content', 'posts', year)
  fs.mkdirSync(dir, { recursive: true })

  const categories = String(post.categories || '未分类')
    .split(',')
    .map(s => s.trim())
    .filter(Boolean)

  const tags = String(post.tags || '')
    .split(',')
    .map(s => s.trim())
    .filter(Boolean)

  const filename = safeFileName(post.slug || post.title)

  const markdown = `---
title: ${JSON.stringify(post.title)}
description: ${JSON.stringify(String(post.text || '').replace(/<[^>]+>/g, '').slice(0, 120))}
date: ${date}
updated: ${updated}
categories: [${categories.join(', ')}]
tags: [${tags.join(', ')}]
permalink: /archives/${post.slug || post.cid}/
---

${post.text || ''}
`

  fs.writeFileSync(path.join(dir, `${filename}.md`), markdown, 'utf8')
}

这只是一个基础版本,实际迁移时还可以继续增强:

  • 自动把 HTML 转 Markdown;
  • 自动下载远程图片;
  • 自动替换旧附件路径;
  • 自动修复标题层级;
  • 自动移除 Typecho 插件短代码;
  • 自动生成 description

HTML 转 Markdown

如果 Typecho 文章里主要是 HTML,可以使用 turndown 转换:

bash
pnpm add -D turndown

示例:

js
import TurndownService from 'turndown'

const turndown = new TurndownService({
  headingStyle: 'atx',
  codeBlockStyle: 'fenced',
})

const markdownBody = turndown.turndown(htmlBody)

转换完后不要急着发布,最好抽样检查几篇文章,尤其是:

  • 代码块是否完整;
  • 图片是否正常;
  • 表格是否错乱;
  • 引用块是否丢失;
  • 链接是否被错误转义。

第五步:迁移图片和附件

文章迁移最容易踩坑的地方不是正文,而是图片。

Typecho 的图片链接常见形式有:

txt
/usr/uploads/2024/01/example.png
https://example.com/usr/uploads/2024/01/example.png

迁移到 blog-v3 后,有几种处理方式。

方案一:继续使用原图片地址

如果旧站点域名不变,或者你还保留原来的 /usr/uploads/ 路径,可以不改图片链接。

优点:

  • 最省事;
  • 不需要批量替换;
  • 老文章几乎不用动。

缺点:

  • 依赖旧服务器或旧路径;
  • 后续迁移 CDN 时还要再处理;
  • 如果旧目录删除,图片会全部失效。

方案二:把图片放到 public 目录

可以把 Typecho 的 usr/uploads 拷贝到 blog-v3 的 public/uploads

bash
mkdir -p public/uploads
cp -r typecho-uploads/* public/uploads/

然后把文章里的路径从:

md
![](/usr/uploads/2024/01/a.png)

替换成:

md
![](/uploads/2024/01/a.png)

可以用脚本批量替换:

js
import fs from 'node:fs'
import path from 'node:path'

function walk(dir) {
  return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
    const full = path.join(dir, entry.name)
    return entry.isDirectory() ? walk(full) : [full]
  })
}

for (const file of walk('content/posts')) {
  if (!file.endsWith('.md')) continue
  const old = fs.readFileSync(file, 'utf8')
  const next = old.replaceAll('/usr/uploads/', '/uploads/')
  if (next !== old) fs.writeFileSync(file, next, 'utf8')
}

方案三:使用对象存储或图床

如果图片很多,或者希望访问更快,可以把图片上传到对象存储/CDN,例如:

  • S3;
  • R2;
  • OSS;
  • COS;
  • 又拍云;
  • 自建静态资源域名。

然后把文章里的图片统一替换为 CDN 地址。

这种方案更适合长期维护,但第一次配置会稍微麻烦一些。

第六步:处理旧链接和 SEO

迁移博客时,最容易被忽略的是旧链接。

如果搜索引擎已经收录了原来的 Typecho 地址,例如:

txt
https://blog.example.com/archives/123/
https://blog.example.com/archives/typecho-slug/

迁移后如果直接变成:

txt
https://blog.example.com/posts/2026/typecho-slug

那么旧链接可能会 404。

blog-v3 的文章支持 permalink 字段,可以给文章指定自定义链接。

例如:

yaml
---
title: 老文章标题
date: 2024-01-01 12:00:00
permalink: /archives/old-slug/
---

这样访问旧路径时依然能打开文章。

迁移时建议尽量保留 Typecho 原来的 slug,并把旧链接写进 permalink

如果链接规则变了,就做重定向

如果你不想逐篇使用 permalink,也可以在部署平台或服务器层做 301 重定向。

比如在 Vercel 项目里可以通过 vercel.json 配置:

json
{
  "redirects": [
    {
      "source": "/archives/:slug/",
      "destination": "/posts/:slug",
      "permanent": true
    }
  ]
}

不过重定向规则是否适用,取决于你旧站和新站的 URL 是否能一一对应。对于不规则的旧链接,还是逐篇 permalink 更稳。

第七步:迁移分类和标签

Typecho 的分类和标签可以直接映射到 blog-v3 的 Front matter。

Typecho:

txt
分类:技术
标签:Typecho、Nuxt、博客

blog-v3:

yaml
categories: [技术]
tags: [Typecho, Nuxt, 博客]

同时,建议在 blog.config.ts 中配置常用分类及图标:

ts
article: {
  categories: {
    未分类: { icon: 'tabler:circle-dashed' },
    技术: { icon: 'tabler:mouse', color: '#33aaff' },
    开发: { icon: 'tabler:code', color: '#7777ff' },
    杂谈: { icon: 'tabler:message', color: '#33bbaa' },
    生活: { icon: 'tabler:leaf', color: '#ff7777' },
  },
}

迁移时我更推荐先收敛分类,再迁移文章。因为很多老博客写久了之后,会出现分类过细、标签重复的问题,比如:

  • 前端Frontend前端开发
  • Linuxlinux
  • 随笔杂谈日记

迁移正好是一次整理信息架构的机会。

第八步:处理独立页面、友链和导航

Typecho 里除了文章,还可能有独立页面:

  • 关于;
  • 友链;
  • 留言板;
  • 归档;
  • 项目页。

迁移时不要只导文章,也要检查 typecho_contents 表里 type = 'page' 的内容。

sql
SELECT cid, title, slug, created, modified, text
FROM typecho_contents
WHERE type = 'page' AND status = 'publish';

在 blog-v3 里,有些页面可以继续使用 Markdown,有些页面更适合做成 Vue 页面。

比如友链,如果只是简单文字,可以写在 Markdown;如果想要卡片、分组、头像、描述,则更适合放在配置文件里,再由页面组件渲染。

我现在的 friend links 就是通过配置维护,页面负责展示。这样好处是:

  • 结构更清晰;
  • 头像、链接、描述、订阅地址可以统一管理;
  • 后续还可以扩展成博友圈、RSS 聚合等功能。

第九步:评论系统怎么处理

评论迁移通常有三种选择。

方案一:不迁移历史评论

这是最省事的方式。

个人博客很多文章的评论并不是核心内容,如果历史评论量不大,可以只保留数据库备份,不迁移到新站。

优点是简单,缺点是老文章下的讨论记录会丢失在前台。

方案二:导出成静态备份页面

可以把老评论导出成 JSON、Markdown 或 HTML,作为存档保留。

比如在每篇文章底部加一个“历史评论存档”折叠块,或者单独做一个评论归档页面。

方案三:迁移到新评论系统

如果新站使用 Twikoo、Waline、Artalk 等评论系统,可以尝试把 Typecho 评论表转换成对应系统的数据格式。

Typecho 评论一般在:

txt
typecho_comments

常用字段包括:

字段说明
coid评论 ID
cid文章 ID
created评论时间
author评论者
mail邮箱
url网址
text评论内容
parent父评论 ID
status评论状态

如果评论量很大,建议先写脚本导出,再根据新评论系统的导入格式转换。

我个人更倾向于:重要评论做静态备份,新评论交给新的评论系统处理。这样迁移成本低,也不会影响新站结构。

第十步:本地检查

文章转换完成后,不要马上部署,先本地跑一遍。

安装依赖:

bash
pnpm install

启动开发环境:

bash
pnpm dev

然后重点检查:

  • 首页文章列表是否正常;
  • 文章详情页是否能打开;
  • 分类、标签是否正常;
  • 图片是否显示;
  • 代码块高亮是否正常;
  • 内链是否正常;
  • RSS / Atom 是否正常;
  • sitemap 是否正常;
  • 移动端布局是否正常。

正式构建前再执行:

bash
pnpm build

如果构建失败,通常从以下几个方向排查:

  1. Front matter 写法错误;
  2. Markdown 中存在未闭合的代码块;
  3. YAML 数组格式不合法;
  4. 图片链接或组件语法写错;
  5. 某些 HTML 标签没有闭合。

其中最常见的是 Front matter 出错。比如标题里有冒号时,最好加引号:

yaml
title: "从 Typecho 到 blog-v3:迁移记录"

第十一步:部署到 Vercel

blog-v3 很适合部署到 Vercel。

基本流程是:

  1. 把项目推送到 GitHub;
  2. Vercel 导入仓库;
  3. 设置构建命令;
  4. 绑定域名;
  5. 后续提交自动部署。

常见构建配置:

txt
Install Command: pnpm install
Build Command: pnpm build
Output Directory: .output/public 或由 Nuxt/Vercel 自动识别

如果使用 Vercel CLI,也可以本地部署:

bash
pnpm dlx vercel deploy --prod

绑定域名后,把 DNS 指向 Vercel。生效后访问:

txt
https://你的域名/

再检查:

  • 首页;
  • 文章页;
  • 分类页;
  • 标签页;
  • 友链页;
  • Atom 订阅;
  • sitemap;
  • 旧链接是否能访问。

第十二步:迁移后的清理工作

上线不是结束,迁移后还有一些收尾工作。

检查死链

可以用爬虫或在线工具检查站内链接是否 404。

重点关注:

  • 老文章内链;
  • 图片链接;
  • 旧附件;
  • 友链;
  • 文章引用的外部链接。

提交搜索引擎

如果域名没变,搜索引擎会逐步重新抓取。但仍然建议提交 sitemap。

常见地址:

txt
https://你的域名/sitemap.xml

保留旧站备份

即使新站已经稳定运行,也建议至少保留一份旧站备份:

  • 数据库 SQL;
  • usr/uploads
  • config.inc.php
  • 主题和插件;
  • 转换脚本。

最好放到本地硬盘和云端各一份。

我踩过的一些坑

1. Front matter 不是随便写的

Markdown 顶部的 YAML 对格式非常敏感。

错误示例:

yaml
title: 从 Typecho 到 blog-v3: 迁移记录

这里标题里有英文冒号,可能导致 YAML 解析异常。建议写成:

yaml
title: "从 Typecho 到 blog-v3: 迁移记录"

2. 标签数组不要混用奇怪格式

建议统一使用:

yaml
tags: [Typecho, Nuxt, 博客]

或者:

yaml
tags:
  - Typecho
  - Nuxt
  - 博客

不要一会儿字符串,一会儿数组,否则后续统计和页面展示容易出问题。

3. 图片路径一定要提前规划

图片到底放在 public/uploads,还是继续使用 CDN,最好迁移前就定下来。否则文章导入后再改,会非常麻烦。

4. 旧链接最好不要全部放弃

如果博客已经运行很久,老链接可能散落在搜索引擎、社交平台、别人的文章里。能通过 permalink 保留就尽量保留,不能保留也尽量做 301 重定向。

5. 不要一次性相信自动转换结果

无论是 HTML 转 Markdown,还是批量替换图片,都要抽样检查。尤其是包含代码块、表格、数学公式、嵌入视频的文章,最容易出问题。

推荐迁移流程总结

如果重新来一次,我会按这个顺序做:

  1. 完整备份 Typecho 数据库和附件;
  2. 搭建 blog-v3 本地环境;
  3. 配置 blog.config.ts
  4. 从数据库导出文章、页面、分类、标签;
  5. 将正文转换为 Markdown;
  6. 迁移图片到 public/uploads 或 CDN;
  7. 使用 permalink 保留旧链接;
  8. 抽样检查文章渲染效果;
  9. 执行 pnpm build
  10. 部署到 Vercel;
  11. 绑定域名;
  12. 检查 sitemap、RSS、死链和搜索引擎收录。

结语

从 Typecho 迁移到 blog-v3,不只是换了一个博客程序,更像是换了一种写作和维护方式。

过去是“登录后台,写文章,点发布”;现在是“本地写 Markdown,提交 Git,自动部署”。

Typecho 的优雅在于简单直接,blog-v3 的舒服在于自由可控。文章、主题、配置、页面都在仓库里,修改有记录,部署可回滚,长期维护也更安心。

如果你的博客还很轻量,只想要一个稳定后台,Typecho 依然很好;但如果你更喜欢 Markdown、Git、现代前端工作流,并且希望博客能慢慢变成一个可以持续打磨的个人站点,那么 blog-v3 值得一试。

迁移过程会有点折腾,但当你看到所有文章都整齐地躺在 content/posts 里,并且每一次提交都能自动发布时,会觉得这一步很值得。