SSF0SSF0
首页
前端
  • Node
  • Go
  • Python
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
  • 开源项目
Timeline
Github
标签
分类
首页
前端
  • Node
  • Go
  • Python
  • MySql
  • Bash
  • Git
  • Docker
  • VuePress
  • CI/CD
  • 服务器
  • 网站
  • 学习资料
  • 软件
  • 开源项目
Timeline
Github
标签
分类
  • HTML

    • html1
    • html2
  • CSS

    • Flex 布局常见问题与解决方案
  • JavaScript

    • 数据类型及引用问题
    • 处理 Blob 类型文件下载问题总结
    • localStorage 与 sessionStorage 区别
    • JavaScript 中的 script 标签问题详解
    • JavaScript 中的 this 指向问题详解
    • SessionStorage踩坑记录
    • 动态加载 JS 脚本方法对比
    • 浏览器页面关闭场景下的数据上报
    • HTTP流式接口详解与封装实践
    • JavaScript 中 this 绑定与解构:为什么 Pinia Store 可以解构,但 Class 实例方法不行?
  • es6

    • Promise
    • es6 模块导出方式全解析
    • reduce
    • forEach 中使用 async/await 的异步陷阱
  • Vue2

    • created VS mounted 发起异步请求
    • vue2-2
  • Vue3

    • Vite + Vue3 修改 element-plus 源码
    • Vue v-if 与 v-show
    • Vue3 ref 获取组件
    • Vue3 路由传参
    • 父子组件与组件里 Hooks 加载顺序
    • 第三方组件传入参数TS提示
    • Vue3 Props 在 Hooks 中的响应性处理
    • Vue Router 的两种历史模式及部署配置详解
    • 在Vue 3项目中顺利集成Tailwind CSS的完整指南
    • Vue Scoped CSS 深度解析
    • Vue 3 Teleport 组件完全指南:从原理到实战
    • Vant List 组件中双向绑定 loading 的注意事项
    • Vue 3 + Vite 中的路径别名与静态资源处理深度解析
    • 获取图片高度
    • 深入理解 Pinia:storeToRefs vs toRefs - 避开 TypeScript 类型推断的坑
  • webSocket

    • WebSocket 鉴权入门(实战版):握手票据 / 首帧鉴权 + 连接级 cid
  • Electron

    • 快速构建 electron + vue3 项目
  • TS

    • TS 泛型
    • 记录模板使用断言的问题
    • type 与 interface
  • WebPack

    • Webpack 介绍
  • Vite

    • Vite CLI 常见命令
    • vite 与 webpack 比较
    • Vite base 配置与 public 静态资源路径问题
  • 客户端数据库

    • IndexedDB 索引陷阱:字段重命名后索引返回旧数据
    • IndexedDB 事务陷阱与最佳实践:TransactionInactiveError 完全解析
  • UniApp

    • uni-app + Monorepo + 微信小程序热更新失效问题排查与结论总结
    • Monorepo + uni-app 开发中监听 / 热更新机制差异与解决方案
    • uni-app 运行与编译原理梳理
    • 为什么 uni-app 在 Monorepo 中必须做「环境区分」,而 Web 项目却不用?
  • 项目工程

    • 前端代码风格
    • Vue3 项目规范
    • npm 镜像问题
    • 包管理工具
    • 使用 engines 限制开发环境依赖
    • 打包与shell交互指定模式
    • Monorepo示例
    • PC 项目兼容 uni-app:基于 pnpm Monorepo 的工程化重构实践
    • Vue3 + Vite + pnpm Monorepo 迁移问题指南
    • 前端工程化:深入理解 npm 依赖管理机制
    • pnpm 修改依赖源码打包报错
    • 代码片段
  • 移动端

    • 浏览器跳转外部应用时请求状态码问题
  • 记录一下小程序
  • 控制浏览器正确保存网站账号密码的技巧
  • 信息泄露
  • 使用Gulp实现前端项目哈希化压缩打包
  • 前端缓存策略与文件哈希化的关联解析
  • 浏览器刷新方式与 Vue 生命周期、缓存行为解析
  • 浏览器消息铃声实现记录

浏览器刷新方式与 Vue 生命周期、缓存行为解析

1. 刷新方式概览

刷新方式浏览器行为资源获取Vue 应用表现
普通刷新(F5 / Cmd+R)重新加载当前文档优先命中内存缓存(Memory Cache)或磁盘缓存,若无则向服务器请求整个 SPA 重新初始化,所有模块与组件重新创建
清除缓存并强制刷新(Ctrl+F5 / Cmd+Shift+R)在重新加载前清空当次请求的缓存所有静态资源(HTML/JS/CSS)均重新下载与首次打开完全一致,只是耗时更长

注意:localStorage、IndexedDB、Cookies 等持久化数据不会因刷新方式不同而自动清除。

2. 对 Vue 组件生命周期的影响

  1. 首次进入路由
    • 组件首次创建:setup、onMounted 等钩子执行一次。
    • 模块级顶层代码也会运行(例如 const socket = new WebSocket(...))。
  2. SPA 内部路由切换
    • 默认情况下,离开路由时旧组件会销毁(触发 onUnmounted),再次进入会重新创建并触发 onMounted。
    • 如果使用 <keep-alive> 缓存路由视图,则只会触发 onDeactivated / onActivated,不会重新创建,顶层状态保持。
  3. 普通刷新
    • 等同于“销毁整个应用再创建”,所有生命周期重新跑一遍;因为资源命中缓存,所以加载速度更快。
  4. 强制刷新
    • 逻辑与普通刷新相同,但由于不使用缓存,静态资源需要重新下载,耗时更高。

结论:刷新方式影响的是“是否重新下载资源”,而组件是否“重新创建”取决于路由切换和 <keep-alive>。只要组件重新挂载,生命周期钩子就会再次触发。

3. 缓存层次与表现

  1. 静态资源缓存
    • 内存缓存(Memory Cache):同一会话内最快的缓存形式,普通刷新会直接复用。
    • 磁盘缓存 / HTTP 缓存:通过 ETag、Last-Modified、Cache-Control 控制;强制刷新会跳过这些缓存。
  2. 运行时状态
    • Vue 组件 / Pinia / Vuex 等响应式状态存于内存,刷新(无论是否强制)都会被清空。
  3. 本地数据存储
    • localStorage / sessionStorage / IndexedDB / Cookies:除非手动清理或代码调用 API,刷新不会擦除。
  4. Service Worker / PWA 缓存
    • 若有 Service Worker,可能会拦截请求并提供离线缓存,需要注意更新策略(skipWaiting、Clients.claim 等)。

4. WebSocket 与副作用的管理建议

  • WebSocket、轮询、定时器等“副作用”最好在全局单例或 store 中管理,确保:
    • 首次进入页面时初始化(onMounted 或 store 的 init 函数)。
    • 离开页面或组件销毁时根据需要关闭或保持连接。
    • 使用 <keep-alive> 时可在 onActivated / onDeactivated 中开始或暂停订阅。
  • 避免把初始化写在模块顶层,否则只会在模块首次解析时运行,后续普通刷新/热更新可能不会重复执行。

5. 常见问题与排查思路

  1. “刷新后接口不再调用”
    • 检查接口调用是否写在组件顶层而非生命周期钩子中。
    • 在开发模式下,Vite/HMR 可能复用模块实例,导致 <script setup> 顶层只在首次编译时执行;建议把数据请求放到 onMounted、onActivated 或 watch 里,确保每次组件挂载都会触发。
    • 若使用了缓存(localStorage/IndexedDB),确认刷新后是否仍旧从缓存短路了网络请求。
  2. “切换路由后状态丢失”
    • 确认有没有使用 <keep-alive>;若没有,离开路由即销毁组件,需要把状态提升到 store 或本地存储。
  3. “WebSocket 多次连接”
    • 确保连接逻辑只在应用初始化或特定组件挂载时执行一次;复用连接时需判断 readyState。
  4. “强制刷新后才能拿到最新代码”
    • 说明已有缓存未被更新,可通过版本号比对、service worker 更新策略或 Cache-Control: no-cache 等手段解决。

6. 实践建议

  • 初始化逻辑放在 onMounted / onBeforeMount,路由缓存场景使用 onActivated。
  • 将需要持久化的数据(如登录态、会话列表)写入 localStorage/IndexedDB,刷新后在 store 中统一恢复。
  • 考虑在入口文件监听 window.addEventListener("beforeunload") 做清理(如断开 WebSocket),避免旧连接残留。
  • 按需开启 <keep-alive>,并针对 activated/deactivated 实现资源暂停与恢复。
  • 通过浏览器 DevTools Network 面板观察资源来自 Memory Cache 还是网络,帮助定位刷新逻辑问题。
  • 如果需要复现 “刷新后 <script setup> 顶层请求未执行” 的场景,可在开发环境中:
    1. 将请求写在自定义 hook/module 顶层,并确保 hook 返回的是单例;
    2. 开启 Vite HMR,修改无关代码触发热替换;
    3. 此时“刷新”往往只是模块热替换/组件重挂载,顶层逻辑不会重新跑,即可观察到请求不再触发。

通过理解浏览器刷新机制与 Vue 生命周期的协作方式,就可以更准确地控制数据初始化、缓存恢复和副作用管理,避免“刷新不生效”或“状态丢失”等常见坑。调整代码结构,将初始化工作与生命周期钩子绑定,是保证 SPA 稳定性的关键。

最后更新时间:
贡献者: 何风顺
上一页
前端缓存策略与文件哈希化的关联解析
下一页
浏览器消息铃声实现记录