锦书在线
80.52M · 2026-03-21
Vue 的核心能力就是 数据变化 → 自动更新视图。
例如:
data() {
return {
count: 1
}
}
<div>{{ count }}</div>
当执行:
this.count = 2
页面会自动更新。
这个过程就是 响应式系统。
核心流程:
数据变化
↓
数据变化
↓
通知依赖更新
↓
重新渲染视图
Vue 内部有三个关键角色:
| 角色 | 作用 |
|---|---|
| Observer | 数据 |
| Dep | 依赖收集 |
| Watcher | 触发更新 |
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 就利用这个机制实现响应式。
Vue 在初始化 data 时:
遍历所有属性
给每个属性添加 getter / setter。
伪代码:
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
// 收集依赖
dep.depend()
return val
},
set(newVal) {
val = newVal
// 通知更新
dep.notify()
}
})
}
当模板渲染:
{{ count }}
会生成一个 Watcher
Watcher 会读取数据:
count
触发
getter
然后:
Dep 收集 Watcher
结构:
count
↓
Dep
↓
Watcher
当执行:
this.count++
触发:
setter
然后:
Dep.notify()
通知所有 Watcher 更新。
Watcher → 重新渲染
Vue2 的响应式有几个问题:
this.obj.age = 18
不会更新。
必须:
Vue.set(this.obj, "age", 18)
this.arr[1] = 10
不会更新。
必须:
splice
push
pop
shift
Vue 重写了数组方法。
Vue2 会:
递归遍历整个 data
如果数据非常大:
初始化慢
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 可以拦截:
13 种操作
比如:
get
set
deleteProperty
has
ownKeys
所以:
新增属性
删除属性
数组下标
都能。
Vue3 内部有两个核心方法:
track(target, key)
当读取数据:
get
收集依赖。
trigger(target, key)
当数据变化:
set
通知更新。
核心结构:
targetMap
↓
WeakMap
↓
Map
↓
Set
结构图:
WeakMap
target -> Map
key -> Set(effect)
意思是:
对象
↓
属性
↓
依赖函数
| 对比 | Vue2 | Vue3 |
|---|---|---|
| 响应式实现 | Object.defineProperty | Proxy |
| 新增属性 | 不支持 | 支持 |
| 数组下标 | 不支持 | 支持 |
| 初始化性能 | 需要递归遍历 | 按需代理 |
| API | Options API | Composition API |
| 代码体积 | 较大 | 更小 |
| TS支持 | 一般 | 非常好 |
Vue3 新增:
setup()
例如:
import { ref } from "vue"
export default {
setup() {
const count = ref(0)
const add = () => {
count.value++
}
return { count, add }
}
}
优势:
逻辑复用更好
代码组织更清晰
TS友好
面试时可以这样回答: