1.window.location.href 和 router.push 跳转方式

1. 冷跳转 (window.location.href) 的数据存活状态

概念:类似于浏览器的"断电重启"或"物理刷新"。跳转后,浏览器会完全卸载当前页面的上下文,并重新加载新页面。

  • 被彻底清空的(无法存活)
    • 所有存在"运行内存(JS Heap)"里的东西。
    • JS 声明的普通变量(let, const, var)、局部闭包变量。
    • JS 模块闭包(Module Cache)及其内部变量 。例如写在 .vue 文件 <script> 顶层、export default {} 外部的变量:let count = 0; let timer = null;
    • 整个 window 对象会被彻底销毁并替换 (浏览器会给新页面赋予一个全新的、原生的 window,旧 window 及挂载在上面的所有自定义属性全部清空)。
    • 框架状态(如 Vue 组件实例的状态,以及 Vuex 和 Pinia 里面的所有数据)。
    • DOM 树及绑定的原生事件。
    • 未执行完的定时器(setTimeoutsetInterval)。
  • 存活下来的(不受跳页影响)
    • 写入"磁盘/硬盘"或由浏览器底层接管的持久化数据。
    • sessionStorage(只要页签不关就一直存在)。
    • localStorageCookie
    • 浏览器的 HTTP 静态资源缓存(强缓存/协商缓存)。
    • URL 上的 Query 参数。

2. 热跳转 (Vue router.push) 的数据存活状态

概念 :单页面应用(SPA)内的无刷新跳转。本质是利用 History API 拦截跳转,仅仅是更换了页面上渲染的组件,JavaScript 执行环境没有被销毁(window 对象也没有被销毁)。

  • 被销毁的(无法存活)
    • 旧页面的 Vue 组件实例本身(触发 beforeDestroy/unmounted 钩子)。
    • 该组件内部的 datacomputed 等局部状态。
    • 该组件对应的 DOM 节点及组件级事件。
  • 依然存在的(存活下来)
    • window 对象及其上的所有全局变量。
    • JS 模块闭包(Module Cache)及其内部变量 。即声明在 .vue.js 文件顶层、export default {} 外部的模块级变量(例如:let count = 0; let timer = null;)。由于模块在一生中只解析一次,这些变量会像全局单例一样永远驻留内存。
    • Vuex / Pinia 的全局状态树。
    • 未清理的异步任务 (如 setTimeout,跳页后依然会在后台倒计时并在新页面触发回调,极易引发 Bug)。

3. 为什么大型混合应用(如本项目)大量使用 window.location.href

在 Vue 项目中放弃丝滑的热跳转,转而使用暴力的冷跳转,主要基于以下架构和业务痛点:

  1. 移动端客户端拦截限制(最核心原因) : 在 iOS/Android 内嵌 H5 (Hybrid App) 的场景下,客户端需要拦截 H5 的跳转请求以注入原生功能(如拦截带特定参数的 URL 以唤起扫一扫、支付,或改变原生导航栏)。Vue 的 router.push 不会发出真实的 HTTP 跳转请求,导致原生客户端(WebView)无法感知、无法拦截,从而导致大量端侧交互功能瘫痪。
  2. 微前端与多工程物理隔离 : 庞大的业务系统往往被拆分成多个独立的 Vue 项目(如商城独立部署,个人中心独立部署)。系统间的跨域跳转超出了单一 Vue Router 的管辖范围,必须通过 window.location.href 跨系统跳跃。
  3. 微信 JS-SDK 签名与兼容性: 在微信 Webview 中,SPA 的无刷新路由变化极易导致 URL 签名错乱(尤其是 iOS 微信),进而导致分享、支付等 SDK 功能失效。
  4. 防范内存泄漏: 在重度电商长列表、多媒体场景下,长期的热跳转会导致内存积压。冷跳转相当于强制垃圾回收(GC),保障低端机型的稳定性。

4. 单页应用 (SPA) 中的网络请求与"幽灵回调"

router.push 热跳转模式下,如果旧页面发起了网络请求且未完成时发生了页面跳转,会产生以下现象:

  • 请求依然在发送 :网络请求(如 Axios/Fetch 发起的 XMLHttpRequest 或 Fetch 动作)是由浏览器底层 API 代理的,并不依附于 Vue 组件实例。因此,跳页后该请求在后台继续存在并执行,直到服务器返回结果。
  • 回调诈尸(幽灵回调) :当请求成功返回时,原先写在旧页面的 .then() 回调函数会继续执行
  • 潜在危害 :如果回调中存在对全局状态(Vuex)的修改,或者使用了已经被销毁的 this 实例,会导致状态意外篡改、报错甚至页面崩溃。
  • 解决方案 :在 SPA 开发规范中,必须在组件销毁钩子(unmounted / beforeDestroy)中,利用 AbortController 或框架提供的 CancelToken,手动取消尚未完成的网络请求。

5. 模块级变量(闭包)在冷热跳转中的差异案例

为了更直观地理解,我们来看一段日常开发的源代码,以及它被打包后的底层形态。

1. 日常开发的 Vue 源代码:

javascript 复制代码
<script>
// ==== 危险地带:写在 export default 外面的模块级变量 ====
let count = 0; 
let timer = null;

export default {
  data() {
    return {
      // ==== 安全地带:写在组件实例内部的变量 ====
      localCount: 0 
    }
  },
  mounted() {
    count++;
    this.localCount++;
    console.log('外部 count:', count, '内部 localCount:', this.localCount);
  }
}
</script>

2. 经过 Webpack/Vite 打包后的真实形态(底层的闭包机制):

javascript 复制代码
// Webpack 在底层会将上面的代码包裹在一个独立的模块工厂函数中
function 模块工厂_组件A(module, exports) {
    // 您的 count 和 timer 被关在这个模块的专属闭包里
    let count = 0; 
    let timer = null;
    
    // 导出您的 Vue 组件配置对象
    module.exports = {
        data() { return { localCount: 0 } },
        mounted() {
            count++;
            this.localCount++;
        }
    }
}
  • 背后的机制(Webpack Module Cache) : 浏览器执行时,Webpack/Vite 会维护一个模块缓存表(Module Cache) 。一个模块(即上述的模块工厂函数)在一生中只会被执行一次,随后其导出结果会被缓存在内存中。写在最外层的变量,实际上就是存在于这个模块工厂的闭包中,成为了事实上的"全局单例"。
  • 在热跳转 (router.push) 中 : 由于 window 和 JS 内存未被销毁,Webpack 的模块缓存表依然存在。当用户再次跳回该页面时,Webpack 查表发现它执行过了,于是不会重新执行 模块工厂代码。因此,闭包中的 counttimer依然存在并保持旧值。这极易导致计数器疯狂累加、或后台存在无数个未被清理的幽灵定时器(引发严重内存泄漏)。
  • 在冷跳转 (window.location.href) 中 : 整个页面"断电重启",Webpack 的模块缓存表被彻底撕毁。当新页面加载时,模块会被视为首次引入,重新执行闭包代码,counttimer 会被彻底销毁并重新初始化,回归最初干净的状态。
相关推荐
其实防守也摸鱼20 分钟前
Codex破局:前端组件秒级生成的技术文章大纲
开发语言·前端·人工智能·学习·安全·web安全
奥莱维23 分钟前
KNX酒店方案_KNX专用线与高端酒店技术逻辑
java·服务器·前端·数据库
qq_4523962338 分钟前
第二篇:《前端架构的“道”与“术”:架构设计原则与决策框架》
前端·架构
用户921080262861 小时前
AI 消息列表虚拟滚动:这是业务问题,还是组件能力边界?
前端
前端snow1 小时前
ai agent---mcp知识汇总
前端
计算机魔术师2 小时前
AI 写了一半代码,谁来背锅?Anthropic 的安全重构笔记
前端
明月_清风2 小时前
vLLM 深度实战:2026 年生产级 LLM 推理引擎完全指南
前端·后端·ai编程
风月说与山鬼2 小时前
七、uni-app页面与组件生命周期
前端·uni-app
绿岛之北2 小时前
Electron 安全第三章:URL 加载与 WebView
前端·electron
sunoo-2292 小时前
C 语言文件 IO 全攻略:从基础函数到实战踩坑(BMP 读取 + 词典查询)
linux·c语言·前端·笔记·vscode·学习