npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vitepress-plugin-permalink

v1.2.1

Published

扫描 Markdown 文档,生成永久链接

Readme

vitepress-plugin-permalink

这是一个适用于 vitepress 的 Vite 插件,在 vitepress 启动后读取 Markdown 文档 frontmatterpermalink

✨ Feature

  • 🚀🚀 支持给 Markdown 文档设置唯一的访问 永久链接,不再因为 Markdown 文档路径移动而导致访问地址发生变化
  • 🚀 读取 Markdown 文档 frontmatterpermalink,挂载到 themeConfig.permalinks
  • 🚀 支持 locales 国际化,自动给 永久链接 添加语言前缀,不同语言的永久链接不会重复
  • 🚀 支持 rewrite 路由重写,最终得到的文档路径是 rewrite 路由重写后的路径
  • 🚀 永久链接 支持导航栏激活高亮

🕯️ Install

安装 vitepress-plugin-permalink 插件

# 推荐使用 pnpm
pnpm i vitepress-plugin-permalink
# or yarn
yarn add vitepress-plugin-permalink
# or npm
npm install vitepress-plugin-permalink

插件提供两种方式实现永久链接:

  1. Proxy 方式
  2. Rewrites 方式

两者只能二选一,如果都配置,则以 Rewrites 方式为主。

Proxy

Proxy 方式不会影响文件路径,而是在访问文件路径时,通过代理(拦截)转换 Permalink,因此既可以通过文件路径访问,也可以通过 Permalink 访问。

添加 vitepress-plugin-permalink 插件到 .vitepress/config.ts

import { defineConfig } from "vitepress";
import Permalink from "vitepress-plugin-permalink";

export default defineConfig({
  vite: {
    plugins: [Permalink(/* options */)],
  },
});

说明:该插件仅限项目启动时生效,已改动或新添加的 Markdown 需要重启项目才能生效。

插件默认忽略 ["node_modules", "dist", ".vitepress", "public"] 目录下的文件,且只扫描 Markdown 文档。

Rewrites

插件于 v1.2.0 提供 createRewrites 方法,用于创建 rewrites,通过该方式可以实现永久链接功能。

如果使用该方式,则 Proxy 相关功能都失效,如 usePermalink 函数。

Rewrites 方式在项目运行或者构建时,通过改变文件路径达到永久链接功能,你可以在构建的 dist 文件夹查看修改后的文件路径。

import { defineConfig } from "vitepress";
import { createRewrites } from "vitepress-plugin-permalink";

export default defineConfig({
  rewrites: createRewrites(),
});

注意:该方式会打乱原来的文件结构,因此侧边栏不再是基于文件路径配置,而是需要基于 frontmatter.permalink 属性配置。

🛠️ Options

| name | description | type | default | | ---------- | ------------------------------------- | ---------- | ------------------------------ | | ignoreList | 忽略的文件/文件夹列表,支持正则表达式 | string[] | [] | | path | 指定扫描的根目录 | string | vitepresssrcDir 配置项 |

❗ Warning

插件的 usePermalink 函数使用了 router.onBeforeRouteChangerouter.onAfterRouteChange 回调方法。

如果您也需要使用这些回调函数,请不要直接这样使用:

router.onAfterRouteChange = (href: string) => {
  // 你的逻辑
};

onAfterRouteChange 是一个函数,您这样使用将会 覆盖 Teek 在该回调函数的逻辑,因此您需要这样使用:

// 获取可能已有的 onAfterRouteChange
const selfOnAfterRouteChange = router.onAfterRouteChange;

router.onAfterRouteChange = async (href: string) => {
  // 调用可能已有的 onAfterRouteChange
  await selfOnAfterRouteChange?.(href);

  // 调用自己的函数
  myFunction();
};

const myFunction = () => {
  /* */
};

onBeforeRouteChange 支持返回 false 来阻止路由跳转,因此请这样使用:

// 获取可能已有的 onBeforeRouteChange
const selfOnBeforeRouteChange = router.onBeforeRouteChange;

router.onBeforeRouteChange = async (href: string) => {
  // 调用已有的 onBeforeRouteChange
  const selfResult = await selfOnBeforeRouteChange?.(href);
  if (selfResult === false) return false;

  // 调用自己的函数
  myFunction();
};

const myFunction = () => {
  /* */
};

📖 Usage

usePermalink 函数

插件已经在 VitePress 的 layout-bottom 插槽引入 usePermalink 函数来初始化 permalinks 功能,如果因为 VitePress 升版等原因导致没有初始化 permalinks,则可以手动引入该函数进行初始化操作。

怎么验证 usePermalink 函数初始化是否生效?

随机访问一个文件路径,如果地址栏变为 permalink,则代表生效,如果地址栏为文件路径,则代表没有生效。

.vitepress/theme/index.ts 引入 usePermalink 函数来初始化 permalinks 功能:

import { h, defineComponent } from "vue";
import DefaultTheme from "vitepress/theme";
import usePermalink from "vitepress-plugin-permalink/src/usePermalink";

export default {
  extends: DefaultTheme,
  Layout: defineComponent({
    name: "LayoutProvider",
    setup() {
      // 开启监听 permalink
      usePermalink().startWatch();

      return h(DefaultTheme.Layout, null, {});
    },
  }),
};

假设项目的目录结构如下:

.
├─ docs                # 项目根目录
│  ├─ guide
│  │  └─ api.md

api.md 内容如下:

---
permalink: /guide-api
---
  • 访问 /guide/api.html 可以进入文档页面,这是 vitepress 的自带功能。如果文件路径发生改变,访问链接也随着改变
  • 访问 /guide-api 可以进入文档页面,这是插件的实现功能。不会随着文件路径变化而改变

永久链接是唯一的,如果出现两个一样的永久链接,则后面的永久链接覆盖前面的,但不影响 vitepress 自带访问路径。

如果永久链接不生效,代表 usePermalink().startWatch() 并没有被执行,请在注册 vitepress 或者任意主题前加载该函数,如何注册请看 (扩展默认主题 | VitePress)

router.state.permalinkPlugin

如果使用了 usePermalink 函数,则会在 router.state 中添加 permalinkPlugin: true,因此您可以根据这个来判断是否判断插件生效。

import { useRouter } from "vitepress";

const router = useRouter();

console.log(router.state.permalinkPlugin);

router.onAfterUrlLoad

插件提供了 router.onAfterUrlLoad 钩子,当浏览器链接发生变化后,触发该钩子,你可以在参数里接收当前地址栏最新的链接。

router.onAfterUrlLoad 钩子在 VitePress 所有的 router.onAfterXxx 之后执行,目的是获取本插件提供的永久链接。

import { useRouter } from "vitepress";

const router = useRouter();

router.onAfterUrlLoad = (href: string) => {
  console.log(href);
};

使用场景

在使用访问量插件如不蒜子时,您需要提供当前的链接来统计其访问量,那么您可以在该钩子拿到地址栏最终的链接,然后提供给访问量插件。

📘 TypeScript

export interface PermalinkOption {
  /**
   * 忽略的文件/文件夹列表,支持正则表达式
   *
   * @default []
   */
  ignoreList?: Array<RegExp | string>;
  /**
   * 文章所在的目录,基于 .vitepress 目录层级添加,开头不需要有 /
   * @default 'vitepress 的 srcDir 配置项'
   */
  path?: string;
}

export interface Permalink {
  /**
   * key 为文件相对路径,value 为永久链接
   */
  map: Record<string, string>;
  /**
   * key 为永久链接,value 为文件相对路径
   */
  inv: Record<string, string>;
}

🉑 License

MIT License © 2025 Teeker