VitePress 实现短链接分享(旧版)
WARNING
本文是已停用架构的历史记录,文中代码不再与当前仓库同步。当前实现见 短链接分享。
基本逻辑
短链接格式为 https://notes.linho.cc/s?q=xxxxxxxxxx,其中 ID 是文件路径去掉 .md 或 index.md 后 MD5 的前 10 位。构建时生成根目录 shortmap.json,浏览器打开 /s 后请求该文件、查找目标路径并完成跳转。
整个流程是:
- 分享组件根据当前页面路径计算 10 位十六进制 ID。
- VitePress
buildEnd钩子为所有页面生成 ID—路径映射表。 /shortUrl.md通过 rewrites 映射到/s。- 跳转组件使用 axios 请求
/shortmap.json,再调用 VitePress Router 跳转。
构建阶段:生成映射表
ts
import md5 from "blueimp-md5";
import fs from "fs";
import type { SiteConfig } from "vitepress";
export default async function mapShortUrl(siteConfig: SiteConfig) {
const shortMap: Record<string, string> = {};
siteConfig.pages.forEach((path) => {
path = path.replace(/(index)?\.md$/, "");
shortMap[md5(path).slice(0, 10)] = path;
});
fs.writeFileSync(
`${siteConfig.outDir}/shortmap.json`,
JSON.stringify(shortMap),
);
}生成结果大致如下:
json
{
"9f5ef1467f": "C-C++ 相关/C++ Primer/",
"6c84242693": "C-C++ 相关/C++ Primer/第1章 开始",
"41efd5a73a": "C-C++ 相关/C++ Primer/零散的笔记素材"
}然后在 config.mts 的 buildEnd 中调用它:
ts
export default {
buildEnd: (siteConfig) => {
mapShortUrl(siteConfig);
},
};客户端:查表与跳转
旧跳转组件从 query string 中取出 10 位 ID,再请求映射表:
ts
import axios from "axios";
import { useRouter } from "vitepress";
import { onMounted } from "vue";
const router = useRouter();
onMounted(() => {
const id = window.location.search.match(/\?q=(.{10})$/)?.[1];
if (!id) return router.go("./404");
axios.get("/shortmap.json").then(
(res) =>
res.data[id] !== undefined
? router.go(`./${encodeURI(res.data[id])}`)
: router.go("./404"),
() => router.go("./404"),
);
});客户端:分享组件
生成分享 URL 时不需要查表,只要使用同一套路径归一化和 MD5 算法:
ts
watchEffect(() => {
const path = page.value.filePath.replace(/(index)?\.md$/, "");
if (encodeURI(path).length < 10)
link.value = `${baseUrl}/${encodeURI(path)}`;
else link.value = `${baseUrl}/s?q=${md5(path).slice(0, 10)}`;
});存在的问题
编码空间利用率低:10 位十六进制字符只使用 0-9a-f,共提供 编码空间。当前版本改用 8 位 Base36,在规避大小写文件名问题的同时提供约 编码空间。
映射表可能与页面产物缓存错配:shortmap.json 位于输出根目录且文件名不带内容哈希。在 GitHub Pages 的缓存窗口内,新页面可能与旧映射表同时出现。
额外的运行时请求:每次打开短链都必须先加载 VitePress 页面,然后再请求整张 JSON 映射表。为这一次简单请求引入 axios 也没有必要。
无法提供链接元数据:/s 的静态 HTML 不知道最终目标页面,大多数社交平台又不执行页面 JavaScript,因此只能抓取到统一的加载页或空元数据。
因此后来换用了新方案,详见新版文档 短链接分享。