一次表单数据复用引发的 Bug:理解 Vue 中的 data 为何是函数

在 Vue 项目开发中,我们经常需要为表单提供默认值。在一个 Vue3 + TypeScript 项目中,我就因为处理默认表单数据的方式不当,踩了一个典型的坑:表单数据意外复用,导致新增页面出现上一次的输入值。

这次经历让我更深刻地理解了 Vue 2 中 data 要求必须是函数返回对象的设计初衷,也让我意识到即使在 Vue 3 中,这类问题依然需要我们主动规避。


问题起因:导出默认表单数据对象

我在项目中封装了一个表单的默认数据,代码如下:

js 复制代码
// defaultFormData.ts
export const defaultFormData = {
  name: '',
  age: 0,
  gender: ''
};

在新增页面中,初始化表单数据时这样写:

js 复制代码
import { defaultFormData } from './defaultFormData';

const formData = reactive(defaultFormData);

刚开始一切正常,页面渲染、填写、提交都没问题。但问题出现在我点击"新增"跳转页面第二次创建数据的时候。


Bug 现象:表单初始化后带着上次填写的值

当我点击"新增"进入页面时,发现表单字段中竟然自动带上了上一次输入的值。

我明明没有使用缓存、没有从接口回填、也没有做本地存储,为什么页面初始化时就带上了旧数据?

这时我意识到:我是不是复用了某个不该复用的东西?


问题根因:引用共享导致状态污染

答案是肯定的。

当我写下:

js 复制代码
const formData = reactive(defaultFormData);

其实是将 defaultFormData 这个对象直接传入 reactive,而 reactive 本身是引用式响应。

也就是说:

  • 第一次新增页面中 formData 修改了数据,其实就直接修改了 defaultFormData 本身
  • 当我第二次使用 defaultFormData 初始化表单时,它已经不是最初的"默认值",而是"被污染"的旧数据。

这个问题本质上就是:共享了同一个引用对象,修改了一处,影响了所有引用它的地方。


解决方案:每次都生成新的初始对象

既然问题出在共享引用,那我们就需要确保每次都获得新的对象。

有几种解决方式:

✅ 推荐方式:导出一个返回新对象的函数

js 复制代码
// defaultFormData.ts
export function getDefaultFormData() {
  return {
    name: '',
    age: 0,
    gender: ''
  };
}

在页面中使用时:

js 复制代码
const formData = reactive(getDefaultFormData());

每次调用函数都会创建一个全新的对象,完全避免引用共享问题。

其他方式(不推荐,但了解一下)

  • 使用浅拷贝:reactive({ ...defaultFormData })
  • 使用深拷贝(如 lodash 的 cloneDeepJSON.parse(JSON.stringify(...))

虽然也能解决问题,但语义性、可维护性、性能都不如使用函数。


联想到 Vue 2 中的经典问题:为何 data 必须是函数?

这次让我突然明白了 Vue 2 中的这道"八股文"题背后的真实含义:

Vue 2 要求组件的 data 是函数,目的是为了保证每个组件实例返回一个新的数据对象,防止不同组件实例之间共享引用,导致数据污染。

通过这次真实的踩坑,我"理解"了这个规则背后的必要性。


总结:引用类型的默认值要特别小心

这次经历告诉我:在 Vue 项目中使用默认数据时,如果这个数据是引用类型(对象、数组),一定要小心共享引用的问题。

✅ 最安全的方式:导出一个函数,每次调用都返回新的对象

不仅避免了数据污染,也让代码的语义更清晰、逻辑更独立。

这也是我认为,Vue 中关于 data 函数化的设计,不是八股,而是真实开发中你迟早会理解的一条"黄金准则"。

相关推荐
鱼樱前端几秒前
重度Cursor用户 最强 Cursor Rules 和 Cursor 配置 mcp 以及最佳实践配置方式
前端
曼陀罗2 分钟前
Path<T> 、 keyof T 什么情况下用合适
前端
来自星星的猫教授3 分钟前
将 VSCode 的快捷键设置为与 IntelliJ IDEA 类似
vue.js·vscode
锈儿海老师7 分钟前
AST 工具大PK!Biome 的 GritQL 插件 vs. ast-grep,谁是你的菜?
前端·javascript·eslint
飞龙AI9 分钟前
鸿蒙Next实现瀑布流布局
前端
令狐寻欢9 分钟前
JavaScript中 的 Object.defineProperty 和 defineProperties
javascript
快起来别睡了10 分钟前
代理模式:送花风波
前端·javascript·架构
海底火旺12 分钟前
电影应用开发:从代码细节到用户体验优化
前端·css·html
陈随易21 分钟前
Gitea v1.24.0发布,自建github神器
前端·后端·程序员
前端付豪24 分钟前
汇丰银行技术架构揭秘:全球交易稳定背后的“微服务+容灾+零信任安全体系”
前端·后端·架构