Bug3及其衍生问题的完整修复记录

bug3:当我进入到第二页分页后,点击导航中的Logo处的首页连接时,这个连接没有反应。但是我从其他的页面,比如文章详情页或分类列表页点击导航中的Logo处的首页连接时,它会立马进入或回到到首页。

问题:基于以上情况,请使用cot方式对附件中的"优化后的需求文档v1.3"进行分析,bug3的问题可能出在哪些文件上?然后告诉我问题的原因和对应的解决方法。
注意:暂时不要输出任何代码,仅做文字沟通。


修复记录 #4

序号: 4

问题概括: 从首页到第 2 分页(/?page=2)点击导航 Logo 回首页(/)无反应,但从文章详情页或分类列表页点击 Logo 可正常跳转。

问题类别: 前端路由 / SPA Router 状态同步

影响范围: 首页分页后的导航(/?page=2/

问题原因:

  • 技术根因: src/router.jsnavigate 方法在判断"是否需要导航"时,仅比较 URL 的 pathname 部分。当用户处于 /?page=2 时点击 Logo(href="/"),pathname 都是 /,路由器误判为"同一页面"而直接短路返回,不做任何处理。同时,src/pages/Home.js 的分页切换直接调用原生 history.pushState 并手动操作 DOM,完全绕过了 router.navigate(),导致 router.currentRoute 始终停留在初始值 /,路由器的状态与浏览器地址栏发生了"分叉"。
  • 大白话解释: 路由器是个"记性不太好"的管家。它只记"你在哪个房间"(pathname),不记"房间里的细节"(page=2)。当你从"客厅(带第2页标签)"点击回"客厅"时,它觉得"你已经在客厅了",就直接无视了你的请求。更糟的是,分页按钮偷偷换房间标签时没告诉管家,导致管家一直以为你在"客厅第1页"。

排查思路:

  1. 提取关键差异:只有"当前 pathname 与目标 pathname 相同、但 query string 不同"的场景出现问题(/?page=2/),而跨页面导航(/categories/)正常,推断路由拦截逻辑对 query string 支持不完整。
  2. 检查 src/router.jsnavigate 方法,发现短路判断 if (this.currentRoute === path) return;path 实际仅为 pathname,未包含 search。
  3. 检查 src/pages/Home.js 发现分页切换使用 history.pushState + renderHome() 手动操作 DOM,未走 router 的 navigate 方法,导致 router 内部状态始终为 /
  4. 检查 src/components/Header.js 确认 Logo 为普通 <a href="/">,无自定义拦截逻辑,排除 Header 问题。
  5. 提出方案 B:让 router 原生支持完整路径(pathname + search),resolve 分离 query 并传给页面 handler;Home.js 分页切换统一走 router.navigate

最佳解决方法:

  1. 修改 src/router.js
    • navigate 接收完整路径(pathname + search),currentRoute 存储完整路径;
    • resolve 使用 new URL() 分离 pathname 与 search,将 query 对象作为第二个参数传给 route.handler(params, query)
    • init()popstate 中传递完整路径。
  2. 修改 src/pages/Home.js
    • 导入 router
    • onPageChange 回调中使用 router.navigate(url.pathname + url.search) 替代原生 history.pushState
    • 删除 renderHome 辅助函数,避免与 router 渲染逻辑重复。
  3. 兼容性微调 src/pages/*.js(除 Home 外):所有页面组件函数签名增加 query = {} 默认参数。

验证状态: ✅ 用户已确认


修复记录 #5

序号: 5

问题概括: Bug 3 修复后,首页第 2 分页(/?page=2)显示的文章列表与第 1 页完全相同(均为 10 篇),且分页控件只有 Next → 没有 ← Last 返回按钮。

问题类别: 前端路由 / 参数传递 / DOM 渲染

影响范围: 首页分页功能(/?page=2

问题原因:

  • 技术根因: 表面原因是 src/router.jsresolve 方法调用异步页面组件时未加 await,导致返回的 Promise 对象被误判为"非 HTMLElement",DOM 挂载失败。但根本原因是 src/main.js 中注册路由 handler 时使用了无参函数 async () => { ... },完全丢弃了 router.js 传递的 paramsquery。当用户进入 /?page=2 时,Home() 被调用时未传入 { page: 2 },默认 page = 1,因此始终渲染第 1 页内容。同时 main.js 的 handler 内部手动操作 DOM(app.innerHTML = ''app.appendChild(content)),架空了 router.js 的统一渲染体系,使 router 的 this.container 设置形同虚设。
  • 大白话解释: 想象 router 是个"快递分拣员",它把包裹(query 参数)和地址(params)一起交给楼下门卫(main.js 的 handler)。但门卫是个"马大哈",拿到包裹后看都不看就扔进了垃圾桶,然后自己跑去找仓库管理员(Home 组件)说"老样子,来一份默认套餐"。同时门卫还抢了分拣员的活,自己把货搬上了货架(手动操作 DOM),导致分拣员站在一旁无事可做。结果就是:地址栏显示"第2页",但实际送上来的是"第1页"的货。

排查思路:

  1. 观察症状对称性:第 2 页内容错误 + 分页按钮状态错误,一致指向 Home 组件拿到的 query.page 不是 2
  2. 检查 router.js 的 query 解析链路:new URL()searchParamsObject.fromEntriesroute.handler(params, query),确认 query 解析和传递逻辑本身正确。
  3. 检查 router.jsresolve 方法发现 const result = route.handler(params, query) 缺少 await,异步 handler 返回 Promise 未被等待。修改 navigateresolveasync 并添加 await
  4. 修改后问题仍存在,在 router.jsHome.js 添加 console.log 验证,确认日志正常输出,代码已生效。
  5. 检查 src/main.js,发现路由 handler 签名是 async () => { ... },未接收 paramsquery;内部调用 await Home() 未传参;handler 内部手动操作 DOM 未 return 任何值。
  6. 推断根因:main.js 的 handler 丢弃了 router 传递的 query,且手动 DOM 操作架空了 router 的渲染体系。

最佳解决方法:

  1. 修改 src/router.js:将 navigateresolve 改为 async 函数,在调用 route.handler(params, query) 前添加 await,确保异步页面组件完全执行并返回 HTMLElement 后再挂载到容器。
  2. 修改 src/main.js
    • 所有路由 handler 改为接收 (params, query) 两个参数;
    • 将参数传给对应页面组件(如 await Home(params, query));
    • handler 直接 return 页面组件生成的 DOM 元素,不再内部手动操作 DOM(删除 const app = ...app.innerHTML = ''app.appendChild(content) 等代码);
    • router.jsresolve 方法统一负责 DOM 挂载。

验证状态: ✅ 用户已确认