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 响应式系统问题,通过移除响应式引用可以有效解决。

相关推荐
苏琢玉5 分钟前
Go + Vue 打包成一个单二进制的后台系统,我做了个后台脚手架
vue.js·golang
终端鹿7 分钟前
Suspense 异步组件与懒加载实战
前端·vue.js
清风细雨_林木木10 分钟前
CSS 报错:css-semicolonexpected 解决方案
前端·css
Shaoxi Zhang10 分钟前
pm2运行项目实践记录(通过ecosystem.config.js配置并自动运行)
javascript·python·pycharm
Jinuss13 分钟前
源码分析之React中useRef解析
前端·javascript·react.js
cch891815 分钟前
css 样式说明,在页面布局开发中,样式表用于控制组件的尺寸、间距、边框及背景等视觉表现
前端·javascript·html
晨枫阳22 分钟前
前端项目部署与问题解决
javascript·vue.js·ecmascript
被AI抢饭碗的人27 分钟前
QT:基础与信号槽
前端·qt
熙街丶一人34 分钟前
css 图片未加载时默认高度,加载后随图片高度
前端·javascript·css
xiaoliuliu1234534 分钟前
Android Studio 2025 安装教程:详细步骤+自定义安装路径+SDK配置(附桌面快捷方式创建)
java·前端·数据库