Vue的响应式原理?Vue2和Vue3有什么区别?

0 阅读3分钟

一、什么是 Vue 的响应式

Vue 的核心能力就是 数据变化 → 自动更新视图

例如:

data() {
  return {
    count: 1
  }
}
<div>{{ count }}</div>

当执行:

this.count = 2

页面会自动更新。

这个过程就是 响应式系统

核心流程:

数据变化
   ↓
监听数据变化
   ↓
通知依赖更新
   ↓
重新渲染视图

Vue 内部有三个关键角色:

角色作用
Observer监听数据
Dep依赖收集
Watcher触发更新

二、Vue2 响应式原理(Object.defineProperty)

Vue2 使用:

Object.defineProperty

劫持对象属性。

示例

let obj = {}

Object.defineProperty(obj, "name", {
  get() {
    console.log("读取")
    return value
  },
  set(newVal) {
    console.log("修改")
    value = newVal
  }
})

当访问:

obj.name

会触发

get()

当修改:

obj.name = "Vue"

会触发

set()

Vue 就利用这个机制实现响应式。


Vue2 内部流程

1 数据劫持

Vue 在初始化 data 时:

遍历所有属性

给每个属性添加 getter / setter。

伪代码:

function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get() {
      // 收集依赖
      dep.depend()
      return val
    },
    set(newVal) {
      val = newVal
      // 通知更新
      dep.notify()
    }
  })
}

2 依赖收集

当模板渲染:

{{ count }}

会生成一个 Watcher

Watcher 会读取数据:

count

触发

getter

然后:

Dep 收集 Watcher

结构:

count
  ↓
Dep
  ↓
Watcher

3 数据变化

当执行:

this.count++

触发:

setter

然后:

Dep.notify()

通知所有 Watcher 更新。

Watcher → 重新渲染

三、Vue2 的缺点

Vue2 的响应式有几个问题:

1 不能监听对象新增属性

this.obj.age = 18

不会更新。

必须:

Vue.set(this.obj, "age", 18)

2 不能监听数组下标

this.arr[1] = 10

不会更新。

必须:

splice
push
pop
shift

Vue 重写了数组方法。


3 初始化性能差

Vue2 会:

递归遍历整个 data

如果数据非常大:

初始化慢

四、Vue3 响应式原理(Proxy)

Vue3 使用:

Proxy

代替

Object.defineProperty

示例:

let obj = { name: "vue" }

let proxy = new Proxy(obj, {
  get(target, key) {
    console.log("读取")
    return target[key]
  },
  set(target, key, value) {
    console.log("修改")
    target[key] = value
    return true
  }
})

Proxy 优势

Proxy 可以拦截:

13 种操作

比如:

get
set
deleteProperty
has
ownKeys

所以:

新增属性
删除属性
数组下标

都能监听。


Vue3 响应式核心

Vue3 内部有两个核心方法:

track(收集依赖)

track(target, key)

当读取数据:

get

收集依赖。


trigger(触发更新)

trigger(target, key)

当数据变化:

set

通知更新。


核心结构:

targetMap
  ↓
WeakMapMapSet

结构图:

WeakMap
  target -> Map
              key -> Set(effect)

意思是:

对象
  ↓
属性
  ↓
依赖函数

五、Vue2 vs Vue3 区别

对比Vue2Vue3
响应式实现Object.definePropertyProxy
监听新增属性不支持支持
数组下标不支持支持
初始化性能需要递归遍历按需代理
APIOptions APIComposition API
代码体积较大更小
TS支持一般非常好

六、Vue3 Composition API(核心变化)

Vue3 新增:

setup()

例如:

import { ref } from "vue"

export default {
  setup() {
    const count = ref(0)

    const add = () => {
      count.value++
    }

    return { count, add }
  }
}

优势:

逻辑复用更好
代码组织更清晰
TS友好

七、面试最佳回答(推荐说法)

面试时可以这样回答:

Vue 的响应式原理是通过数据劫持和依赖收集实现的。

在 Vue2 中,主要通过 Object.defineProperty 对 data 的属性进行 getter 和 setter 劫持,当数据被读取时进行依赖收集,当数据被修改时通知依赖更新,从而触发视图重新渲染。

Vue2 的缺点是无法监听对象新增属性和数组下标变化,因此需要使用 Vue.set 或重写数组方法。

在 Vue3 中,响应式系统改为使用 Proxy 实现。Proxy 可以拦截更多操作,例如属性新增、删除、数组索引等,因此解决了 Vue2 的很多限制。同时 Vue3 使用 tracktrigger 来进行依赖收集和触发更新,并且性能更好。

此外 Vue3 还引入了 Composition API,使得逻辑复用更加灵活,对 TypeScript 支持更好。