Vue 响应式无限递归问题总结

Vue 响应式无限递归问题总结

问题现象

在项目中遇到了 "Maximum call stack size exceeded"(最大调用堆栈大小超出)错误,导致页面无法正常加载。

问题原因

1. 响应式对象的问题

  • Vue 的 Vuex 状态管理中的数据是响应式对象
  • 响应式对象包含特殊的 getter 和 setter,用于监听数据变化
  • 当访问响应式对象的属性时,会触发这些 getter

2. 循环引用导致无限递归

  • 在路由过滤函数中,直接访问 Vuex 中的菜单数据
  • 当访问 menu.menuType 时,触发 Vue 的响应式 getter
  • 由于某些原因(可能是数据结构或 Vue 版本问题),这个 getter 存在循环引用
  • 导致无限递归调用,最终堆栈溢出

3. 为什么之前能运行

  • 可能之前的菜单数据结构不同
  • 或者 Vue 的响应式系统配置有变化
  • 或者某个依赖更新导致了这个问题

解决方案

核心思路:移除响应式引用

使用 JSON 序列化和反序列化,将响应式对象转换为普通 JavaScript 对象:

javascript 复制代码
// 修改前(有问题)
function filterRoutesByMenus(routes, menus) {
  const pageMenuNames = [];
  const extractPageMenuNames = (menuList) => {
    menuList.forEach((menu) => {
      if (menu.menuType === "2") {  // 访问响应式对象,触发无限递归
        pageMenuNames.push(menu.name);
      }
      if (Array.isArray(menu.children) && menu.children.length > 0) {
        extractPageMenuNames(menu.children);
      }
    });
  };
  extractPageMenuNames(menus);  // 直接使用响应式对象
  // ...
}

// 修改后(正确)
function filterRoutesByMenus(routes, menus) {
  const pageMenuNames = [];
  // 使用 JSON 序列化移除响应式引用
  const safeMenus = JSON.parse(JSON.stringify(menus || []));
  const extractPageMenuNames = (menuList) => {
    menuList.forEach((menu) => {
      if (menu.menuType === "2") {  // 访问普通对象,不会触发递归
        pageMenuNames.push(menu.name);
      }
      if (Array.isArray(menu.children) && menu.children.length > 0) {
        extractPageMenuNames(menu.children);
      }
    });
  };
  extractPageMenuNames(safeMenus);  // 使用普通对象
  // ...
}

为什么这样解决有效

1. JSON 序列化的作用

  • JSON.stringify() 将对象转换为 JSON 字符串,移除所有响应式特性
  • JSON.parse() 将 JSON 字符串转换回普通 JavaScript 对象
  • 得到的对象是纯净的,没有任何 Vue 的响应式特性

2. 避免响应式触发

  • 普通对象的属性访问不会触发 Vue 的 getter
  • 不会存在循环引用问题
  • 代码可以正常执行,不会陷入无限递归

3. 保持功能不变

  • 菜单过滤逻辑完全相同
  • 只是改变了数据源的类型
  • 不影响业务逻辑

总结

核心问题:Vue 响应式对象在特定情况下会触发无限递归

解决方案:使用 JSON 序列化将响应式对象转换为普通对象

关键点

  • 响应式对象包含特殊的 getter/setter
  • 直接访问可能触发无限递归
  • JSON 序列化可以移除响应式特性
  • 普通对象不会触发 Vue 的响应式系统

这是一个常见的 Vue 响应式系统问题,通过移除响应式引用可以有效解决。

相关推荐
万少2 小时前
小龙虾(openclaw),轻松玩转自动发帖
前端·人工智能·后端
Jagger_4 小时前
抱怨到躺床关灯的一次 DIY 记录
前端
陈随易7 小时前
前端大咖mizchi不满Rust、TypeScript却爱上MoonBit
前端·后端·程序员
whinc8 小时前
🚀 两年小程序开发,我把踩过的坑做成了开源 Skills
前端·微信小程序·ai编程
兆子龙9 小时前
ahooks useRequest 深度解析:一个 Hook 搞定所有请求
java·javascript
兆子龙9 小时前
React Suspense 从入门到实战:让异步加载更优雅
java·javascript
sure2829 小时前
React Native中创建自定义渐变色
前端·react native
KKKK10 小时前
SSE(Server-Sent Events)流式传输原理和XStream实践
前端·javascript
子兮曰10 小时前
Humanizer-zh 实战:把 AI 初稿改成“能发布”的技术文章
前端·javascript·后端