持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情
在 Vue2 中使用Object.defineProperty 来实现数据的响应式,在 Vue3 中使用 Proxy 来实现响应式(Proxy 是 ES6 中新增的功能)
Vue响应式
Vue 的双向绑定(响应式)是基于 MVVM 模型的。 MVVM 的特点是:数据变化后更新视图,视图变化后更新数据
- Model 数据层
- View 视图层
- ViewModel 业务逻辑层,负责关联数据和视图
这个过程包含两个组成部分:
- 监听器 Observer:对所有数据属性进行监听
- 解析器 Compiler:对每个元素节点的指令进行扫描和解析,根据指令替换数据,绑定对应的更新函数
Object.defineProperty
采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调
代码实现(简版)
- 循环对象中的每个数据,通过 Object.defineProperty 进行数据劫持
- 通过递归实现深层的监听
- typeof obj === 'object' 就是递归的条件(注:递归的是当前的val,不是当前obj)
- 使用 getter 收集依赖 ,setter 通知 watcher派发更新
- set() 中判断值是否改变,改变的话更新视图,如果未改变不更新视图
const render = (key, val) => {
console.log(`key:${key},val:${val}`)
}
const defineReactive = (obj,key,val) => {
reactive(val)
Object.defineProperty(obj,key,{
get() {
return val
},
set(newVal) {
if(val === newVal) return
val = newVal
render(key,val)
}
})
}
const reactive = (obj) => {
if(typeof obj === 'object' && obj !== null) {
for(const key in obj) {
defineReactive(obj,key,obj[key])
}
}
}
const data = {
a:1,
b:2,
c: {
name: {
firstName: 'Ha'
},
msg: 'hello'
}
}
reactive(data)
data.a = 10
data.c.name.firstName = 'AAA'
data.c.msg = 'welcome!'
运行结果:
Proxy
Proxy 可以用来自定义对象中的操作。
Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,Proxy 可以监听到任何方式的数据改变。(第一种方法深度监听需要递归到底,性能层面相比不是特别好)