深入理解vue3中的reactive()
在vue3的開(kāi)發(fā)中,reactive是提供實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的方法。日常開(kāi)發(fā)這個(gè)是使用頻率很高的api。這篇文章筆者就來(lái)探索其內(nèi)部運(yùn)行機(jī)制。小白一枚,寫(xiě)得不好請(qǐng)多多見(jiàn)諒。
調(diào)試版本為3.2.45
什么是reactive?
reactive是Vue3中提供實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的方法.
在Vue2中響應(yīng)式數(shù)據(jù)是通過(guò)defineProperty來(lái)實(shí)現(xiàn)的.
而在Vue3響應(yīng)式數(shù)據(jù)是通過(guò)ES6的Proxy來(lái)實(shí)現(xiàn)的
reactive注意點(diǎn)
reactive參數(shù)必須是對(duì)象(json/arr)
如果給reactive傳遞了其他對(duì)象,默認(rèn)情況下修改對(duì)象,界面不會(huì)自動(dòng)更新,如果想更新,可以通過(guò)重新賦值的方式。
<script setup> import {reactive} from 'vue' const data = reactive({ //定義對(duì)象 name:'測(cè)試', age:10 }) const num = reactive(1)//定義基本數(shù)據(jù)類(lèi)型 console.log(data)//便于定位到調(diào)試位置 </script> <template> <div> <h1>{{ data.name }}</h1> </div> </template> <style scoped></style>
設(shè)置斷點(diǎn)
開(kāi)始調(diào)試
接下來(lái)我們可以開(kāi)始調(diào)試了,設(shè)置好斷點(diǎn)后,只要重新刷新頁(yè)面就可以進(jìn)入調(diào)試界面。
復(fù)雜數(shù)據(jù)類(lèi)型
我們先調(diào)試簡(jiǎn)單的基本數(shù)據(jù)類(lèi)型
/*1.初始進(jìn)來(lái)函數(shù),判斷目標(biāo)對(duì)象target是否為只讀對(duì)象,如果是直接返回*/ function reactive(target) { // if trying to observe a readonly proxy, return the readonly version. if (isReadonly(target)) { return target; } //創(chuàng)建一個(gè)reactive對(duì)象,五個(gè)參數(shù)后續(xù)會(huì)講解 return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap); } /*2.判斷是來(lái)判斷target是否為只讀。*/ function isReadonly(value) { return !!(value && value["__v_isReadonly" /* ReactiveFlags.IS_READONLY */]); } /*3.創(chuàng)建一個(gè)reactive對(duì)象*/ /*createReactiveObject接收五個(gè)參數(shù): target被代理的對(duì)象, isReadonl是不是只讀的, baseHandlers proxy的捕獲器, collectionHandlers針對(duì)集合的proxy捕獲器, proxyMap一個(gè)用于緩存proxy的`WeakMap`對(duì)象*/ function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers, proxyMap) { //如果target不是對(duì)象則提示并返回 /*這里會(huì)跳轉(zhuǎn)到如下方法 判斷是否原始值是否為object類(lèi)型 const isObject = (val) => val !== null && typeof val === 'object'; */ if (!isObject(target)) { if ((process.env.NODE_ENV !== 'production')) { console.warn(`value cannot be made reactive: ${String(target)}`); } return target; } // 如果target已經(jīng)是proxy是代理對(duì)象則直接返回. if (target["__v_raw" /* ReactiveFlags.RAW */] && !(isReadonly && target["__v_isReactive" /* ReactiveFlags.IS_REACTIVE */])) { return target; } // 從proxyMap中獲取緩存的proxy對(duì)象,如果存在的話(huà),直接返回proxyMap中對(duì)應(yīng)的proxy。否則創(chuàng)建proxy。 const existingProxy = proxyMap.get(target); if (existingProxy) { return existingProxy; } // 并不是任何對(duì)象都可以被proxy所代理。這里會(huì)通過(guò)getTargetType方法來(lái)進(jìn)行判斷。 const targetType = getTargetType(target); //當(dāng)類(lèi)型值判斷出是不能代理的類(lèi)型則直接返回 if (targetType === 0 /* TargetType.INVALID */) { return target; } //通過(guò)使用Proxy函數(shù)劫持target對(duì)象,返回的結(jié)果即為響應(yīng)式對(duì)象了。這里的處理函數(shù)會(huì)根據(jù)target對(duì)象不同而不同(這兩個(gè)函數(shù)都是參數(shù)傳入的): //Object或者Array的處理函數(shù)是collectionHandlers; //Map,Set,WeakMap,WeakSet的處理函數(shù)是baseHandlers; const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers); proxyMap.set(target, proxy); return proxy; }
getTargetType
方法調(diào)用流程
//1.進(jìn)入判斷如果value有__v_skip屬性且為true或?qū)ο笫强赏卣箘t返回0,否則走類(lèi)型判斷函數(shù) function getTargetType(value) { //Object.isExtensible() 方法判斷一個(gè)對(duì)象是否是可擴(kuò)展的(是否可以在它上面添加新的屬性)。 return value["__v_skip" /* ReactiveFlags.SKIP */] || !Object.isExtensible(value) ? 0 /* TargetType.INVALID */ : targetTypeMap(toRawType(value)); } //2.這里通過(guò)Object.prototype.toString.call(obj)來(lái)判斷數(shù)據(jù)類(lèi)型 const toRawType = (value) => { // extract "RawType" from strings like "[object RawType]" return toTypeString(value).slice(8, -1); }; const toTypeString = (value) => objectToString.call(value); //3.這里rawType是為'Object'所以會(huì)返回1 function targetTypeMap(rawType) { switch (rawType) { case 'Object': case 'Array': return 1 /* TargetType.COMMON */; case 'Map': case 'Set': case 'WeakMap': case 'WeakSet': return 2 /* TargetType.COLLECTION */; default: return 0 /* TargetType.INVALID */;//返回0說(shuō)明除前面的類(lèi)型外其他都不能被代理,如Date,RegExp,Promise等 } }
在createReactiveObject
方法中const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);
這一條語(yǔ)句中,第二個(gè)參數(shù)判斷target是否為Map或者Set類(lèi)型。從而使用不同的handler來(lái)進(jìn)行依賴(lài)收集。
在調(diào)試的文件node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js
中,我們從reactive
函數(shù)的createReactiveObject
函數(shù)調(diào)用的其中兩個(gè)參數(shù)mutableHandlers
和mutableCollectionHandlers
開(kāi)始往上查詢(xún)
mutableHandlers
的實(shí)現(xiàn)
const mutableHandlers = { get,// 獲取值的攔截,訪(fǎng)問(wèn)對(duì)象時(shí)會(huì)觸發(fā) set,// 更新值的攔截,設(shè)置對(duì)象屬性會(huì)觸發(fā) deleteProperty,// 刪除攔截,刪除對(duì)象屬性會(huì)觸發(fā) has,// 綁定訪(fǎng)問(wèn)對(duì)象時(shí)會(huì)攔截,in操作符會(huì)觸發(fā) ownKeys// 獲取屬性key列表 }; function deleteProperty(target, key) { // key是否是target自身的屬性 const hadKey = hasOwn(target, key); // 舊值 const oldValue = target[key]; // 調(diào)用Reflect.deleteProperty從target上刪除屬性 const result = Reflect.deleteProperty(target, key); // 如果刪除成功并且target自身有key,則觸發(fā)依賴(lài) if (result && hadKey) { trigger(target, "delete" /* TriggerOpTypes.DELETE */, key, undefined, oldValue); } return result; } // function has(target, key) { //檢查目標(biāo)對(duì)象是否存在此屬性。 const result = Reflect.has(target, key); // key不是symbol類(lèi)型或不是symbol的內(nèi)置屬性,進(jìn)行依賴(lài)收集 if (!isSymbol(key) || !builtInSymbols.has(key)) { track(target, "has" /* TrackOpTypes.HAS */, key); } return result; } /*ownKeys可以攔截以下操作: 1.Object.keys() 2.Object.getOwnPropertyNames() 3.Object.getOwnPropertySymbols() 4.Reflect.ownKeys()操作*/ function ownKeys(target) { track(target, "iterate" /* TrackOpTypes.ITERATE */, isArray(target) ? 'length' : ITERATE_KEY); return Reflect.ownKeys(target); }
get
方法實(shí)現(xiàn)
const get = /*#__PURE__*/ createGetter(); /*傳遞兩個(gè)參數(shù)默認(rèn)都為false isReadonly是否為只讀 shallow是否轉(zhuǎn)換為淺層響應(yīng),即Reactive---> shallowReactive,shallowReactive監(jiān)聽(tīng)了第一層屬性的值,一旦發(fā)生改變,則更新視圖;其他層,雖然值發(fā)生了改變,但是視圖不會(huì)進(jìn)行更新 */ function createGetter(isReadonly = false, shallow = false) { return function get(target, key, receiver) { //1.是否已被reactive相關(guān)api處理過(guò); if (key === "__v_isReactive" /* ReactiveFlags.IS_REACTIVE */) { return !isReadonly; } //2.是否被readonly相關(guān)api處理過(guò) else if (key === "__v_isReadonly" /* ReactiveFlags.IS_READONLY */) { return isReadonly; } else if (key === "__v_isShallow" /* ReactiveFlags.IS_SHALLOW */) { return shallow; } //3.檢測(cè)__v_raw屬性 else if (key === "__v_raw" /* ReactiveFlags.RAW */ && receiver === (isReadonly ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap).get(target)) { return target; } //4.如果target是數(shù)組,且命中了一些屬性,則執(zhí)行函數(shù)方法 const targetIsArray = isArray(target); if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) { return Reflect.get(arrayInstrumentations, key, receiver); } //5.Reflect獲取值 const res = Reflect.get(target, key, receiver); //6.判斷是否為特殊的屬性值 if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { return res; } if (!isReadonly) { track(target, "get" /* TrackOpTypes.GET */, key); } if (shallow) { return res; } //7.判斷是否為ref對(duì)象 if (isRef(res)) { // ref unwrapping - skip unwrap for Array + integer key. return targetIsArray && isIntegerKey(key) ? res : res.value; } //8.判斷是否為對(duì)象 if (isObject(res)) { // Convert returned value into a proxy as well. we do the isObject check // here to avoid invalid value warning. Also need to lazy access readonly // and reactive here to avoid circular dependency. return isReadonly ? readonly(res) : reactive(res); } return res; }; }
檢測(cè)__v_isReactive
屬性,如果為true,表示target已經(jīng)是一個(gè)響應(yīng)式對(duì)象了。
依次檢測(cè)__v_isReadonly
和__v_isShallow
屬性,判斷是否為只讀和淺層響應(yīng),如果是則返回對(duì)應(yīng)包裝過(guò)的target。
檢測(cè)__v_raw
屬性,這里是三元的嵌套,主要判斷原始數(shù)據(jù)是否為只讀或者淺層響應(yīng),然后在對(duì)應(yīng)的Map里面尋找是否有該目標(biāo)對(duì)象,如果都為true則說(shuō)明target已經(jīng)為響應(yīng)式對(duì)象。
如果target是數(shù)組,需要對(duì)一些方法(針對(duì)includes
、indexOf
、lastIndexOf
、push
、pop
、shift
、unshift
、splice
)進(jìn)行特殊處理。并對(duì)數(shù)組的每個(gè)元素執(zhí)行收集依賴(lài),然后通過(guò)Reflect獲取數(shù)組函數(shù)的值。
Reflect
獲取值。
判斷是否為特殊的屬性值,symbol
, __proto__
,__v_isRef
,__isVue
, 如果是直接返回前面得到的res
,不做后續(xù)處理;
如果為ref
對(duì)象,target
不是數(shù)組的情況下,會(huì)自動(dòng)解包。
如果res
是Object
,進(jìn)行深層響應(yīng)式處理。從這里就能看出,Proxy
是懶惰式的創(chuàng)建響應(yīng)式對(duì)象,只有訪(fǎng)問(wèn)對(duì)應(yīng)的key
,才會(huì)繼續(xù)創(chuàng)建響應(yīng)式對(duì)象,否則不用創(chuàng)建。
set
方法實(shí)現(xiàn)
例子:data.name='2'
const set = /*#__PURE__*/ createSetter(); //shallow是否轉(zhuǎn)換為淺層響應(yīng),默認(rèn)為false function createSetter(shallow = false) { //1.傳遞四個(gè)參數(shù) return function set(target, key, value, receiver) { let oldValue = target[key]; //首先獲取舊值,如果舊值是ref類(lèi)型,且新值不是ref類(lèi)型,則不允許修改 if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) { return false; } //2.根據(jù)傳遞的shallow參數(shù),來(lái)執(zhí)行之后的操作 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; } } //3.檢測(cè)key是不是target本身的屬性 const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key); //利用Reflect.set()來(lái)修改值,返回一個(gè)Boolean值表明是否成功設(shè)置屬性 //Reflect.set(設(shè)置屬性的目標(biāo)對(duì)象, 設(shè)置的屬性的名稱(chēng), 設(shè)置的值, 如果遇到 `setter`,`receiver`則為`setter`調(diào)用時(shí)的`this`值) const result = Reflect.set(target, key, value, receiver); // 如果目標(biāo)是原始原型鏈中的某個(gè)元素,則不要觸發(fā) if (target === toRaw(receiver)) { //如果不是target本身的屬性那么說(shuō)明執(zhí)行的是'add'操作,增加屬性 if (!hadKey) { trigger(target, "add" /* TriggerOpTypes.ADD */, key, value); } //4.比較新舊值,是否觸發(fā)依賴(lài) else if (hasChanged(value, oldValue)) { //5.觸發(fā)依賴(lài) trigger(target, "set" /* TriggerOpTypes.SET */, key, value, oldValue); } } return result; }; }
1、以data.name='2'
這段代碼為例,四個(gè)參數(shù)分別為:
target
:目標(biāo)對(duì)象,即target={"name": "測(cè)試","age": 10}
(此處為普通對(duì)象)
key
:修改的對(duì)應(yīng)key,即key: "name"
value
:修改的值,即value: "2"
receiver
:目標(biāo)對(duì)象的代理。即receiver=Proxy {"name": "測(cè)試","age": 10}
2、shallow
為false的時(shí)候。
第一個(gè)判斷:如果新值不是淺層響應(yīng)式并且不是readonly,新舊值取其對(duì)應(yīng)的原始值。
第二個(gè)判斷:如果target不是數(shù)組并且舊值是ref類(lèi)型,新值不是ref類(lèi)型,直接修改oldValue.value為value
3.檢測(cè)key
是不是target本身的屬性。這里的hadKey
有兩個(gè)方法,isArray
就不解釋?zhuān)褪桥袛嗍欠駷閿?shù)組
isIntegerKey
:判斷是不是數(shù)字型的字符串key值
//判斷參數(shù)是否為string類(lèi)型,是則返回true const isString = (val) => typeof val === 'string'; //如果參數(shù)是string類(lèi)型并且不是'NaN',且排除-值(排除負(fù)數(shù)),然后將 key 轉(zhuǎn)換成數(shù)字再隱式轉(zhuǎn)換為字符串,與原 key 對(duì)比 const isIntegerKey = (key) => isString(key) && key !== 'NaN' && key[0] !== '-' && '' + parseInt(key, 10) === key;
4.比較新舊值,如果新舊值不同,則觸發(fā)依賴(lài)進(jìn)行更新
hasChanged
方法
//Object.is()方法判斷兩個(gè)值是否是相同的值。 const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
5.觸發(fā)依賴(lài),這里太過(guò)復(fù)雜,筆者也沒(méi)搞懂,如果有興趣的讀者可自行去調(diào)試
<script setup> import { reactive } from "vue"; const data = reactive({ name: "測(cè)試", age: 10, }); data.name='1'//這里并未收集依賴(lài),在處理完 createSetupContext 的上下文后,組件會(huì)停止依賴(lài)收集,并且開(kāi)始執(zhí)行 setup 函數(shù)。具體原因有興趣的讀者可以自行去了解 const testClick = ()=>{ data.name='test' } </script> <template> <div> <h1>{{ data.name }}</h1> <el-button @click="testClick">Click</el-button> </div> </template> <style scoped></style>
基本數(shù)據(jù)類(lèi)型
const num = reactive(2)
這里比較簡(jiǎn)單,在createReactiveObject
函數(shù)方法里面:
if (!isObject(target)) { if ((process.env.NODE_ENV !== 'production')) { console.warn(`value cannot be made reactive: ${String(target)}`); } return target; }
因?yàn)榕袛囝?lèi)型不是對(duì)象,所以會(huì)在控制臺(tái)打印出警告,并且直接返回原數(shù)據(jù)
proxy對(duì)象
<script> const data = reactive({ name: "測(cè)試", age: 10, }); const num = reactive(data)//定義一個(gè)已經(jīng)是響應(yīng)式對(duì)象 </script>
1.調(diào)試開(kāi)始進(jìn)來(lái)reactive
函數(shù),然后會(huì)經(jīng)過(guò)isReadonly
函數(shù),這里跟前面不同的是,target是一個(gè)proxy對(duì)象,它已經(jīng)被代理過(guò)有set
,get
等handler。所以在isReadonly
函數(shù)讀取target
的時(shí)候,target
會(huì)進(jìn)行get
函數(shù)的讀取操作。
2.可以看到get
傳入的參數(shù)有個(gè)key="__v_isReadonly"
,這里的isReadonly
返回是false,接下來(lái)進(jìn)入createReactiveObject
函數(shù)
這里說(shuō)明下,在本次調(diào)試中常見(jiàn)的vue里面定義的私有屬性有:
__v_skip
:是否無(wú)效標(biāo)識(shí),用于跳過(guò)監(jiān)聽(tīng)__v_isReactive
:是否已被reactive相關(guān)api處理過(guò)__v_isReadonly
:是否被readonly相關(guān)api處理過(guò)__v_isShallow
:是否為淺層響應(yīng)式對(duì)象__v_raw
:當(dāng)前代理對(duì)象的源對(duì)象,即target
3.在createReactiveObject
函數(shù)中,經(jīng)過(guò)target["__v_isReactive"]
的時(shí)候會(huì)觸發(fā)target
的get函數(shù),這時(shí)候get
函數(shù)傳入的參數(shù)中key='__v_raw'
if (target["__v_raw" /* ReactiveFlags.RAW */] && !(isReadonly && target["__v_isReactive" /* ReactiveFlags.IS_REACTIVE */])) { return target; }
由上圖可知我們檢測(cè)target
即已定義過(guò)的proxy對(duì)象,被reactive
api處理過(guò)就會(huì)有__v_raw
私有屬性,然后再進(jìn)行receiver
的判斷,判斷target
是否為只讀或淺層響應(yīng)。如果都不是則從緩存proxy的WeakMap
對(duì)象中獲取該元素。最后直接返回target
的原始數(shù)據(jù)(未被proxy代理過(guò))。
最后回到之前的判斷,由下圖可知,target
的__v_raw
屬性存在,isReadonly
為false,__v_isReactive
的值為true,可以說(shuō)明reactive
函數(shù)需要處理的對(duì)象是一個(gè)被reactive
API處理過(guò)的對(duì)象,然后直接返回該對(duì)象的原始數(shù)據(jù)。
ref類(lèi)型
經(jīng)過(guò)ref
函數(shù)處理,其本質(zhì)也是一個(gè)對(duì)象,所以使用reactive
函數(shù)處理ref
類(lèi)型就跟處理復(fù)雜數(shù)據(jù)類(lèi)型一樣過(guò)程。有些內(nèi)容跟這里差不多,也有對(duì)此補(bǔ)充,如果覺(jué)得不錯(cuò)請(qǐng)各位幫忙點(diǎn)個(gè)贊
(開(kāi)發(fā)中應(yīng)該不會(huì)有這種嵌套行為吧,這里只是為了測(cè)試多樣化)。
<script setup> import { reactive,ref } from "vue"; const data = reactive({ name: "測(cè)試", age: 10, }); const numRef = ref(1) const dataRef = ref({ name: "測(cè)試2", age: 20, }) const num = reactive(numRef) const dataReactive = reactive(dataRef) console.log('data',data) console.log('numRef',numRef) console.log('num',num) console.log('dataRef',dataRef) console.log('dataReactive',dataReactive) </script>
Map類(lèi)型和Set類(lèi)型
Map
類(lèi)型是鍵值對(duì)的有序列表,而鍵和值都可以是任意類(lèi)型。
Set
和Map
類(lèi)似,也是一組key的集合,但不存儲(chǔ)value。由于key不能重復(fù),所以,在Set
中,沒(méi)有重復(fù)的key。
<script setup> import { reactive } from "vue"; const mapData = new Map(); mapData.set('name','張三') const setData = new Set([1,2,3,1,1]) console.log(mapData) console.log(setData) const mapReactive = reactive(mapData) console.log(mapReactive) </script>
由上圖可知Map結(jié)構(gòu)和Set結(jié)構(gòu)使用typeof
判斷是object
,所有流程前面會(huì)跟復(fù)雜數(shù)據(jù)類(lèi)型一樣,知道在createReactiveObject
函數(shù)的getTargetType()
函數(shù)開(kāi)始不同。
在getTargetType
函數(shù)里面toRawType()
判斷數(shù)據(jù)類(lèi)型所用方法為Object.prototype.toString.call()
const targetType = getTargetType(target); function getTargetType(value) { return value["__v_skip" /* ReactiveFlags.SKIP */] || !Object.isExtensible(value) ? 0 /* TargetType.INVALID */ : targetTypeMap(toRawType(value)); } function targetTypeMap(rawType) {//rawType="Map",這里返回值為2 switch (rawType) { case 'Object': case 'Array': return 1 /* TargetType.COMMON */; case 'Map': case 'Set': case 'WeakMap': case 'WeakSet': return 2 /* TargetType.COLLECTION */; default: return 0 /* TargetType.INVALID */; } }
這時(shí)候targetType=2
,在createReactiveObject
的函數(shù)中const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);
的三元表達(dá)式中可得知,這里的handler
為collectionHandlers
。
網(wǎng)上查找可在reactive
函數(shù)中return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
這條語(yǔ)句找到,當(dāng)rawType=1
時(shí)handler
是用mutableHandlers
,rawType=1
時(shí)是用mutableCollectionHandlers
。
mutableCollectionHandlers
方法:
const mutableCollectionHandlers = { get: /*#__PURE__*/ createInstrumentationGetter(false, false) }; //解構(gòu)createInstrumentations const [mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations] = /* #__PURE__*/ createInstrumentations(); //傳入兩個(gè)參數(shù),是否為可讀,是否為淺層響應(yīng) function createInstrumentationGetter(isReadonly, shallow) { const instrumentations = shallow ? isReadonly ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly ? readonlyInstrumentations : mutableInstrumentations; return (target, key, receiver) => { if (key === "__v_isReactive" /* ReactiveFlags.IS_REACTIVE */) { return !isReadonly; } else if (key === "__v_isReadonly" /* ReactiveFlags.IS_READONLY */) { return isReadonly; } else if (key === "__v_raw" /* ReactiveFlags.RAW */) { return target; } return Reflect.get(hasOwn(instrumentations, key) && key in target ? instrumentations : target, key, receiver); }; }
//篇幅問(wèn)題以及這方面筆者并未深入,所以就大概帶過(guò) function createInstrumentations() { //創(chuàng)建了四個(gè)對(duì)象,對(duì)象內(nèi)部有很多方法,其他去掉了,完整可自行去調(diào)試查看 const mutableInstrumentations = { get(key) { return get$1(this, key); }, get size() { return size(this); }, has: has$1, add, set: set$1, delete: deleteEntry, clear, forEach: createForEach(false, false) }; ................. //通過(guò)createIterableMethod方法操作keys、values、entries、Symbol.iterator迭代器方法 const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]; iteratorMethods.forEach(method => { mutableInstrumentations[method] = createIterableMethod(method, false, false); readonlyInstrumentations[method] = createIterableMethod(method, true, false); shallowInstrumentations[method] = createIterableMethod(method, false, true); shallowReadonlyInstrumentations[method] = createIterableMethod(method, true, true); }); return [ mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations ]; }
后續(xù)比較復(fù)雜,加上筆者技術(shù)力還不夠,暫時(shí)先到這里吧
總結(jié):關(guān)于reactive
的源碼調(diào)試就到這了,這只是其中一小部分的源碼,希望有興趣的讀者可以以此深入
到此這篇關(guān)于深入理解vue3中的reactive()的文章就介紹到這了,更多相關(guān)vue3 reactive()內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+ElementUI實(shí)現(xiàn)訂單頁(yè)動(dòng)態(tài)添加產(chǎn)品數(shù)據(jù)效果實(shí)例代碼
本篇文章主要介紹了vue+ElementUI實(shí)現(xiàn)訂單頁(yè)動(dòng)態(tài)添加產(chǎn)品效果實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07vue component 中引入less文件報(bào)錯(cuò) Module build failed
這篇文章主要介紹了vue component 中引入less文件報(bào)錯(cuò) Module build failed的解決方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04Vue重要修飾符.sync對(duì)比v-model的區(qū)別及使用詳解
這篇文章主要為大家介紹了Vue中重要修飾符.sync與v-model的區(qū)別對(duì)比及使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07VUE + UEditor 單圖片跨域上傳功能的實(shí)現(xiàn)方法
這篇文章主要介紹了VUE + UEditor 單圖片跨域上傳功能的實(shí)現(xiàn)方法,需要的朋友參考下2018-02-02electron-vite新一代electron開(kāi)發(fā)構(gòu)建工具
這篇文章主要介紹了electron-vite新一代electron開(kāi)發(fā)構(gòu)建工具,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04Vue項(xiàng)目如何獲取本地文件夾絕對(duì)路徑
這篇文章主要介紹了Vue項(xiàng)目如何獲取本地文件夾絕對(duì)路徑問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01如何在Vue.js項(xiàng)目中使用Jest進(jìn)行單元測(cè)試
在眾多測(cè)試框架中,Jest?因其易用性、強(qiáng)大功能以及與?Vue.js?的良好兼容性,成為了許多開(kāi)發(fā)者的首選,本文將詳細(xì)介紹如何在?Vue.js?項(xiàng)目中使用?Jest?進(jìn)行單元測(cè)試,需要的可以參考下2024-11-11