Vue响应式原理六:Vue3响应式原理
1. 多个对象响应式
-
- 当前存在的问题:当前实现仅针对某个固定对象(obj)进行依赖收集,实际开发中需要处理多个不同对象
-
- 将对象响应式处理逻辑抽取为通用函数,支持任意对象
-
- 代码如下:
// 方案一:Object.defineProperty -> Vue2// 多个对象响应式抽取成通用函数function reactive(obj) {Object.keys(obj).forEach(key => {let value = obj[key];Object.defineProperty(obj, key, {set: function(newValue) {value = newValue;const dep = getDepend(obj, key)dep.notify()},get: function() {// 找到对应的obj对象的key对应的dep对象const dep = getDepend(obj, key)// dep.addDepend(reactiveFn)dep.depend()return value;}})})return obj}
- 代码如下:
-
- Vue2实现:
- 基于Object.defineProperty的响应式系统
- data返回的对象会被reactive包裹处理
- 模板编译生成的render函数自动收集依赖
2. Vue3响应式原理(监听对象-Proxy)
-
2.1. 核心:使用
Proxy
替代Vue2的Object.defineProperty
实现响应式 -
2.2 与
Vue2
对比:Vue2
需要遍历对象所有属性进行监听,Vue3
的Proxy
可以自动监听整个对象Proxy
能捕获更多操作类型(如新增属性、删除属性等)
-
2.3. 完整代码:
// 方案二:new Proxy() -> Vue3 function reactive(obj) {const objProxy = new Proxy(obj, {// receiver作用:1. 可以改变操作中的this指向 2. 确保getter/setter中的this指向代理对象set: function(target, key, newValue, receive) {// target[key] = newValueReflect.set(target, key, newValue, receive)const dep = getDepend(target, key)dep.notify()},get: function (target, key, receive) {const dep = getDepend(target, key)dep.depend()return Reflect.get(target, key, receive)}})return objProxy }
3. Depend类的重构
-
- 重构的点:
- 使用
Set替代数组存储依赖函数,避免重复收集
- 添加depend方法专门处理依赖收集逻辑
-
- 代码如下:
class Depend {constructor() {// 使用Set替代数组存储依赖函数,避免重复收集this.reactiveFns = new Set();}addDepend (fn) {if(fn) {this.reactiveFns.add(fn);}}depend () {if(reactiveFn) {this.reactiveFns.add(reactiveFn)}}notify () {this.reactiveFns.forEach(fn => {fn()})}}
- 代码如下:
4. 完整代码如下:
class Depend {constructor() {// 使用Set替代数组存储依赖函数,避免重复收集this.reactiveFns = new Set();}addDepend (fn) {if(fn) {this.reactiveFns.add(fn);}}depend () {if(reactiveFn) {this.reactiveFns.add(reactiveFn)}}notify () {this.reactiveFns.forEach(fn => {fn()})}
}// 封装一个函数:负责通过obj的key获取对应的Depend对象
const objMap = new WeakMap() // WeakMap弱引用
function getDepend (obj, key) {// 1.根据对象obj,找到对应的map对象let map = objMap.get(obj)if(!map) {map = new Map()objMap.set(obj, map)}// 2.根据key,找到对应的depend对象let dep = map.get(key)if(!dep) {dep = new Depend();map.set(key, dep)}return dep
}// 监听属性变化数据劫持
// 方案一:Object.defineProperty -> Vue2
// 多个对象响应式抽取成通用函数
// function reactive(obj) {
// Object.keys(obj).forEach(key => {
// let value = obj[key];
// Object.defineProperty(obj, key, {
// set: function(newValue) {
// value = newValue;
// const dep = getDepend(obj, key)
// dep.notify()
// },
// get: function() {
// // 找到对应的obj对象的key对应的dep对象
// const dep = getDepend(obj, key)
// // dep.addDepend(reactiveFn)
// dep.depend()
// return value;
// }
// })
// })
// return obj
// }// 方案二:new Proxy() -> Vue3
function reactive(obj) {const objProxy = new Proxy(obj, {// receiver作用:1. 可以改变操作中的this指向 2. 确保getter/setter中的this指向代理对象set: function(target, key, newValue, receive) {// target[key] = newValueReflect.set(target, key, newValue, receive)const dep = getDepend(target, key)dep.notify()},get: function (target, key, receive) {const dep = getDepend(target, key)dep.depend()return Reflect.get(target, key, receive)}})return objProxy
}// 设置一个专门执行响应式函数的一个函数
let reactiveFn = null // 自由变量
function watchFn (fn) {reactiveFn = fnfn()reactiveFn = null
}// =================================== 业务代码 =====================================
const obj = reactive({name: 'why',age: 18,address: '长沙市'
})watchFn(function () {console.log(obj.name);console.log(obj.age);
})// 修改obj的属性
console.log('age发生变化时----------------------------------------');
obj.age = 21console.log('user对象----------------------------------------');const user = reactive({nickName: 'abc',level: 100
})watchFn(function () {console.log('nickName: ' ,user.nickName);console.log('level: ' ,user.level);
})user.nickName = 'cba'