快捷菜单
常用功能一站直达
更多功能请点顶栏「快捷菜单」
[[toc]]
[toc]
那么,为什么要切换到rewrite呢?
使用原则

在 Teek@1.5.0-2025.9.23 版本上测试
自己的开源库《vitepress-theme-teek-one》 –(克隆自 作者Teeker的文档风开源库《vitepress-theme-teek-docs-template》)
2025年9月26日测试
默认安装的Teek是proxy模式,这里需要手动切换到rewrite模式。
1、导入
编辑docs\.vitepress\config.ts文件
1import { createRewrites } from "vitepress-theme-teek/config"; //配置rewrites
2、配置
编辑docs\.vitepress\config.ts文件
在export default defineConfig({}) 区域配置。
1//配置rewrites
2rewrites: createRewrites({
3 srcDir: 'docs',
4 }),
编辑docs\.vitepress\teekConfig.ts文件:
1 // 开启 permalink 插件
2 vitePlugins: {
3 permalink: true,
4 sidebar: true,
5
6
7 sidebarOption: {
8 // initItems: false, //这条命令注释后,才会让文档和目录的样式保持一致
9 collapsed: true, //打开侧边栏自动收缩功能
10 ignoreList: [/^_.*$/],
11 resolveRule: "rewrites",
12 checkRewritesPrefix: true,
13 ignoreIndexMd: true,
14 },
15
16 autoFrontmatter: true, // 自动生成 frontmatter
17 // permalinkOption: {
18 // notFoundDelayLoad: 1000, // 1秒后加载
19 // },
20
21 // 自动格式formatter插件 添加文章封面图
22 autoFrontmatterOption: {
23 // exclude: { title: true, date: true }, // 排除自动生成字段
24 transform: frontmatter => {
25 // 如果文件本身存在了 coverImg,则不生成
26 if (frontmatter.coverImg) return;
27
28 const list = CoverImgList;
29
30 const coverImg = list[Math.floor(Math.random() * list.length)];
31
32 const transformResult = { ...frontmatter, coverImg };
33
34 return Object.keys(transformResult).length ? transformResult : undefined;
35 },
36 },
37 },

NOTE
以上配置就将我们的Teek从proxy模式切换到了rewrite模式。
但是,如何把自己之前的老库全部迁移过来呢?(老库有成千上万的md文档时,我们的头都要炸了……此时,就可以用到如下autoformatter插件了,让脚本替我们完成。😜)
这里利用autoformatter插件来自动注入 带对应一级前缀的permalink。
📌1.安装插件
1pnpm add vitepress-plugin-auto-frontmatter📌2.配置代码
01.导入
编辑docs\.vitepress\config.ts文件
1import AutoFrontmatter, {FileInfo} from "vitepress-plugin-auto-frontmatter";
2import { useTransformByRules, type TransformRule } from "./theme/composables/useTransform";
02.配置
编辑docs\.vitepress\config.ts文件
1 plugins: [
2 // 自动注入一级前缀(rewrite模式)
3 AutoFrontmatter({
4 pattern: "**/*.md",
5 // exclude 指定的对象如果在 markdown frontmatter 存在,则忽略该文件。当 include 和 exclude 存在相同文件时,exclude 优先级高
6 //exclude: { coverImg: true},
7 recoverTransform: true, // false 只添加不存在的字段
8 // 返回一个新的 frontmatter 或只返回 undefined,如果返回 {},则清空 MD 文件本身存在的 frontmatter
9 transform: (frontMatter: Record<string, any>, fileInfo: FileInfo) => {
10
11 // 定义需要处理的所有规则(可扩展多个)
12 const rules: TransformRule[] = [
13 // { folderName: "95.Teek", prefix: "/teek" }, // 添加前缀
14 // { folderName: "10.Teek", prefix: "/teek" }, // 添加前缀
15 // { folderName: "20.工具资源/01.SSL证书", prefix: "/tool", removeLevel: 1 }, // 移除一层前缀后再添加前缀
16 // { folderName: "10.笔记专栏/99.博客搭建", prefix: "/note", clear: true }, // 清空 permalink,优先级最高
17 // { folderName: "20.文档", prefix: "/note", clear: true }, // 清空 permalink,优先级最高
18 // { folderName: "01.前端/01.vite/", prefix: "/testa/$uuid5/$uuid1/$uuid10/$uuid99", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
19 { folderName: "10.运维", prefix: "/linux/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
20 { folderName: "20.前端", prefix: "/qianduan/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
21 { folderName: "30.编程", prefix: "/code/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
22 { folderName: "35.黑客", prefix: "/hacker/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
23 { folderName: "40.专题", prefix: "/zhuanti/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
24 { folderName: "50.工具", prefix: "/tools/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
25 { folderName: "60.生活", prefix: "/life/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
26 { folderName: "70.精神小屋", prefix: "/love/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
27 { folderName: "80.娱乐", prefix: "/yule/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
28 { folderName: "85.兴趣", prefix: "/xingqu/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
29 { folderName: "90.关于", prefix: "/about/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
30 { folderName: "100.Teek", prefix: "/teek/$uuid5", removeLevel: 99}, // 清空前缀并且添加前缀使用随机数
31 ];
32 // 应用规则转换
33 return useTransformByRules(frontMatter, fileInfo, rules);
34
35 /* // 如果文件本身存在了 coverImg,则不生成
36 if (frontMatter.coverImg) return; // 随机获取 coverImg
37 const list = [...Wallpaper, ...BlogCover];
38 const coverImg = list[Math.floor(Math.random() * list.length)];
39 const transformResult = { ...frontMatter, coverImg };
40 console.log("transformResult", transformResult)
41 return Object.keys(transformResult).length
42 ? transformResult
43 : undefined;*/
44 },
45 }),
46
47 ],
03.创建docs\.vitepress\theme\composables\useTransform.ts文件
1import { FileInfo } from "vitepress-plugin-auto-frontmatter";
2
3// 定义规则类型
4export interface TransformRule {
5 folderName: string; // 匹配文件或文件夹名称
6 prefix: string; // 要添加的前缀
7 removeLevel?: number; // 可选:要移除的前缀层级(以 / 分割),可以填写一个很大的数,那么就会全部清空然后添加前缀,适合移除前缀并添加的场景
8 clear?: boolean; // 可选,是否清空 permalink,适合只想要清空使用。true=清空(即使填了prefix也会清空,clear优先级高),默认false
9}
10
11/**
12 * 获取路径按 / 分割后的第一个有效分组(忽略空字符串)
13 * @param path
14 */
15const getFirstPathSegment = (path: string): string => {
16 // 按 / 分割并过滤空字符串(处理开头/结尾的斜杠或连续斜杠)
17 const segments = path.split('/').filter(segment => segment.trim() !== '');
18 return segments.length > 0 ? segments[0] : '';
19};
20
21/**
22 * 生成指定长度的随机字符串(数字 + 小写字母)
23 * @param length 字符串长度(最大为10)
24 * @returns 指定长度的随机字符串
25 */
26const generateRandomString = (length: number): string => {
27 if (length <= 0) return '';
28 const maxLen = 10;
29 const actualLength = Math.min(length, maxLen); // 最大10位
30
31 const chars = '0123456789abcdefghijklmnopqrstuvwxyz';
32 const charsLength = chars.length;
33 let result = '';
34
35 for (let i = 0; i < actualLength; i++) {
36 result += chars[Math.floor(Math.random() * charsLength)];
37 }
38
39 return result;
40};
41
42/**
43 * 替换字符串中的 $UUID{n} 占位符
44 * 支持 $UUID2, $UUID5, $UUID10 等格式
45 * - n 超过 10 按 10 处理
46 * - 不区分大小写(可选)
47 * @param str 原始字符串
48 * @returns 替换后的字符串
49 * @example
50 * replaceUuidPlaceholder('/test/$UUID10') → '/test/a3k9m2x8p1'
51 * replaceUuidPlaceholder('/user/$UUID5/$UUID2') → '/user/abc12/de'
52 */
53const replaceUuidPlaceholder = (str: string): string => {
54 return str.replace(/\$UUID(\d+)/gi, (match, numStr) => {
55 const length = parseInt(numStr, 10);
56 return generateRandomString(length);
57 });
58};
59
60export const useTransformByRules = (frontMatter: Record<string, any>, fileInfo: FileInfo, rules: TransformRule[]) => {
61
62 // 转换函数:支持移除指定层级前缀后再添加新前缀,新增clear清空逻辑 + UUID占位符替换
63 for (const rule of rules) {
64 const { folderName, prefix, removeLevel, clear = false } = rule; // 解构时给clear默认值false
65
66 // 1. 检查文件路径是否匹配文件夹规则(精确匹配单个文件时,需完全一致)
67 if (!fileInfo.relativePath.startsWith(folderName)) {
68 continue;
69 }
70
71 // 处理日期:减去8小时抵消时区转换(原有逻辑不变)
72 if (frontMatter.date) {
73 const originalDate = new Date(frontMatter.date);
74 originalDate.setHours(originalDate.getHours() - 8);
75 frontMatter.date = originalDate;
76 }
77
78 // 2. 如果clear为true,直接清空permalink并返回(优先级最高)
79 if (clear) {
80 const newFrontMatter = { ...frontMatter, permalink: '' };
81 console.log(`匹配规则:${folderName}(clear=true)→ 清空permalink`);
82 return newFrontMatter;
83 }
84
85/* // 3. 非clear模式:检查permalink是否存在(原有逻辑不变)
86 if (!frontMatter.permalink) {
87 continue;
88 }*/
89
90 // 4. 新增!替换prefix中的$UUID/$UUID10占位符
91 let normalizedPrefix = replaceUuidPlaceholder(prefix);
92 // 原有:标准化前缀(确保以 / 开头)
93 normalizedPrefix = normalizedPrefix.startsWith('/') ? normalizedPrefix : `/${normalizedPrefix}`;
94 if (prefix === ""){
95 normalizedPrefix = ""
96 }
97
98 let originalPermalink = frontMatter.permalink;
99 if (originalPermalink === null || originalPermalink === undefined) {
100 originalPermalink = "";
101 }
102
103 // 5. 核心调整:按 / 分组,比较第一个前缀是否一致(原有逻辑不变)
104 // 获取目标前缀的第一个分组(如 "/test/12345" → "test")
105 const targetFirstSegment = getFirstPathSegment(normalizedPrefix);
106 // 获取当前 permalink 的第一个分组(如 "/old/path" → "old")
107 const currentFirstSegment = getFirstPathSegment(originalPermalink);
108
109 // 若第一个分组相同,说明已包含目标前缀,无需处理(原有逻辑不变)
110 if (currentFirstSegment === targetFirstSegment) {
111 continue;
112 }
113
114 // 7. 处理 permalink:先移除指定层级,再添加新前缀(原有逻辑不变)
115 if (removeLevel !== undefined && removeLevel > 0) {
116 // 分割permalink(处理空字符串和开头的 /)
117 const parts = originalPermalink.split('/').filter(part => part);
118 // 确保移除的层级不超过实际存在的层级(removeLevel=99时,会移除所有层级,只剩根路径)
119 const actualRemoveLevel = Math.min(removeLevel, parts.length);
120 // 移除前N个层级,再重新拼接
121 const remainingParts = parts.slice(actualRemoveLevel);
122 originalPermalink = remainingParts.length > 0
123 ? `/${remainingParts.join('/')}`
124 : ''; // 移除所有层级后,originalPermalink为 ""
125 }
126
127 // 8. 拼接新 permalink 并返回结果(原有逻辑不变,此时prefix已替换占位符)
128 const newPermalink = `${normalizedPrefix}${originalPermalink}`;
129 const newFrontMatter = { ...frontMatter, permalink: newPermalink };
130
131 console.log(`原permalink:${frontMatter.permalink} → 新permalink:${newPermalink}`);
132 return newFrontMatter;
133 }
134 // 没有匹配的规则,返回undefined(不修改数据)
135 return undefined;
136}📌3.配置一级前缀
警告
根据自己docs下的目录,填好上述代码,然后运行测试:

📌4.修改nav
编辑D:\vitepress-theme-teek-one-private\docs\.vitepress\ConfigHyde\Nav.ts文件:
1// nav导航栏配置
2import { TeekIcon, VdoingIcon, SSLIcon, BlogIcon } from "./icon/NavIcon";
3export const Nav = [
4 { text: "🏡首页", link: "/" },
5 {
6 text: `
7 <div style="display: flex; align-items: center; gap: 4px;">
8 <img src="/img/nav/teek.svg" alt="" style="width: 16px; height: 16px;">
9 <span>Teek</span>
10 </div>
11 `,
12 link: '/teek/teek',
13 },
14
15
16
17
18 // 笔记
19 {
20 text: '📚知识库',
21 items: [
22 {
23 text: `
24 <div style="display: flex; align-items: center; gap: 4px;">
25 <img src="/img/nav/linux.svg" alt="" style="width: 16px; height: 16px;">
26 <span>运维</span>
27 </div>
28 `,
29 link: '/linux/linux',
30 },
31 {
32 text: `
33 <div style="display: flex; align-items: center; gap: 4px;">
34 <img src="/img/nav/前端.svg" alt="" style="width: 16px; height: 16px;">
35 <span>前端</span>
36 </div>
37 `,
38 link: '/qianduan/qianduan',
39 },
40 {
41 text: `
42 <div style="display: flex; align-items: center; gap: 4px;">
43 <img src="/img/nav/编程.svg" alt="" style="width: 16px; height: 16px;">
44 <span>编程</span>
45 </div>
46 `,
47 link: '/code/code',
48 },
49 {
50 text: `
51 <div style="display: flex; align-items: center; gap: 4px;">
52 <img src="/img/nav/黑客.svg" alt="" style="width: 16px; height: 16px;">
53 <span>黑客</span>
54 </div>
55 `,
56 link: '/hacker/hacker',
57 },
58 ],
59 },
60
61 // 专题
62 {
63 text: '🛠️专题',
64 items: [
65 {
66 text: `
67 <div style="display: flex; align-items: center; gap: 4px;">
68 <img src="/img/nav/博客.svg" alt="" style="width: 16px; height: 16px;">
69 <span>博客搭建</span>
70 </div>
71 `,
72 link: '/zhuanti/blog',
73 },
74 {
75 text: `
76 <div style="display: flex; align-items: center; gap: 4px;">
77 <img src="/img/nav/前端demo.svg" alt="" style="width: 16px; height: 16px;">
78 <span>前端demo</span>
79 </div>
80 `,
81 link: '/zhuanti/qianduan-demo',
82 },
83 {
84 text: `
85 <div style="display: flex; align-items: center; gap: 4px;">
86 <img src="/img/nav/Git.svg" alt="" style="width: 16px; height: 16px;">
87 <span>Git</span>
88 </div>
89 `,
90 link: '/zhuanti/git',
91 },
92 {
93 text: `
94 <div style="display: flex; align-items: center; gap: 4px;">
95 <img src="/img/nav/面试.svg" alt="" style="width: 16px; height: 16px;">
96 <span>面试</span>
97 </div>
98 `,
99 link: '/zhuanti/mianshi',
100 },
101 {
102 text: `
103 <div style="display: flex; align-items: center; gap: 4px;">
104 <img src="/img/nav/NAS.svg" alt="" style="width: 16px; height: 16px;">
105 <span>NAS</span>
106 </div>
107 `,
108 link: '/zhuanti/NAS',
109 },
110 {
111 text: `
112 <div style="display: flex; align-items: center; gap: 4px;">
113 <img src="/img/nav/脚本.svg" alt="" style="width: 16px; height: 16px;">
114 <span>脚本</span>
115 </div>
116 `,
117 link: '/zhuanti/jiaoben',
118 },
119 {
120 text: `
121 <div style="display: flex; align-items: center; gap: 4px;">
122 <img src="/img/nav/工具.svg" alt="" style="width: 16px; height: 16px;">
123 <span>工具</span>
124 </div>
125 `,
126 link: '/tools/tools',
127 },
128 {
129 text: `
130 <div style="display: flex; align-items: center; gap: 4px;">
131 <img src="/img/nav/开源项目.svg" alt="" style="width: 16px; height: 16px;">
132 <span>开源项目</span>
133 </div>
134 `,
135 link: '/zhuanti/opensource',
136 },
137 ],
138 },
139
140
141
142
143
144
145
146 // 生活
147 {
148 text: '🏓生活',
149 items: [
150 {
151 // 分组标题1
152 text: '娱乐',
153 items: [
154 {
155 text: `
156 <div style="display: flex; align-items: center; gap: 4px;">
157 <img src="/img/nav/相册.svg" alt="" style="width: 16px; height: 16px;">
158 <span>相册</span>
159 </div>
160 `,
161 link: '/yule/photo',
162 },
163 {
164 text: `
165 <div style="display: flex; align-items: center; gap: 4px;">
166 <img src="/img/nav/电影.svg" alt="" style="width: 16px; height: 16px;">
167 <span>电影</span>
168 </div>
169 `,
170 link: '/yule/movie',
171 },
172 {
173 text: `
174 <div style="display: flex; align-items: center; gap: 4px;">
175 <img src="/img/nav/音乐.svg" alt="" style="width: 16px; height: 16px;">
176 <span>音乐</span>
177 </div>
178 `,
179 link: '/yule/music',
180 },
181 ],
182 },
183 {
184 // 分组标题2
185 text: '小屋',
186 items: [
187 {
188 text: `
189 <div style="display: flex; align-items: center; gap: 4px;">
190 <img src="/img/nav/精神小屋.svg" alt="" style="width: 16px; height: 16px;">
191 <span>精神小屋</span>
192 </div>
193 `,
194 link: '/love/love',
195 },
196 {
197 text: `
198 <div style="display: flex; align-items: center; gap: 4px;">
199 <img src="/img/nav/时间管理.svg" alt="" style="width: 16px; height: 16px;">
200 <span>时间管理</span>
201 </div>
202 `,
203 link: '/love/time-plan',
204 },
205 {
206 text: `
207 <div style="display: flex; align-items: center; gap: 4px;">
208 <img src="/img/nav/文案.svg" alt="" style="width: 16px; height: 16px;">
209 <span>情感文案</span>
210 </div>
211 `,
212 link: '/love/wenan',
213 },
214 // { text: "💖情侣空间", link: "https://fxj.onedayxyy.cn/" },
215 ],
216 },
217 ],
218 },
219
220
221 // 兴趣
222 {
223 text: '🎨兴趣',
224 items: [
225 {
226 text: `
227 <div style="display: flex; align-items: center; gap: 4px;">
228 <img src="/img/nav/旅行.svg" alt="" style="width: 16px; height: 16px;">
229 <span>旅行</span>
230 </div>
231 `,
232 link: '/xingqu/travel',
233 },
234 {
235 text: `
236 <div style="display: flex; align-items: center; gap: 4px;">
237 <img src="/img/nav/读书.svg" alt="" style="width: 16px; height: 16px;">
238 <span>读书</span>
239 </div>
240 `,
241 link: '/xingqu/reading',
242 },
243 ],
244 },
245
246
247 // 索引
248 {
249 text: '👏索引',
250 items: [
251 { text: '📃分类页', link: '/categories' },
252 { text: '🔖标签页', link: '/tags' },
253 {
254 text: `
255 <div style="display: flex; align-items: center; gap: 4px;">
256 <img src="/img/nav/归档.svg" alt="" style="width: 16px; height: 16px;">
257 <span>归档页</span>
258 </div>
259 `,
260 link: '/archives',
261 },
262 {
263 text: `
264 <div style="display: flex; align-items: center; gap: 4px;">
265 <img src="/img/nav/清单.svg" alt="" style="width: 16px; height: 16px;">
266 <span>清单页</span>
267 </div>
268 `,
269 link: '/articleOverview',
270 },
271 {
272 text: `
273 <div style="display: flex; align-items: center; gap: 4px;">
274 <img src="/img/nav/登录.svg" alt="" style="width: 16px; height: 16px;">
275 <span>登录页</span>
276 </div>
277 `,
278 link: '/login',
279 },
280 {
281 text: `
282 <div style="display: flex; align-items: center; gap: 4px;">
283 <img src="/img/nav/风险提示.svg" alt="" style="width: 16px; height: 16px;">
284 <span>风险链接提示页</span>
285 </div>
286 `,
287 link: '/risk-link?target=https://onedayxyy.cn/',
288 },
289 ],
290 },
291
292 // 关于
293 {
294 text: '🍷关于',
295 items: [
296 { text: '👋关于我', link: '/about/me' },
297 { text: '🎉关于本站', link: '/about/website' },
298 { text: '🌐网站导航', link: '/about/websites' },
299 { text: "👂留言区", link: "/about/liuyanqu" },
300 { text: "💡思考", link: "/about/thinking" },
301 {
302 text: `
303 <div style="display: flex; align-items: center; gap: 4px;">
304 <img src="/img/nav/时间轴.svg" alt="" style="width: 16px; height: 16px;">
305 <span>时间轴</span>
306 </div>
307 `,
308 link: 'https://one.onedayxyy.cn/',
309 },
310 {
311 text: `
312 <div style="display: flex; align-items: center; gap: 4px;">
313 <img src="/img/nav/网站统计.svg" alt="" style="width: 16px; height: 16px;">
314 <span>网站统计</span>
315 </div>
316 `,
317 link: 'https://umami.onedayxyy.cn/share/DzS4g85V8JkxsNRk/onedayxyy.cn',
318 },
319 {
320 text: `
321 <div style="display: flex; align-items: center; gap: 4px;">
322 <img src="/img/nav/站点监控.svg" alt="" style="width: 16px; height: 16px;">
323 <span>站点监控</span>
324 </div>
325 `,
326 link: 'https://status.onedayxyy.cn/status/monitor',
327 },
328
329 ],
330 },
331 ]以上配置完成后,验证。
2025年8月27日测试。
结束。
日历 · 精选 · 友链 · 更多
如果内容对你有帮助,欢迎请我喝杯咖啡 ☕



One的公众号
爱折腾博客的小白