1. Kod sumber reaktif
1. laluan kod sumber reaktif:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | export function reactive(target: object) {
if (isReadonly(target)) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
|
Salin selepas log masuk
packages/reactivity/src/reactive.ts
Apabila kami melaksanakan
, ini akan menjadi kaedah Kilang dilaksanakan, mengembalikan objek reaktif.
reactive({})
2. Seterusnya, lihat kaedah kilang createReactiveObjectcreateReactiveObject
Laluan kod sumber:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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: ${String(target)}`)
}
return target
}
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
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
}
|
Salin selepas log masuk
Hanya sah untuk jenis objek (objek, tatasusunan dan jenis koleksi seperti Map and Set) , yang tidak sah untuk jenis primitif seperti rentetan, nombor dan boolean. packages/reactivity/src/reactive.ts
1 2 3 4 5 6 | if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
|
Salin selepas log masuk
Jika sasaran sudah menjadi objek proksi, maka teruskan kembali sasaran
1 2 3 4 5 6 | if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
|
Salin selepas log masuk
Jika sasaran sudah mempunyai objek proksi yang sepadan, maka teruskan kembali objek proksi
1 2 3 4 | const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
|
Salin selepas log masuk
Untuk Jenis yang tidak boleh diperhatikan terus mengembalikan sasaran
1 2 3 4 | const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
|
Salin selepas log masuk
rrree
Buat objek responsif (kod teras)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | function getTargetType(value: Target) {
return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
? TargetType.INVALID
: targetTypeMap(toRawType(value))
}
export const enum ReactiveFlags {
SKIP = '__v_skip',
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly',
IS_SHALLOW = '__v_isShallow',
RAW = '__v_raw'
}
function targetTypeMap(rawType: string) {
switch (rawType) {
case 'Object':
case 'Array':
return TargetType.COMMON
case 'Map':
case 'Set':
case 'WeakMap':
case 'WeakSet':
return TargetType.COLLECTION
default :
return TargetType.INVALID
}
}
export const toRawType = (value: unknown): string => {
return toTypeString(value).slice(8, -1)
}
|
Salin selepas log masuk
Yang berikut akan memfokuskan pada fungsi panggil balik
.
2. baseHandlersbaseHandlers
1. baseHandlers
ialah
, yang datang daripada fail
. baseHandlers
mutableHandlers
baseHandlers
Kod sumber mutableHandlers adalah seperti berikut, proksi mana yang masing-masing mendapat, menetapkan, memadamProperty, mempunyai dan ownKeys. 
1 2 3 4 | const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
|
Salin selepas log masuk
Mari kita lihat pelaksanaan khusus pemintas ini.
(1), dapatkan proksi
1 2 3 4 5 6 7 | export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys
}
|
Salin selepas log masuk
Apabila sasaran ialah tatasusunan, 'tolak', 'pop', 'shift', 'unshift', ' splice ' kaedah akan mengubah panjang tatasusunan dan menyebabkan rekursi tak terhingga Oleh itu, pengumpulan kebergantungan mesti dijeda dahulu, jadi kaedah tatasusunan di atas dipintas dan ditulis semula 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | const get = createGetter()
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
return shallow
} else if (
key === ReactiveFlags.RAW &&
receiver ===
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target
}
const targetIsArray = isArray(target)
if (!isReadonly) {
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
if (key === 'hasOwnProperty') {
return hasOwnProperty
}
}
const res = Reflect.get(target, key, receiver)
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res
}
if (!isReadonly) {
track(target, TrackOpTypes.GET, key)
}
if (shallow) {
return res
}
if (isRef(res)) {
return targetIsArray && isIntegerKey(key) ? res : res.value
}
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
|
Salin selepas log masuk
Kod ditulis semula:
1 2 3 4 | if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
|
Salin selepas log masuk
.
Gambar berikut ialah hasil pelaksanaan:
boleh difahami dengan kod berikut: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | const arrayInstrumentations = createArrayInstrumentations()
function createArrayInstrumentations() {
const instrumentations: Record<string, Function> = {}
;(['push', 'pop', 'shift', 'unshift', 'splice'] as const ).forEach(key => {
instrumentations[key] = function (this: unknown[], ...args: unknown[]) {
console.log('----自定义push等入口:this, args, key');
pauseTracking()
console.log('----自定义push等暂停收集依赖&执行开始')
const res = (toRaw(this) as any)[key].apply(this, args)
console.log('----自定义push等暂停收集依赖&执行结束')
resetTracking()
return res
}
})
return instrumentations
}
|
Salin selepas log masuk
Atribut khas tidak mengumpul kebergantungan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | let arr = [1,2,3]
let obj = {
'push': function (...args) {
return Array.prototype.push.apply(this, [...args])
}
}
let proxy = new Proxy(arr, {
get: function (target, key, receiver) {
console.log('get的key为 ===>' + key);
let res = '';
if (key === 'push') {
res = Reflect.get(obj, key, receiver)
} else {
res = Reflect.get(target, key, receiver)
}
return res
},
set(target, key, value, receiver){
console.log('set的key为 ===>' + key, value);
return Reflect.set(target, key, value, receiver);
}
})
proxy.push('99')
|
Salin selepas log masuk
Langkah ini adalah untuk menapis beberapa atribut khas, seperti atribut jenis Simbol asli, seperti: Symbol.iterator, Symbol.toStringTag, dll. Atribut ini tidak memerlukan pengumpulan kebergantungan kerana ia terbina dalam dan tidak akan berubah ;
Terdapat juga beberapa atribut yang tidak dapat dikesan, seperti:
proto
, __v_isRef, __isVue ini tidak memerlukan pengumpulan pergantungan
Koleksi pergantungan
1 2 3 4 | if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res;
}
|
Salin selepas log masuk
Shallow tidak melakukan proksi rekursif
1 2 3 4 | if (!isReadonly) {
track(target, "get" , key);
}
|
Salin selepas log masuk
Buka pek nilai pulangan
1 2 3 | if (shallow) {
return res;
}
|
Salin selepas log masuk
Langkah ini adalah untuk mengendalikan situasi ref, kemudian bongkarkan res. jika ia adalah tatasusunan dan kuncinya ialah jenis integer, maka ia tidak akan dibongkar kerana reaktif sangat responsif, ia mesti dibongkar jika atribut adalah ref
Proksi rekursif objek
1 2 3 4 5 | if (isRef(res)) {
return targetIsArray && isIntegerKey(key) ? res : res.value;
}
|
Salin selepas log masuk
(2), proksi set
1 2 3 4 | if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res);
}
|
Salin selepas log masuk
Logik utama: Dapatkan nilai lama
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | const set = createSetter()
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
let oldValue = (target as any)[key]
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false
}
if (!shallow) {
if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue)
value = toRaw(value)
}
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
} else {
}
console.log('----set', target, key, value)
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key)
const result = Reflect.set(target, key, value, receiver)
if (target === toRaw(receiver)) {
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
}
return result
}
}
|
Salin selepas log masuk
Nilai sama ada nilai lama itu sahaja Baca
rreeee
Atas ialah kandungan terperinci Analisis kod sumber reaktif teras responsif Vue3. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!