基本功能
根据官方的文档,我们可以对 reactive 做出基本功能的以下总结
reactive()将使对象本身具有响应性,返回一个Proxy- 只有代理对象是响应式的,更改原始对象不会触发更新
reactive()将深层地转换对象,当访问嵌套对象时,它们也会被reactive()包装- 对同一个原始对象调用
reactive()总是返回同样的代理对象
好接下来我们一起来实现以上四个基本功能
1. reactive() 将使对象本身具有响应性
- 基本使用
const origin = { a:1 }
const observe = reactive(origin)
console.log(observe.a) // 1
- 代码实现
众所周知 vue2.x 的响应式原理的实现是通过 Object.defineProperty 对对象的 set get 方法进行响应式拦截;
而在 vue3.x 中的响应式是通过 Proxy 实现的
function reactive(origin) {
const observe = new Proxy(origin, {
get(target, key, recevier) {
// TODO 依赖收集
console.log('依赖收集', target, key ,recevier)
const res = Reflect(target, key, recevier)
return res
},
set(target, key, value, recevier) {
// TODO 触发响应
console.log('触发响应')
Reflect.set(target, key, value, recevier)
}
})
return observe
}
const originData = { a: 1 }
const observeData = reactive(originData)
console.log(observeData.a) // 1
console.log(observeData === originData) // false
但是当前的代码并不满足 第三条特点, 深层次转换
const originData = { a: { b: 1 } }
const observeData = reactive(originData)
console.log(observeData.a.b) // 1
以上代码 会触发get 但是 监听到的key 是 a 而不是 b
因为 此时的 b 并不是一个 响应式的对象
我们需要对它包装一层, 修改 get 方法
非常简单,只需要判断我们获取到的值是否是对象,如果是一个对象我们就包裹一层 reactive
get(target, key, recevier) {
// TODO依赖收集
console.log('依赖收集', target, key ,recevier)
const res = Reflect.get(target, key, recevier)
// 如果 res 是对象的话 就包裹一层
if (res !== null && typeof res === 'object') {
return reactive(res)
}
return res
}
依赖收集 { a: { b: 2 } } a { a: { b: 2 } }
依赖收集 { b: 2 } b { b: 2 }
这样就会触发 两次 get 方法 从而在获取到深层的时候得到收集相应的依赖
到现在我们就已经满足三个特点了。
最后一个算是 一个小小的优化,相当于做了一个缓存,如果是同一个对象被 reactive,将会被返回同一个 Proxy 对象
const reactiveMap = new Map()
function reactive(origin) {
const cachedItem = reactiveMap.get(origin)
if (cachedItem) {
return cachedItem
}
const observe = new Proxy(origin, {
get(target, key, recevier) {
// TODO依赖收集
console.log('依赖收集', target, key, recevier)
const res = Reflect.get(target, key, recevier)
// 如果 res 是对象的话 就包裹一层
if (res !== null && typeof res === 'object') {
return reactive(res)
}
return res
},
set(target, key, value, recevier) {
// TODO触发响应
Reflect.set(target, key, value, recevier)
}
})
reactiveMap.set(origin, observe)
return observe
}
const originData = { a: 1 }
const observeData = reactive(originData)
const observeData2 = reactive(originData)
console.log(observeData === observeData2) // true
扩展方法
最后我们将实现 reactive 相关的方法
isReactive 判断是否是 由Reactive创建的
这个方法只需要 在 reactive 里面做适配即可
function isReactive(data) {
return !!data['_Is_Reactive']
}
get(target, key, recevier) {
// TODO依赖收集
console.log('依赖收集', target, key ,recevier)
// !!!!重要
if (key === '_Is_Reactive') {
return true
}
const res = Reflect.get(target, key, recevier)
// 如果 res 是对象的话 就包裹一层
if (res !== null && typeof res === 'object') {
return reactive(res)
}
return res
}
const originData = { a: { b: 1 } }
const observeData = reactive(originData)
console.log(isReactive(observeData)) // true
console.log(isReactive(observeData.a)) // true
console.log(isReactive(observeData.a.b)) // true
至于依赖收集,响应触发 将在之后有机会再实现