More articles
preface
I have been using the combined API for a while, so I should read the code of Yuda with an attitude of knowing what it is and why. Please share the part about Reactive and analyze it one by one according to the APIreactive part on the official website. You can refer to the simplified code for easy understanding
Simplify the code
Vue3-reactive source location
Proxy
Vue3 uses Proxy to replace defineProperty to solve the drawbacks brought by defineProperty, and reactive is closely related to Proxy. For those who do not know Proxy, please see Ruan Yinfong ES6 introduction, and then start source analysis
reactive
See how reactive is defined in the source code
export function reactive<T extends object> (target: T) :UnwrapNestedRefs<T>
export function reactive(target: object) {
// if trying to observe a readonly proxy.return the readonly version.
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
// createReactiveObject
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
if(! isObject(target)) {if (__DEV__) {
console.warn(`value cannot be made reactive: The ${String(target)}`)}return target
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
if( target[ReactiveFlags.RAW] && ! (isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) {return target
}
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
Copy the code
The createReactiveObject method is used to create a reactive method. This code and createReactiveObject are simplified as follows:
function reactive(target) {
return createReactiveObject(target, reactiveMap, mutableHandlers)
}
function createReactiveObject(target, proxyMap, baseHandlers) {
// Non-object return
if(! isObject(target)) {return target
}
// Whether it already exists
const existingProxy = proxyMap.get(target)
// There is a return
if(existingProxy) return existingProxy
// Determine the type
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) return target
// Use proxy to generate a new object
const proxy = new Proxy(target, baseHandlers)
// Cache data
proxyMap.set(target, proxy)
return proxy
}
Copy the code
You can see that a Proxy instance is returned, and reactive is implemented as follows:
function reactive(target) {
return new Proxy(target, {
get,
set
})
}
Copy the code
The implementation of get, set and so on is very important, the implementation of the source code is still more complex, here to simplify (try to use the code in the source code) :
/ * * *@param IsReadOnly Whether to read only *@param Shallow is shallowReadonly or shallowReactive */
function createGetter(isReadOnly = false, shallow = false) {
return function get(target, key, receiver) {
// Check whether it is reactive
if(key === IS_REACTIVE) return! isReadOnly// Check whether it is readonly
else if(key === IS_READONLY) return isReadOnly
// toRaw operation judgment
else if(
key === RAW &&
receiver ===
(
isReadOnly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) return target
const result = Reflect.get(target, key, receiver)
// Shallow is true, if true, no further processing is performed
if (shallow) return result
// If it is an object, it is processed in depth
if(isObject(result)) return isReadOnly ? readonly(result) : reactive(result)
return result
}
}
function createSetter() {
return function set(target, key, value, receiver) {
console.log(`${key}Has changed)
const result = Reflect.set(target, key, value, receiver)
return result
}
}
const get = createGetter();
const set = createSetter();
Copy the code
Here you can see that rective does some deep processing on the object, simplifying get as follows:
function createGetter(isReadOnly = false, shallow = false) {
return function get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
// If it is an object, it is processed in depth
if(isObject(result)) return isReadOnly ? readonly(result) : reactive(result)
return result
}
}
Copy the code
readonly
When the readonly method is called, isReadOnly in GET is set to true, and methods such as set and deleteProperty are overridden to prevent and prompt read-only
const readonlyHandlers = {
get: createGetter(true),
set: (target, key) = > {
console.warn(
`Set operation on key "The ${String(key)}" failed: target is readonly.`,
target
);
return true
},
deleteProperty(target, key) {
if (__DEV__) {
console.warn(
`Delete operation on key "The ${String(key)}" failed: target is readonly.`,
target
)
}
return true}}function readonly(target) {
return createReactiveObject(target, readonlyMap, readonlyHandlers)
}
Copy the code
shallowReactive
When the shallowReactive method is called, shallow in GET is set to true and the data will not be processed further
function shallowReactive(target) {
return createReactiveObject(target, shallowReactiveMap, shallowReactiveHandlers)
}
const shallowReactiveHandlers = {
get: createGetter(false.true),
set
}
Copy the code
shallowReadonly
Set both isReadOnly and shallow to true
function shallowReadonly(target) {
return createReactiveObject(target, shallowReadonlyMap, shallowReadonlyHandlers)
}
const shallowReadonlyHandlers = {
get: createGetter(true.true),
set(target, key) {
// Readonly responsive objects cannot be modified
console.warn(
`Set operation on key "The ${String(key)}" failed: target is readonly.`,
target
);
return true; }};Copy the code
isReactive
When the IS_REACTIVE attribute is fetched, the get method returns! IsReadOnly: Reactive if not read-only
function isReactive(value) {
return!!!!! value[IS_REACTIVE] }Copy the code
isReadonly
When the IS_READONLY property is retrieved, isReadOnly is returned in the get method
function isReadonly(value) {
return!!!!! value[IS_READONLY] }Copy the code
isProxy
IsProxy is isReactive or isReadonly
function isProxy(value) {
return isReactive(value) || isReadonly(value)
}
Copy the code
toRaw
When we call the toRaw method, we will get value[RAW]. The get method will judge this attribute and get the corresponding cache data. If it hits, the RAW data will be returned (corresponding to the three-entry operation in get).
function toRaw(value) {
const raw = value[RAW]
return raw ? toRaw(raw) : value
}
Copy the code
markRaw
MarkRaw adds an identifier skip to data, and createReactiveObject immediately returns the original object when skip is judged to have hit, never going to the proxy
function markRaw(value) {
def(value, SKIP, true)
return value
}
Copy the code
conclusion
The code looks better with simplicity