触发条件

  • 传统多页思路写进 Vue SPA:点击"计算"用 location.href = "?param=value" 做整页跳转,期望刷新后从 URL 读参数渲染结果。
  • 配套的全局变量在页面模板里写死为 null(如 var birthday = null;),而 JS 文件直接引用该全局变量初始化表单。
  • 页面没有任何代码调用 c.urlParam() / URLSearchParams 读取 location.search。
  • 结果展示区(.compute-result)甚至从未写进 HTML,只在 CSS 里定义了样式。

症状

  • 点击"计算"后页面闪一下(整页刷新),输入框清空,结果永远不显示。
  • 用户感知是"点击没反应",因为刷新回到的是同一张空白表单。
  • 手动访问 /page?param=value 也不会出结果,因为参数没人读。

容易误判的方向

  • 以为是日期选择器组件或 Element Plus 版本问题。
  • 以为 validate 回调没执行(实际执行了,只是里面只有一句跳转)。
  • 只改 JS 加回跳转逻辑,没发现刷新侧本来就缺"读参数"的代码,怎么跳都是死循环。
  • 检查了 Vue 挂载却漏了 HTML 里根本没有结果区块。

根因

原站(如 tool.6767.chat 系工具页)的服务端渲染时代逻辑残留:服务端模板会解析 query 参数并注入 var birthday = "..."。静态化/自托管后服务端注入消失,前端却仍按"提交→刷新→读参数"的旧契约工作,两侧契约都断了:

  1. location.href = "?birthday=..." 触发整页重新加载,Vue 应用销毁重建。
  2. 重建时 formData.birthday = birthday,而 birthday 写死 null。
  3. mounted() 里 if (!c.isNullOrEmpty(this.formData.birthday)) 不成立,结果区不渲染。

最终解决原则

单页计算器不需要"跳转传参"这层 router 机制,直接页内计算:

  1. compute() 在 validate 通过后直接调用计算方法更新响应式字段,不碰 location.href。
  2. 把"当前时间"直接用 new Date() 取(纯前端工具无需 API 或环境变量),日期差计算保持纯函数。
  3. 分享链接能力用 history.replaceState(null, "", "?birthday=...") 保留:不刷新页面,但 URL 同步参数,复制链接/收藏/刷新都能还原结果。
  4. mounted() 里用 c.urlParam("birthday") 读入站参数并自动计算,兼容旧分享链接。
  5. 结果区块用 v-cloak + v-if="结果!=null" 防闪现。
  6. setInterval 倒计时类副作用要存句柄并在 beforeUnmount 清理;重复计算时先 clearInterval 旧句柄再建新的,避免计时器叠加。

验证方法

  • node --check 逐个过改动的 JS 文件。
  • 无头浏览器(Edge --headless=new + CDP Runtime.evaluate)三组断言:

    1. 无参进入 → 填输入 → click() 计算按钮 → .compute-result 有文本、location.search 已被 replaceState 同步、无导航发生。
    2. 改值再点一次 → 结果更新、计时器不叠加。
    3. 直接访问带参 URL → 结果自动渲染、输入框回填。
  • grep -rn 'location.href = "?'" js/tools/ 确认无残留旧模式。

识别口诀

SPA 里见 location.href = "?..." 当提交,先问刷新侧谁在读参数;没人读就是断链,改成页内计算加 replaceState。

相关概念

  • SPA 单页应用路由 (SPA Routing)
  • History API 与 replaceState (History API and replaceState)

本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Frontend