Skip to content

VitePress 实现短链接分享

基本逻辑

  • 短链接的格式为 https://notes.linho.cc/s?q=xxxxxxxxxx,其中 q= 后面的参数是文件路径去掉 .md 或者 index.md 之后取 MD5 的前十位。
  • 在构建阶段,生成一个 JSON 文件(shortmap.json)放在打包后的根目录,其中记录哈希值与路径的对应关系。
  • 通过路由覆写,将 /shortUrl.md 映射到 /s,该 Markdown 文件内加载组件,请求 shortmap.json 获取对应路径,并完成跳转。
  • 在导航栏上注册分享组件,提供分享二维码和复制链接按钮。

构建阶段:生成哈希 - 路径对应表

生成哈希表的代码很简单:

ts
import md5 from "blueimp-md5";
import fs from "fs";
import type { SiteConfig } from "vitepress";

type ShortUrlMap = {
  [key: string]: string;
};

/** 生成短链接哈希表 */
export default async function mapShortUrl(siteConfig: SiteConfig) {
  const shortMap: ShortUrlMap = {};
  siteConfig.pages.forEach((path) => {
    path = path.replace(/(index)?\.md$/, "");
    shortMap[md5(path).slice(0, 10)] = path;
  });
  try {
    fs.writeFileSync(
      `${siteConfig.outDir}/shortmap.json`,
      JSON.stringify(shortMap)
    );
  } catch (err) {
    console.error("Create shortmap.json failed!", err);
  }
}

输出的 JSON 大概长这个样子:

json
{
  "9f5ef1467f": "C-C++ 相关/C++ Primer/",
  "6c84242693": "C-C++ 相关/C++ Primer/第1章 开始",
  "41efd5a73a": "C-C++ 相关/C++ Primer/零散的笔记素材"
  // ...
}

然后在 config.mts 里面的 buildEnd 钩子里面加上这个就好了:

.vitepress/config.mts
ts
import { defineConfig } from "vitepress";
import mapShortUrl from "path/to/mapShortUrl.ts";

export default defineConfig({
  // ...
  buildEnd: (siteConfig) => {
    mapShortUrl(siteConfig);
  },
});

客户端:查表与跳转

利用 axios 请求 shortmap.json,然后查询哈希值。如果查到了,调用 VitePress 提供的 useRouter().go 方法跳转。然后在 shortUrl.md 中引入即可。

vue
<script setup lang="ts">
import axios from "axios";
import { onMounted } from "vue";
import { useRouter } from "vitepress";
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`)
  );
});
</script>
md
---
layout: false
head:
  - - meta
    - name: robots
      content: noindex
---

<Loading />
<ClientOnly><Jumper /></ClientOnly>

<script lang="ts" setup>
import Jumper from '/.vitepress/shortUrl/jumper.vue'
import Loading from '/.vitepress/shortUrl/loading.vue'
</script>

其中 loading.vue 是显示加载动画的组件。

客户端:分享组件

在网页上生成短链接只需要取哈希即可,不需要查表。

ts
import { useData } from "vitepress";
const { page } = useData();
const baseUrl = "https://notes.linho.cc";
const link = ref("");
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)}`;
});

至于展示二维码之类的细节,建议直接 看代码

然后在 config.mts 中的 nav 中添加上该组件即可。

相关文档链接