项目进展与架构字典 (progress.md)
已完成
[x] 修复 VitePress 右侧文章大纲(Outline / Table of Contents)未列出多级标题(H2 ~ H6)的问题。
[x] 在
.vitepress/config.ts的themeConfig.outline中显式指定level: [2, 6],支持所有多级子标题在右侧目录正常渲染。[x] 执行项目完整编译构建验证(
npm run build),打包成功无报错。[x] 解答并分析 Vite/Rollup 打包体积阈值告警(
Some chunks are larger than 500 kB)的成因与优化策略。[x] 修复点击标签跳转时默认显示错误标签的问题(
Tags.vue初始化重构与 URL 状态同步)。[x] 修复进入页面时阅读次数跳动(先显示错误数据、后显示正确数据)的问题:
- 定位根因:原 [vercount.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/vercount.ts) 将全站与单页统计混在全局单一缓存键
visitorCountData中。进入新页面时,立即读取上一页甚至其他页面的page_pv进行回填展示,待接口请求完成后又覆盖为真实值,造成数值突变/跳动。 - 缓存分层隔离:拆分为全站缓存
vercount_site_stats与基于规范化路径哈希/路径键名的单页独立缓存vercount_page_stats。新页面加载时绝不复用跨页面的page_pv。 - SPA 切页平滑重置:在 [index.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/index.ts) 中增加
router.onBeforeRouteChange钩子,切页开始时调用resetCurrentPagePv隔离并精准预置目标页独立缓存(无缓存时为0,渲染为占位符--)。 - 异步竞态与多端回填防护:引入
AbortController与requestId机制,切页时自动中止上一次未完成的计数网络请求,防止慢响应覆盖新页面数据;将 DOM 回填由getElementById升级为querySelectorAll,确保宽屏侧边栏与窄屏顶部卡片同步准确更新。
- 定位根因:原 [vercount.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/vercount.ts) 将全站与单页统计混在全局单一缓存键
[x] 为 VitePress 配置与实现 WikiLinks / Obsidian 格式内部双链(
[[...]])解析支持:- 排查与选型:原包
markdown-it-wikilinks依赖已废弃且 Windows 平台编译失败;第三方@binyamin/markdown-it-wikilinks正则限制为 ASCII 字符(\w),无法匹配中文、章节锚点(#)及相对路径。 - 定制化插件实现:在 [
.vitepress/wikilinks.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/wikilinks.ts) 中实现针对 VitePress 深度定制的wikilinksPlugin,支持智能全站 Markdown 文档路由映射表构建,支持:[[页面名]]/[[页面名|显示文本]][[页面名#章节锚点]]/[[页面名#章节锚点|显示文本]][[#章节锚点]]/[[#章节锚点|显示文本]](当前页内跳转)[[../相对路径]]相对路径自动解构与规范化- 深度对齐 VitePress 原生
slugify中文及符号锚点生成算法。
- 主题排版美化:在 [
.vitepress/theme/custom.css](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/custom.css) 中为.wikilink增加虚线装饰与高亮悬浮动效。 - 全站构建验证:执行
npm run build打包验证成功,HTML、RSS、Feed 及 SPA 内部双链全部精准渲染。
- 排查与选型:原包
[x] 修复搜索框文字无法部分选中 / 鼠标选词强制全选的问题:
- 根因定位:VitePress 官方默认主题 [VPLocalSearchBox.vue](file:///D:/Users/A/Desktop/VitepressBlog/node_modules/vitepress/dist/client/theme-default/components/VPLocalSearchBox.vue) 在外层
<form class="search-bar">上绑定了@pointerup="onSearchBarClick"事件,该函数默认调用searchInput.value?.select()。用户在搜索输入框内用鼠标拖选部分文字或单击定位光标松开鼠标时,pointerup事件冒泡至父级form,强行触发全选覆盖了用户的局部选区与光标。 - 优雅无侵入式修复:新建 [
.vitepress/theme/searchFix.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/searchFix.ts)setupSearchFix(),在捕获阶段拦截针对#localsearch-input/.search-input的pointerup事件冒泡,阻断全选逻辑,完整保留浏览器原生局部文字拖选与精准光标落点;同时保留点击搜索框外部空白或快捷键唤醒时的全选体验。 - 主题挂载与全站验证:在 [
.vitepress/theme/index.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/index.ts) 中客户端挂载;执行npm run build打包验证无任何 SSR/构建报错。
- 根因定位:VitePress 官方默认主题 [VPLocalSearchBox.vue](file:///D:/Users/A/Desktop/VitepressBlog/node_modules/vitepress/dist/client/theme-default/components/VPLocalSearchBox.vue) 在外层
[x] 实现搜索结果左右分栏布局(Master-Detail)与右侧全文自由滚动预览:
- 解决痛点:原有单列弹窗内搜索结果由于高度受限(
max-height: 90px且overflow: hidden),关键词上下文极易截断看不全,且无法独立滚动浏览章节全文。 - 定制左右双栏搜索组件:实现 [
.vitepress/theme/components/VPLocalSearchBox.vue](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/components/VPLocalSearchBox.vue),在 [.vitepress/config.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/config.ts) 中通过 Viteresolve.alias完成无缝重定向。 - 左侧列表栏(Master):展示检索结果列表、匹配文章与小节层级面包屑、关键词匹配小标、当前激活项品牌高亮;支持键盘
↑/↓快速导航及鼠标 hover 即时同步。 - 右侧全文滚动预览栏(Detail):
- 同步渲染当前选中文章/章节的完整 HTML(带
.vp-doc标准排版、代码高亮、引用块与列表)。 - 集成
Mark.js实时对预览正文关键词整词高亮。 - 切换选中项时自动平滑滚动定位至首个命中关键词。
- 独立纵向滚动条,支持鼠标滚轮与拖拽自由上下滚动阅读全文。
- 顶部配备面包屑导航与「阅读整篇 ↵」一键跳转按钮。
- 同步渲染当前选中文章/章节的完整 HTML(带
- 响应式与移动端适配:在窄屏/手机端自动回退为紧凑直观的单栏列表,兼顾大屏多任务浏览效率与移动端便捷点选。
- 解决痛点:原有单列弹窗内搜索结果由于高度受限(
架构字典
| 文件路径 | 模块/说明 | 主要导出/函数及含义 |
|---|---|---|
.vitepress/config.ts | 站点主配置文件 | defineConfig: 配置站点元数据、Markdown 渲染规则(包含 LaTeX 公式与 WikiLinks 插件增强)、本地全文检索、主题导航栏与多级大纲目录、Vite 别名重定向(左右分栏搜索)等 |
.vitepress/theme/components/VPLocalSearchBox.vue | 左右分栏搜索弹窗组件 | 实现 Master-Detail 搜索界面:左侧搜索列表与按键导航,右侧全文独立自由滚动、关键词整词高亮及首个命中点自动聚焦定位 |
.vitepress/wikilinks.ts | 双向内链 Markdown-it 插件 | wikilinksPlugin(md, options): 解析 [[...]] 双链语法并转化为 VitePress 标准链接;slugify: 对齐 VitePress 原生中文锚点算法;getFileRouteMap: 扫描并构建全站 Markdown 路由映射 |
.vitepress/rss.ts | RSS/Feed 生成器 | createRssFeed(config): 提取文章信息并生成 rss.xml、feed.atom、feed.json 等订阅源文件 |
.vitepress/theme/index.ts | 自定义主题入口 | enhanceApp: 注册全局 Vue 组件、初始化数学公式一键复制、挂载搜索框选区修复以及 SPA 路由切换阅读统计更新(含切页前数据重置与切页后统计请求) |
.vitepress/theme/searchFix.ts | 搜索交互体验修复 | setupSearchFix(): 捕获并阻止搜索框输入节点的 pointerup 冒泡,修复鼠标局部划词选区被 input.select() 强制全选的 VitePress 原生缺陷 |
.vitepress/theme/serverUtils.ts | 服务端/构建时工具 | getPosts(pageSize): 递归扫描 Markdown 文章并解析 frontmatter 排序生成文章列表 |
.vitepress/theme/functions.ts | 数据聚合与处理工具 | initTags: 聚合全站文章标签与对应文章列表;initCategory: 聚合文章分类;useYearSort: 按年份归档文章 |
.vitepress/theme/date.ts | 日期格式化工具 | convertDate(date) / convertDateV2(date): 处理各种格式的发布日期并标准化展示 |
.vitepress/theme/vercount.ts | 访问量统计服务 | vercountState: 响应式统计对象;resetCurrentPagePv(url): 路由切页前独立预置当前页阅读量;updateVercount(url): 防竞态获取单页及站点访问量并回填;updateDomElements(data): 多端 DOM 元素批量更新 |
.vitepress/theme/mathCopy.ts | 数学公式复制增强 | setupMathCopy(): 为渲染的 LaTeX 行内/块级公式绑定悬浮一键复制原始公式代码 |
.vitepress/theme/components/Tags.vue | 标签聚合与筛选视图组件 | 响应式根据 URL ?tag= 动态筛选并渲染所属标签的文章列表,支持标签高亮与无刷新 URL 状态同步 |
函数字典目录
1. 配置文件与核心脚本
- [
.vitepress/config.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/config.ts)markdown.config(md): 自定义 markdown-it 扩展,支持 LaTeX 格式识别与包裹,并挂载wikilinksPlugin。search.options.miniSearch._splitIntoSections(file, html): 智能切分文章分段以供搜索。search.options.miniSearch.options.tokenize(str): 基于Intl.Segmenter的现代中文与英文混合分词函数。vite.resolve.alias: 别名重定向VPLocalSearchBox.vue至自定义左右分栏搜索组件。
- [
.vitepress/wikilinks.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/wikilinks.ts)slugify(str): 去除 Unicode 标点符号与特殊字符,生成与 VitePress 标题锚点完全一致的 URL Slug。getFileRouteMap(docsDir): 扫描当前博客全量 Markdown 文档,建立文档名、相对路径与 Web 路由映射表。wikilinksPlugin(md, options): Markdown-it 行内解析规则,精准提取目标文档、锚点与显示文本并转换为 Token 节点。
- [
.vitepress/rss.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/rss.ts)createRssFeed(config): 批量构建 RSS / Atom / JSON Feed 文件并写入磁盘。
2. 主题端逻辑与组件
- [
.vitepress/theme/components/VPLocalSearchBox.vue](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/components/VPLocalSearchBox.vue)fetchExcerpt(id): 异步加载目标 Markdown 的 JS chunk 编译产物。updatePreviewHighlight(scrollIntoMark): 执行 Mark.js 关键词高亮并平滑定位至首个命中词条。openResult(result): 路由跳转至目标文章/锚点并关闭搜索弹窗。onMouseMove(e)/onKeyStroke('ArrowUp' / 'ArrowDown'): 键盘上下键与鼠标移动双向同步选中状态。
- [
.vitepress/theme/searchFix.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/searchFix.ts)setupSearchFix(): 监听window的pointerup捕获阶段事件,针对.VPLocalSearchBox .search-input阻断冒泡,恢复自由选词。
- [
.vitepress/theme/functions.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/functions.ts)initTags(posts): 将文章数组按标签汇总为字典对象并按数量降序排序。initCategory(posts): 将文章数组按分类归类。useYearSort(post): 将文章数组按发布年份进行分组归档。
- [
.vitepress/theme/serverUtils.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/serverUtils.ts)getPosts(pageSize): 读取所有 Markdown 文档元数据并进行分页切分。
- [
.vitepress/theme/vercount.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/vercount.ts)getPathKey(rawUrl): 提取标准化的路由 pathname,作为独立缓存与单页标识。getCachedSiteData()/setCachedSiteData(): 全站 PV/UV 独立缓存读写。getCachedPagePv(pathKey)/setCachedPagePv(pathKey, pv): 单页路径级 PV 独立缓存读写与容量保护。resetCurrentPagePv(url): 路由切换发生时先行阻断旧数据残留,预置当前页有效缓存或占位。updateDomElements(data): 使用querySelectorAll批量更新页面中所有挂载的阅读量节点。updateVercount(url): 具备请求防抖/AbortController 取消机制的异步统计主入口。
- [
.vitepress/theme/mathCopy.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/mathCopy.ts)setupMathCopy(): 监听页面内数学公式并挂载点击复制 LaTeX 逻辑。
- [
.vitepress/theme/components/NewLayout.vue](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/components/NewLayout.vue)- 全局布局外壳,负责响应式分发文章元数据组件与底部版权。
- [
.vitepress/theme/components/ArticleMeta.vue](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/components/ArticleMeta.vue)- 文章标签、分类、阅读量、日期展示组件(支持顶部卡片与左下固定两种插槽位置)。
- [
.vitepress/theme/components/Tags.vue](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/components/Tags.vue)getTagFromUrl(): 安全解析当前 window URL 中的tag查询参数并完成解码。syncSelectedTag(): 匹配目标标签并同步激活状态(未传参时回退默认首个标签)。toggleTag(tag): 切换选中标签并更新浏览器历史记录 URL。
- [
.vitepress/theme/components/Page.vue](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/components/Page.vue)- 首页与分页列表文章渲染组件。
错误/交互日志
| 时间 | 现象/命令 | 原因分析 | 解决方案/修复措施 |
|---|---|---|---|
| 2026-08-31 | 搜索结果关键字过长或在正文深处看不全,单列浮层无法滚动查看全文 | 原生搜索框为单列固定高度卡片,摘要区域 max-height: 90px 且 overflow: hidden,长段落与多处命中词被截断且无法滚动 | 实现左右分栏(Master-Detail)搜索组件 [.vitepress/theme/components/VPLocalSearchBox.vue](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/components/VPLocalSearchBox.vue),左侧搜索结果列表、右侧全文独立滚动预览并支持关键词自动高亮聚焦 |
| 2026-08-31 | 多级标题未在右侧大纲展示 | .vitepress/config.ts 中 outline 配置仅配置了 label,未指定 level,VitePress 默认仅展示 h2 级别标题 | 在 themeConfig.outline 中显式添加 level: [2, 6] |
| 2026-08-31 | 构建提示 Some chunks are larger than 500 kB | Vite / Rollup 打包阈值默认限制单个 JS chunk 小于 500 kB,整站搜索索引或大库打包后触发警告(非报错) | 评估实际影响:若对静态博客无加载瓶颈,可调大 chunkSizeWarningLimit 或配置 manualChunks |
| 2026-08-31 | 点击标签进入页面未显示目标标签,显示为默认第一个标签 | Tags.vue 组件初始化时虽然读取了 URL 参数,但在后续无条件执行了 toggleTag(defaultDisplayTag),强行把选中标签重置为列表第一个 | 重构标签选中初始化逻辑 syncSelectedTag,优先使用 URL 参数并匹配有效标签;增加 URL 历史状态无刷新替换与高亮样式 |
| 2026-08-31 | 进入页面或切页时阅读次数跳动(先错后对) | 1. 缓存未按页面隔离,将上一页的阅读数直接赋给新页面; 2. SPA 切页时旧页面的阅读数未被及时重置; 3. 连续快速切页时旧网络请求晚于新请求返回造成数据倒灌覆盖。 | 1. 重构 [vercount.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/vercount.ts),实行按 pathname 分离的单页缓存; 2. 在 [index.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/index.ts) router.onBeforeRouteChange 挂载 resetCurrentPagePv,切页时即刻重置或只展示本页缓存;3. 加入 AbortController 与请求序号锁,中止过期请求。 |
| 2026-08-31 | npm install -D markdown-it-wikilinks 失败 | 旧版 markdown-it-wikilinks 依赖的 reurl 包含 Makefile 脚本,在 Windows 环境无法执行 make 报错 | 实现原生适配 VitePress 的定制 WikiLinks 插件 [.vitepress/wikilinks.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/wikilinks.ts),全面支持中文、锚点 Slug 及全站文档自动路由寻址 |
| 2026-08-31 | 搜索框文字鼠标拖选部分或点击定位时光标被强制全选 | VitePress 官方 [VPLocalSearchBox.vue](file:///D:/Users/A/Desktop/VitepressBlog/node_modules/vitepress/dist/client/theme-default/components/VPLocalSearchBox.vue) 在 <form class="search-bar"> 上绑定 @pointerup 执行 searchInput.select(),导致鼠标选词/松开时冒泡触发全选 | 编写 [.vitepress/theme/searchFix.ts](file:///D:/Users/A/Desktop/VitepressBlog/.vitepress/theme/searchFix.ts),在捕获阶段拦截搜索框的 pointerup 冒泡,解除对局部选区的强制覆盖 |
关联映射
用户触发搜索 (Ctrl+K / / 或点击搜索图标) -> 打开 VPLocalSearchBox 左右分栏弹窗输入搜索词 -> MiniSearch 检索并异步加载相关文章组件 -> 提取全篇文章与小节完整 HTML左侧列表上下按键/鼠标移动切换项 -> selectedIndex 改变 -> 右侧预览区实时切换展示完整排版内容右侧全文预览更新 -> Mark.js 动态整词高亮 -> 自动平滑滚动定位至首个命中的 <mark> 标签用户使用鼠标滚轮/滚动条自由在右侧预览区全屏滚动阅读全文 -> 键盘 Enter 或点击「阅读整篇 ↵」一键导航至目标文章Markdown 文章书写 [[页面#锚点|文本]] -> wikilinks.ts 智能路由寻址与 slugify -> 生成标准 VitePress 内部链接 -> SPA 客户端极速跳转搜索框鼠标划词选中文本 / 单击定位光标 -> searchFix.ts 捕获阶段拦截 pointerup -> 阻止冒泡至父级 form.search-bar -> 避免触发 input.select() -> 恢复原生部分选中与光标定位