文章目录

Vue2概述
Vue2 中组件的 data 必须是函数,返回一个对象,这是 Vue2 最核心的设计之一
优缺点
好处(优点)
- 组件实例数据隔离(最重要)
组件被多次复用(比如列表里循环多个组件)时,每个组件都会独立调用 data 函数,返回全新对象。
不会出现一个组件改数据,所有组件一起变的问题。
本质:对象是引用类型,直接用对象会共享内存,函数返回能保证每次都是新对象。 - 响应式系统自动驱动视图
Vue2 会遍历 data 里的所有属性,用 Object.defineProperty 劫持 getter/setter。
数据一变,视图自动更新,不用手动操作 DOM。 - 集中管理组件状态,代码易维护
组件自己的数据都放在 data 里,清晰统一,便于阅读、调试、维护。 - 搭配 computed、watch 非常丝滑
data 是响应式源头,计算属性自动依赖收集,监听属性轻松监听变化,开发效率极高。 - 简单直观,适合新手
定义数据 → 模板使用 → 修改数据,流程非常简单,学习成本低。
坏处(缺点 / 局限性)
-
新增 / 删除属性无法响应式
Vue2 只能劫持初始化时已存在的属性:// 初始化没有 user.name
this.user = {}
this.user.name = '张三' // 不响应式!视图不更新
必须用 Vue.set / this.$set 手动处理,非常麻烦。
- 数组下标 / 长度修改不响应
js
this.arr[0] = 100 // 不响应
this.arr.length = 0 // 不响应
只能用数组方法(push/pop/splice)或 $set,容易踩坑。
- 对象嵌套深时,性能有损耗
Vue2 会递归遍历 data 所有属性做劫持,数据越大、层级越深,初始化开销越大。
- 组件数据分散,大型项目难管理
data 是组件级状态,大型项目多人协作时,数据流混乱。
必须搭配 Vuex,增加了学习成本和代码量。
- this 指向与模板魔法,容易迷惑新手
模板里直接写 msg,JS 里写 this.msg,本质是代理,但新手不理解原理容易踩坑。
总结
Vue2 让 data 是函数,就是为了避免多个组件实例共享同一个数据对象;用 Object.defineProperty 实现响应式,但带来了新增属性不响应的缺陷。
Vue3 概述
ref():把基本类型(string/number/boolean)包装成响应式对象,底层套一层 .value;
复杂对象也能用 ref,但一般优先用 reactive。
优缺点
优点
-
完美支持「基本类型响应式」
- reactive 管不了 string/number/boolean,只能靠 ref 包装;解决
- Vue2 基础类型零散定义、没法精细化监听的问题。
-
解构不丢响应式(比 reactive 稳)
-
reactive 直接解构会丢失响应:
js// reactive坑 const obj = reactive({a:1}) let {a} = obj // 丢响应 -
ref 单个变量导出、解构、return 都稳,适合拆分细粒度状态。
-
-
模板自动解包,写起来干净
模板里不用写 .value:
js<p>{{ count }}</p> JS 里才加 .value,观感清爽。 -
简单轻量,适合零散 / 独立变量
- 开关、loading、页码、弹窗显隐、倒计时......
-
跨组件 / 组合式函数抽离超方便
写 hooks / 组合函数:
jsexport function useNum(){ const n = ref(0) return {n} }返回直接用,嵌套少、依赖清晰,比 reactive 好维护。
缺点
-
JS 里必须写 .value,容易忘、漏写
新手高频 bug:jscount = 100 // ❌直接覆盖,丢响应 count.value = 100 // ✅正确 -
存复杂对象 / 深层数据,不如 reactive 顺手
jsconst user = ref({name:'',age:0}) // 改深层要写多层value,啰嗦 user.value.name = '张三'深层对象、表单大对象,优先 reactive。
-
不能直接整个替换对象(易踩坑)
jsuser.value = {name:'李四'} // 能替换,但依赖追踪要重走大规模覆写不如reactive直观,还容易打断监听。
-
和原生变量混用容易迷惑
普通 let/const 没 .value,ref 有;多人协作时,一眼分不清是不是响应式。 -
批量状态时代码偏碎
一堆零散 ref:name、age、sex、phone......代码散,不如 reactive 聚合规整。
reactive 是什么
概述
reactive 靠 Proxy 实现,Proxy 只能包装「对象 / 数组 / Map/Set」引用类型;
Proxy 是 ES6 原生 API,只能拦截引用类型(堆内存对象):
基础语法
js
import { reactive } from 'vue'
// 只能放:对象 / 数组 / Map / Set
const 变量名 = reactive(引用类型值)
常用场景写法
-
普通对象(表单 / 配置最常用)
js<script setup> import { reactive } from 'vue' // 定义 const form = reactive({ username: '', password: '', age: 18 }) // 修改:直接点属性,不用 .value form.username = '张三' form.age = 20 </script> <template> <input v-model="form.username"> </template> -
数组
jsconst list = reactive([1,2,3]) list.push(4) // 响应 list[0] = 99 // 响应(Vue3 Proxy 原生支持)
深层嵌套对象(天生深度响应)
js
const info = reactive({
user: {
name: '',
addr: { city: '' }
}
})
info.user.addr.city = '上海' // 直接生效,不用$set
Map / Set
js
const map = reactive(new Map())
map.set('key', 123) // 响应式更新
搭配组合(企业级标准写法)
js
import { reactive, toRefs } from 'vue'
// 1.聚合状态用 reactive
const state = reactive({
loading: false,
page: 1,
list: []
})
// 2.需要解构导出,用 toRefs
const { loading, page, list } = toRefs(state)
一堆相关数据、表单、数组、嵌套对象 → reactive
单个数字 / 布尔 / 字符串 → ref
reactive 要解构 → 必加 toRefs