Vue2和Vue3响应式数据对比

Vue2和Vue3全面对比的第二项是响应式数据,这是框架的核心机制。Vue通过响应式系统实现数据变化时视图自动更新,开发者只需关注数据本身,无需手动操作DOM。

Vue2基于Object.defineProperty实现响应式,存在无法检测属性新增/删除、数组索引修改等限制,需要借助Vue.set()/Vue.delete()等辅助API。Vue3采用Proxy代理实现响应式,全面拦截对象操作,无上述限制,同时引入了ref()、reactive()、computed()、watch()等函数式API,配合Composition API实现了更灵活的响应式数据管理。

 

对比项

Vue 2 功能

Vue 2 示例

Vue 3 功能

Vue 3 示例

基本类型响应式

data 选项

data() {
  return {
    count: 0,
    message: "hello"
  }
}
// 使用:this.count

ref()

注意:在 script 中用 .value,在 template 中不用;ref 虽支持所有类型,但 reactive 提供更简洁、高效的响应式对象操作,避免 .value 层级,适合复杂状态结构

 

const count = ref(0)
const message = ref("hello")
// JS 中使用 .value
count.value++
// 模板中自动解包
// {{ count }}

对象类型响应式

data 选项

data() {
  return {
    user: {
      name: "",
      age: 0
    }
  }
}
// 使用:this.user.name

reactive()

注意:永远不要对 reactive 对象进行解构,除非用 toRefs

const user = reactive({
  name: "",
  age: 0
})
// 直接访问,无需 .value
user.name = "张三"

数组响应式

data 选项
(索引修改有限制)

data() {
  return { list: [1, 2, 3] }
}
// 非响应式
this.list[0] = 99
this.list.length = 0
// 用变异方法
this.list.push(4)
this.list.splice(0, 1)

ref() / reactive()
Proxy 全面拦截)

注意:强烈建议用 ref([])而不是reactive([])来存储数组

const list = ref([1, 2, 3])
// 全部响应式
list.value[0] = 99
list.value.length = 0
list.value.push(4)

计算属性

computed 选项

// 只读
counted: {
  double() {
    return this.count * 2
  }
}
// 可写
computed: {
  fullName: {
    get() {
      return this.first + this.last
    },
    set(val) {
      this.first = val[0]
    }
  }
}

computed() 函数

注意:computed 返回的是 Ref 对象,需要.value访问

// 只读
const double = computed(
  () => count.value * 2
)
// 可写
const fullName = computed({
  get: () => first.value + last.value,
  set: (val) => { first.value = val[0] }
})

侦听属性

watch 选项

watch: {
  count(newVal, oldVal) {
    console.log(newVal)
  },
  // 深度监听
  user: {
    handler(val) { /* ... */ },
    deep: true
  },
  // 立即执行
  name: {
    handler(val) { /* ... */ },
    immediate: true
  }
}

watch() 函数

注意:watch 监听的是 Ref 对象,但回调函数中不需要.value

// 基本用法
watch(count, (n, o) => {
  console.log(n)
})
// 深度监听
watch(user, (v) => {},
  { deep: true })
// 立即执行
watch(name, (v) => {},
  { immediate: true })

监听多个数据源

多个 watch 处理器

watch: {
  firstName(val) {
    this.full = val + this.lastName
  },
  lastName(val) {
    this.full = this.firstName + val
  }
}

watch() 传入数组

watch(
  [firstName, lastName],
  ([f, l]) => {
    fullName.value = f + l
  }
)

自动收集依赖侦听

watchEffect()

watchEffect(() => {
  console.log(count.value)
})
// 自动追踪内部
// 使用的响应式数据

解构保持响应式

解构会丢失响应式

// 解构后丢失响应式
const { name } = this.user
// name 不再是响应式

toRefs() / toRef()

// 解构仍保持响应式
const { name, age } = toRefs(user)
// name, age 仍是 ref

// 单个属性
const name = toRef(user, "name")

响应式工具函数

isRef()
unref()
toValue()

isRef(count)  // true

// 自动解包 ref
unref(count)  // = count.value

// Vue 3.3+
toValue(getterOrRef)

新增属性

Vue.set()
this.$set()

// 方式一
this.$set(obj, "newKey", value)

// 方式二
Vue.set(obj, "newKey", value)

// 直接赋值不触发更新
// this.obj.newKey = value //

直接赋值

// Proxy 自动追踪,直接赋值
obj.newKey = "新增属性"

const obj = reactive({})
obj.newKey = "hello" //响应式

删除属性

Vue.delete()
this.$delete()

// 方式一
this.$delete(obj, "key")

// 方式二
Vue.delete(obj, "key")

// 直接 delete 不触发更新
// delete this.obj.key //

直接 delete

// Proxy 自动追踪
delete obj.key // 响应式

const obj = reactive({ name: "" })
delete obj.name

主动更新DOM

this.$nextTick()

 

await nextTick()

 

底层实现原理

Object.defineProperty()

// 拦截属性的 getter/setter
Object.defineProperty(
  obj, "key", {
    get() { /* 收集依赖 */ },
    set(v) { /* 派发更新 */ }
  }
)
// 限制:
// - 无法检测新增/删除属性
// - 无法检测数组索引修改
// - 需要递归遍历所有属性

Proxy

// 代理整个对象
new Proxy(target, {
  get(target, key) { /* track */ },
  set(target, key, val) { /* trigger */ },
  deleteProperty(target, key) {}
})
// 优势:
// - 可检测新增/删除属性
// - 可检测数组索引修改
// - 惰性监听,按需追踪

 

以上对比可以清晰地看出Vue3在响应式系统方面的重大改进:从底层原理上用Proxy替代Object.defineProperty解决了诸多限制,从API设计上引入了ref/reactive/computed/watch等函数式API,提供了更灵活、更强大的响应式数据管理能力,使得开发者可以更精细地控制响应式行为。

posted on 2026-05-25 20:15  JustItIs  阅读(0)  评论(0)    收藏  举报

导航