先写一个简单的reactive,就是返回了一个proxy
function reactive(target: object) {
return new Proxy(target, {
get() {
track() // 收集依赖,下面会讲
},
set() {
trigger() // 通知依赖进行更新,下面会讲
},
has() {},
ownKeys() {}
// ....
})
}
// 通过 reactive转了之后,每次使用的属性的地方(target.xxx),都会触发get,get就会收集依赖.依赖简单来说就是,你在watchEffect里面使用了 target.xx, 这个watchEffect就是一个依赖(watchEffect传入的回调函数)
// 修改target.xxx, 就会触发 set,set里面会trigger,trigger就是通知依赖进行更新,上面收集了watchEffect的回调函数,这里再次让回调函数执行。达到了修改一个地方,其他地方跟着一起更新
复制代码
源码部分(自己做了一部分删减,核心部分还是不变的,目的是想更好的让大家理解):
function reactive(target: object) {
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) { // 传了一个readOnly的proxy,就会直接返回这个对象,不进行下一部
return target
}
return createReactiveObject( // 生成响应式对象,这里的 mutableHandlers 就是对应的我在上面写的 get,set,has, ownkeys的方法
target,
false,
mutableHandlers,
mutableCollectionHandlers // 这个是针对Map,Set。暂时不管
)
}
// createReactiveObject
function createReactiveObject(
target: Target, // 传入的对象
isReadonly: boolean, // false
baseHandlers: ProxyHandler<any>, // get,set 一系列方法
collectionHandlers: ProxyHandler<any> // 针对Map,Set,暂时不管
) {
if (!isObject(target)) { // reactive 只接收对象
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// exception: calling readonly() on a reactive object
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target // 简单来说,就是当你reactive(reactiveObject)再次传入一个reactive时,不会重新实例化一个proxy,会直接返回当前对象
// 这里还需要关注一个点,当target已经是一个reactive时,这里方法 target[ReactiveFlags.RAW],就会触发这个target的 get,因为它已经是一个proxy了
}
const proxyMap = isReadonly ? readonlyMap : reactiveMap
const existingProxy = proxyMap.get(target) // 传入一个相同的对象时,直接在全局的rroxyMap里面拿到值了,不会重新实例化
if (existingProxy) {
return existingProxy
}
const targetType = getTargetType(target) // 基本都是合法的的类型,都是返回的 TARGET.COMMON
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy( // 实例化proxy
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy) // 做了一个WeakMap的缓存,key就是这个对象,值是proxy,目的就在上面,见existingProxy的解析
return proxy
// 以下是模拟的 baseHandlers
const baseHandlers = {
get,
set
}
const get = (isReadOnly = false,shallow = false) => { // 这里也是函数柯里化的一种形式,返回的函数里面通过闭包的方法,能够拿到isReadOnly, 和 shallow 两个变量, 调用 reactive 时,两个都为false
return function createGetter(target, key, receiver) {
// ... 省略一部分处理当reactive传入一个reactiveObject的过程(ps:传入一个reactiveObject,会直接返回)
const res = Reflect.get(target, key, receiver) // 拿到对应的value
if (!isReadonly) {
track(target, TrackOpTypes.GET, key) // ! 收集依赖,外部调用 target.xx,这里就会收集依赖。比如当你在watchEffect里面访问 target,xxx, 这个就会收集watchEffect的依赖(watchEffect传入的回调函数)
}
if (shallow) { // ! shallow 不会把子元素变为 reactive,对于一些第三方库提供的对象,不要变为reactive
return res
}
if (isObject(res)) { // res 是一个对象的话,vue会把这个子对象再次转为reactive的
// ! 并没有直接帮你把子属性全部转为reactive,而是在你需要调用的时候去转,vue2.0是直接递归所有的data,这里更好
return isReadonly ? readonly(res) : reactive(res) // 将拿到的对象再次转为一个reactive的对象,这样 target.a.b 也是一个响应式的对象了
}
return res
}
}
const set = (shallow = false) => { // 修改 target.xxx 时,就会触发这个set
return function createSetter() {
const oldValue = (target as any)[key]
if (!shallow) { // 拿到的对象也是一个reactive的对象,要转为原始对象
value = toRaw(value)
if (!isArray(target) && isRef(oldValue) && !isRef(value)) { // 老的值是一个ref,但是新的值不是一个ref,vue不会把老的ref干掉,而是把老的ref.value的值替换掉
oldValue.value = value
return true
}
} else {
// in shallow mode, objects are set as-is regardless of reactive or not
}
const result = Reflect.set(target, key, value, receiver) // 设置值
if (target === toRaw(receiver)) {
if (!hadKey) {trigger
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue) // 通知依赖进行更新!访问值得时候触发了track,这里就是触发track收集的依赖。(触发watchEffect的回调函数)
}
}
return result
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END