Heim > Web-Frontend > View.js > Hauptteil

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

青灯夜游
Freigeben: 2023-01-06 21:21:13
Original
2841 Leute haben es durchsucht

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

Bei der Entwicklung von vue3 bietet reactive eine Methode zur Implementierung reaktionsfähiger Daten. Dies ist eine häufig verwendete API in der täglichen Entwicklung. In diesem Artikel wird der Autor seinen internen Betriebsmechanismus untersuchen. Ich bin ein Neuling, also verzeihen Sie mir bitte meine schlechte Schreibweise.

Die Debug-Version ist 3.2.453.2.45

  • 什么是reactive?

    reactive是Vue3中提供实现响应式数据的方法.

    在Vue2中响应式数据是通过defineProperty来实现的.

    而在Vue3响应式数据是通过ES6的Proxy来实现的

  • reactive注意点

    reactive参数必须是对象(json/arr)

    如果给reactive传递了其他对象,默认情况下修改对象,界面不会自动更新,如果想更新,可以通过重新赋值的方式。【相关推荐:vuejs视频教程web前端开发

<script setup>
import {reactive} from &#39;vue&#39;
const data = reactive({ //定义对象
  name:&#39;测试&#39;,
  age:10
})
const num = reactive(1)//定义基本数据类型
console.log(data)//便于定位到调试位置
</script>
<template>
<div>
<h1>{{ data.name }}</h1>
</div>
</template>

<style scoped></style>
Nach dem Login kopieren

设置断点

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

开始调试

接下来我们可以开始调试了,设置好断点后,只要重新刷新页面就可以进入调试界面。

复杂数据类型

我们先调试简单的基本数据类型

Lassen Sie uns ausführlich über reactive() in vue3 sprechen1.

/*1.初始进来函数,判断目标对象target是否为只读对象,如果是直接返回*/
function reactive(target) {
    // if trying to observe a readonly proxy, return the readonly version.
    if (isReadonly(target)) {
        return target;
    }
    //创建一个reactive对象,五个参数后续会讲解
    return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
}


/*2.判断是来判断target是否为只读。*/
function isReadonly(value) {
    return !!(value && value["__v_isReadonly" /* ReactiveFlags.IS_READONLY */]);
}

/*3.创建一个reactive对象*/
/*createReactiveObject接收五个参数:
target被代理的对象,
isReadonl是不是只读的,
baseHandlers proxy的捕获器,
collectionHandlers针对集合的proxy捕获器,
proxyMap一个用于缓存proxy的`WeakMap`对象*/
function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers, proxyMap) {
//如果target不是对象则提示并返回
/*这里会跳转到如下方法
判断是否原始值是否为object类型 
const isObject = (val) => val !== null && typeof val === &#39;object&#39;;
*/
    if (!isObject(target)) {
        if ((process.env.NODE_ENV !== &#39;production&#39;)) {
            console.warn(`value cannot be made reactive: ${String(target)}`);
        }
        return target;
    }
    // 如果target已经是proxy是代理对象则直接返回.
    if (target["__v_raw" /* ReactiveFlags.RAW */] &&
        !(isReadonly && target["__v_isReactive" /* ReactiveFlags.IS_REACTIVE */])) {
        return target;
    }
    // 从proxyMap中获取缓存的proxy对象,如果存在的话,直接返回proxyMap中对应的proxy。否则创建proxy。
    const existingProxy = proxyMap.get(target);
    if (existingProxy) {
        return existingProxy;
    }
    // 并不是任何对象都可以被proxy所代理。这里会通过getTargetType方法来进行判断。
    const targetType = getTargetType(target);
    //当类型值判断出是不能代理的类型则直接返回
    if (targetType === 0 /* TargetType.INVALID */) {
        return target;
    }
    //通过使用Proxy函数劫持target对象,返回的结果即为响应式对象了。这里的处理函数会根据target对象不同而不同(这两个函数都是参数传入的):
    //Object或者Array的处理函数是collectionHandlers;
    //Map,Set,WeakMap,WeakSet的处理函数是baseHandlers;
    const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);
    proxyMap.set(target, proxy);
    return proxy;
}
Nach dem Login kopieren

getTargetType方法调用流程

//1.进入判断如果value有__v_skip属性且为true或对象是可拓展则返回0,否则走类型判断函数
function getTargetType(value) {
//Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
    return value["__v_skip" /* ReactiveFlags.SKIP */] || !Object.isExtensible(value)
        ? 0 /* TargetType.INVALID */
        : targetTypeMap(toRawType(value));
}
//2.这里通过Object.prototype.toString.call(obj)来判断数据类型
const toRawType = (value) => {
    // extract "RawType" from strings like "[object RawType]"
    return toTypeString(value).slice(8, -1);
};
const toTypeString = (value) => objectToString.call(value);
//3.这里rawType是为&#39;Object&#39;所以会返回1
function targetTypeMap(rawType) {
    switch (rawType) {
        case &#39;Object&#39;:
        case &#39;Array&#39;:
            return 1 /* TargetType.COMMON */;
        case &#39;Map&#39;:
        case &#39;Set&#39;:
        case &#39;WeakMap&#39;:
        case &#39;WeakSet&#39;:
            return 2 /* TargetType.COLLECTION */;
        default:
            return 0 /* TargetType.INVALID */;//返回0说明除前面的类型外其他都不能被代理,如Date,RegExp,Promise等
    }
}
Nach dem Login kopieren

createReactiveObject方法中const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);这一条语句中,第二个参数判断target是否为Map或者Set类型。从而使用不同的handler来进行依赖收集。

在调试的文件node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js中,我们从reactive函数的createReactiveObject函数调用的其中两个参数mutableHandlersmutableCollectionHandlers开始往上查询

mutableHandlers的实现

const mutableHandlers = {
    get,// 获取值的拦截,访问对象时会触发
    set,// 更新值的拦截,设置对象属性会触发
    deleteProperty,// 删除拦截,删除对象属性会触发
    has,// 绑定访问对象时会拦截,in操作符会触发
    ownKeys// 获取属性key列表
};

function deleteProperty(target, key) {
    // key是否是target自身的属性
    const hadKey = hasOwn(target, key);
    // 旧值
    const oldValue = target[key];
    // 调用Reflect.deleteProperty从target上删除属性
    const result = Reflect.deleteProperty(target, key);
    // 如果删除成功并且target自身有key,则触发依赖
    if (result && hadKey) {
        trigger(target, "delete" /* TriggerOpTypes.DELETE */, key, undefined, oldValue);
    }
    return result;
}
//
function has(target, key) {
    //检查目标对象是否存在此属性。
    const result = Reflect.has(target, key);
    // key不是symbol类型或不是symbol的内置属性,进行依赖收集
    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) ? &#39;length&#39; : ITERATE_KEY);
    return Reflect.ownKeys(target);
}
Nach dem Login kopieren
get方法实现
const get = /*#__PURE__*/ createGetter();
/*传递两个参数默认都为false
isReadonly是否为只读
shallow是否转换为浅层响应,即Reactive---> shallowReactive,shallowReactive监听了第一层属性的值,一旦发生改变,则更新视图;其他层,虽然值发生了改变,但是视图不会进行更新
*/
function createGetter(isReadonly = false, shallow = false) {
    return function get(target, key, receiver) {
    //1.是否已被reactive相关api处理过;
        if (key === "__v_isReactive" /* ReactiveFlags.IS_REACTIVE */) {
            return !isReadonly;
        }
     //2.是否被readonly相关api处理过
        else if (key === "__v_isReadonly" /* ReactiveFlags.IS_READONLY */) {
            return isReadonly;
        }
        else if (key === "__v_isShallow" /* ReactiveFlags.IS_SHALLOW */) {
            return shallow;
        }
      //3.检测__v_raw属性
        else if (key === "__v_raw" /* ReactiveFlags.RAW */ &&
            receiver ===
                (isReadonly
                    ? shallow
                        ? shallowReadonlyMap
                        : readonlyMap
                    : shallow
                        ? shallowReactiveMap
                        : reactiveMap).get(target)) {
            return target;
        }
      //4.如果target是数组,且命中了一些属性,则执行函数方法
        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对象
        if (isRef(res)) {
            // ref unwrapping - skip unwrap for Array + integer key.
            return targetIsArray && isIntegerKey(key) ? res : res.value;
        }
      //8.判断是否为对象
        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;
    };
}
Nach dem Login kopieren
  • 检测__v_isReactive属性,如果为true,表示target已经是一个响应式对象了。

  • 依次检测__v_isReadonly__v_isShallow属性,判断是否为只读和浅层响应,如果是则返回对应包装过的target。

  • 检测__v_raw属性,这里是三元的嵌套,主要判断原始数据是否为只读或者浅层响应,然后在对应的Map里面寻找是否有该目标对象,如果都为true则说明target已经为响应式对象。

  • 如果target是数组,需要对一些方法(针对includesindexOflastIndexOfpushpopshiftunshiftsplice)进行特殊处理。并对数组的每个元素执行收集依赖,然后通过Reflect获取数组函数的值。

  • Reflect获取值。

  • 判断是否为特殊的属性值,symbol, __proto____v_isRef__isVue, 如果是直接返回前面得到的res,不做后续处理;

  • 如果为ref对象,target不是数组的情况下,会自动解包。

  • 如果resObject,进行深层响应式处理。从这里就能看出,Proxy是懒惰式的创建响应式对象,只有访问对应的key,才会继续创建响应式对象,否则不用创建。

set方法实现

例子:data.name=&#39;2&#39;

const set = /*#__PURE__*/ createSetter();
  //shallow是否转换为浅层响应,默认为false
function createSetter(shallow = false) {
    //1.传递四个参数
    return function set(target, key, value, receiver) {
        let oldValue = target[key];
        //首先获取旧值,如果旧值是ref类型,且新值不是ref类型,则不允许修改
        if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
            return false;
        }
     //2.根据传递的shallow参数,来执行之后的操作
        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.检测key是不是target本身的属性
        const hadKey = isArray(target) && isIntegerKey(key)
            ? Number(key) < target.length
            : hasOwn(target, key);
        //利用Reflect.set()来修改值,返回一个Boolean值表明是否成功设置属性
        //Reflect.set(设置属性的目标对象, 设置的属性的名称, 设置的值, 如果遇到 `setter`,`receiver`则为`setter`调用时的`this`值)
        const result = Reflect.set(target, key, value, receiver);
        // 如果目标是原始原型链中的某个元素,则不要触发
        if (target === toRaw(receiver)) {
        //如果不是target本身的属性那么说明执行的是&#39;add&#39;操作,增加属性
            if (!hadKey) {
                trigger(target, "add" /* TriggerOpTypes.ADD */, key, value);
            }
      //4.比较新旧值,是否触发依赖
            else if (hasChanged(value, oldValue)) {
      //5.触发依赖
                trigger(target, "set" /* TriggerOpTypes.SET */, key, value, oldValue);
            }
        }
        return result;
    };
}
Nach dem Login kopieren

1、以data.name=&#39;2&#39;这段代码为例,四个参数分别为:

target:目标对象,即target={"name": "测试","age": 10}(此处为普通对象)

key:修改的对应key,即key: "name"

value:修改的值,即value: "2"

receiver:目标对象的代理。即receiver=Proxy {"name": "测试","age": 10}

  • Was ist reaktiv?🎜🎜reaktiv ist eine reaktionsfähige Implementierung, die in bereitgestellt wird Vue3-Datenmethode.🎜🎜In Vue2 werden reaktionsfähige Daten durch defineProperty implementiert.🎜🎜Und in Vue3 werden reaktionsfähige Daten durch den Proxy🎜
  • 🎜Hinweise zu Reactive🎜🎜Der Reactive-Parameter muss Object (json/arr) sein.🎜🎜Wenn andere Objekte an Reactive übergeben werden, wird das Objekt standardmäßig geändert und die Schnittstelle wird nicht automatisch aktualisiert. Wenn Sie aktualisieren möchten, können Sie es neu zuweisen. [Verwandte Empfehlungen: vuejs Video-Tutorial, Web-Frontend-Entwicklung]🎜
//判断参数是否为string类型,是则返回true
const isString = (val) => typeof val === &#39;string&#39;;
//如果参数是string类型并且不是&#39;NaN&#39;,且排除-值(排除负数),然后将 key 转换成数字再隐式转换为字符串,与原 key 对比
const isIntegerKey = (key) => isString(key) &&
    key !== &#39;NaN&#39; &&
    key[0] !== &#39;-&#39; &&
    &#39;&#39; + parseInt(key, 10) === key;
Nach dem Login kopieren
Nach dem Login kopieren

Haltepunkt setzen

🎜 Bild .png🎜🎜Lassen Sie uns ausführlich über reactive() in vue3 sprechen 🎜

Debuggen starten

🎜Als nächstes können wir mit dem Debuggen beginnen. Nachdem Sie den Haltepunkt festgelegt haben, aktualisieren Sie einfach die Seite, um die Debugging-Schnittstelle aufzurufen. 🎜

Komplexe Datentypen

🎜Lass uns zuerst einfache Basisdatentypen debuggen🎜🎜Lassen Sie uns ausführlich über reactive() in vue3 sprechen1.🎜
//Object.is()方法判断两个值是否是相同的值。
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
Nach dem Login kopieren
Nach dem Login kopieren
🎜getTargetType Methode, die den Prozess🎜
<script setup>
import { reactive } from "vue";
const data = reactive({
  name: "测试",
  age: 10,
});

data.name=&#39;1&#39;//这里并未收集依赖,在处理完 createSetupContext 的上下文后,组件会停止依赖收集,并且开始执行 setup 函数。具体原因有兴趣的读者可以自行去了解
const testClick = ()=>{
  data.name=&#39;test&#39;
}
</script>
<template>
  <div>
    <h1>{{ data.name }}</h1>
    <el-button @click="testClick">Click</el-button>
  </div>
</template>

<style scoped></style>
Nach dem Login kopieren
Nach dem Login kopieren
🎜increateReactiveObject < aufruft /code>In der Methode <code>const Proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */? collectionHandlers : baseHandlers); In dieser Anweisung bestimmt der zweite Parameter das Ziel Ob Es handelt sich um den Typ „Karte“ oder „Set“. Daher werden unterschiedliche Handler für die Sammlung von Abhängigkeiten verwendet. 🎜🎜In der debuggten Datei node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js beginnen wir mit dem createReactiveObject des reactive</ code> function >Zwei Parameter des Funktionsaufrufs, <code>mutableHandlers und mutableCollectionHandlers, beginnen mit der Abfrage nach oben🎜

mutableHandlers Implementierung der

if (!isObject(target)) {
        if ((process.env.NODE_ENV !== &#39;production&#39;)) {
            console.warn(`value cannot be made reactive: ${String(target)}`);
        }
        return target;
    }
Nach dem Login kopieren
Nach dem Login kopieren
get-Methodenimplementierung
<script>
const data = reactive({
  name: "测试",
  age: 10,
});
const num = reactive(data)//定义一个已经是响应式对象
</script>
Nach dem Login kopieren
Nach dem Login kopieren
    < li>🎜Erkennen Sie das Attribut __v_isReactive. Wenn es wahr ist, bedeutet dies, dass das Ziel bereits ein reaktives Objekt ist. 🎜
  • 🎜Überprüfen Sie die Attribute __v_isReadonly und __v_isShallow nacheinander, um festzustellen, ob es sich um eine schreibgeschützte und flache Antwort handelt. Wenn ja, geben Sie die entsprechende zurück eingewickeltes Ziel. 🎜
  • 🎜Erkennen Sie das Attribut __v_raw. Hier erfolgt die Verschachtelung von Ternärdaten, hauptsächlich um festzustellen, ob die Originaldaten schreibgeschützt oder sind flache Antwort und suchen Sie dann in der entsprechenden Map nach dem Zielobjekt. Wenn beides zutrifft, bedeutet dies, dass das Ziel bereits ein reaktionsfähiges Objekt ist. 🎜
  • 🎜Wenn target ein Array ist, können einige Methoden (für includes, indexOf, lastIndexOf< / code>, <code>push, pop, shift, unshift, splice) Besondere Behandlung . Führen Sie Sammlungsabhängigkeiten für jedes Element des Arrays durch und ermitteln Sie dann den Wert der Array-Funktion über Reflect. 🎜
  • 🎜ReflectDen Wert abrufen. 🎜
  • 🎜 Bestimmen Sie, ob es sich um einen speziellen Attributwert, symbol, __proto__, __v_isRef, __isVue< handelt /code code>, wenn die zuvor erhaltene <code>res direkt ohne anschließende Verarbeitung zurückgegeben wird 🎜
  • 🎜Wenn es sich um ein ref-Objekt handelt, target< Wenn /code> kein Array ist, wird es automatisch entpackt. 🎜</li><li>🎜Wenn <code>res Object ist, führen Sie eine tiefgreifende reaktionsfähige Verarbeitung durch. Daraus ist ersichtlich, dass Proxy reaktionsfähige Objekte träge erstellt. Erst wenn auf den entsprechenden Schlüssel zugegriffen wird, werden weiterhin reaktionsfähige Objekte erstellt, andernfalls besteht keine Notwendigkeit, diese zu erstellen . 🎜
set Methodenimplementierung
🎜Beispiel: data.name='2'</ code>🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">function reactive(target) { // if trying to observe a readonly proxy, return the readonly version. if (isReadonly(target)) { return target; } return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap); }</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>🎜1. Nehmen Sie als Beispiel <code>data.name='2'. Die vier Parameter sind: 🎜🎜target: Zielobjekt, also target={"name": "Test", "age": 10(Dies ist ein normales Objekt)🎜🎜key: Der entsprechende zu ändernde Schlüssel, d. h. key: „name“🎜🎜value: geänderter Wert, also value: „2“🎜🎜receiver</code >: Zielobjektagent. Das heißt, <code>receiver=Proxy {"name": "test", "age": 10}🎜

2、shallow为false的时候。

第一个判断:如果新值不是浅层响应式并且不是readonly,新旧值取其对应的原始值。

第二个判断:如果target不是数组并且旧值是ref类型,新值不是ref类型,直接修改oldValue.value为value

3.检测key是不是target本身的属性。这里的hadKey有两个方法,isArray就不解释,就是判断是否为数组

isIntegerKey:判断是不是数字型的字符串key值

//判断参数是否为string类型,是则返回true
const isString = (val) => typeof val === &#39;string&#39;;
//如果参数是string类型并且不是&#39;NaN&#39;,且排除-值(排除负数),然后将 key 转换成数字再隐式转换为字符串,与原 key 对比
const isIntegerKey = (key) => isString(key) &&
    key !== &#39;NaN&#39; &&
    key[0] !== &#39;-&#39; &&
    &#39;&#39; + parseInt(key, 10) === key;
Nach dem Login kopieren
Nach dem Login kopieren

4.比较新旧值,如果新旧值不同,则触发依赖进行更新

hasChanged方法

//Object.is()方法判断两个值是否是相同的值。
const hasChanged = (value, oldValue) => !Object.is(value, oldValue);
Nach dem Login kopieren
Nach dem Login kopieren

5.触发依赖,这里太过复杂,笔者也没搞懂,如果有兴趣的读者可自行去调试

<script setup>
import { reactive } from "vue";
const data = reactive({
  name: "测试",
  age: 10,
});

data.name=&#39;1&#39;//这里并未收集依赖,在处理完 createSetupContext 的上下文后,组件会停止依赖收集,并且开始执行 setup 函数。具体原因有兴趣的读者可以自行去了解
const testClick = ()=>{
  data.name=&#39;test&#39;
}
</script>
<template>
  <div>
    <h1>{{ data.name }}</h1>
    <el-button @click="testClick">Click</el-button>
  </div>
</template>

<style scoped></style>
Nach dem Login kopieren
Nach dem Login kopieren

基本数据类型

const num = reactive(2)

这里比较简单,在createReactiveObject函数方法里面:

if (!isObject(target)) {
        if ((process.env.NODE_ENV !== &#39;production&#39;)) {
            console.warn(`value cannot be made reactive: ${String(target)}`);
        }
        return target;
    }
Nach dem Login kopieren
Nach dem Login kopieren

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

因为判断类型不是对象,所以会在控制台打印出警告,并且直接返回原数据

proxy对象

<script>
const data = reactive({
  name: "测试",
  age: 10,
});
const num = reactive(data)//定义一个已经是响应式对象
</script>
Nach dem Login kopieren
Nach dem Login kopieren

1.调试开始进来reactive函数,然后会经过isReadonly函数,这里跟前面不同的是,target是一个proxy对象,它已经被代理过有set,get等handler。所以在isReadonly函数读取target的时候,target会进行get函数的读取操作。

function reactive(target) {
    // if trying to observe a readonly proxy, return the readonly version.
    if (isReadonly(target)) {
        return target;
    }
    return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);
}
Nach dem Login kopieren
Nach dem Login kopieren

2.可以看到get传入的参数有个key="__v_isReadonly",这里的isReadonly返回是false,接下来进入createReactiveObject函数

这里说明下,在本次调试中常见的vue里面定义的私有属性有:

  • __v_skip:是否无效标识,用于跳过监听
  • __v_isReactive:是否已被reactive相关api处理过
  • __v_isReadonly:是否被readonly相关api处理过
  • __v_isShallow:是否为浅层响应式对象
  • __v_raw:当前代理对象的源对象,即target

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

3.在createReactiveObject函数中,经过target["__v_isReactive"]的时候会触发target的get函数,这时候get函数传入的参数中key=&#39;__v_raw&#39;

    if (target["__v_raw" /* ReactiveFlags.RAW */] &&
        !(isReadonly && target["__v_isReactive" /* ReactiveFlags.IS_REACTIVE */])) {
                return target;
    }
Nach dem Login kopieren

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

由上图可知我们检测target即已定义过的proxy对象,被reactiveapi处理过就会有__v_raw私有属性,然后再进行receiver的判断,判断target是否为只读或浅层响应。如果都不是则从缓存proxy的WeakMap对象中获取该元素。最后直接返回target的原始数据(未被proxy代理过)。

最后回到之前的判断,由下图可知,target__v_raw属性存在,isReadonly为false,__v_isReactive的值为true,可以说明reactive函数需要处理的对象是一个被reactiveAPI处理过的对象,然后直接返回该对象的原始数据。

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

ref类型

经过ref函数处理,其本质也是一个对象,所以使用reactive函数处理ref类型就跟处理复杂数据类型一样过程。对于ref函数,如果大家有兴趣可以阅读这篇文章vue3——深入了解ref()。有些内容跟这里差不多,也有对此补充,如果觉得不错请各位帮忙点个赞

(开发中应该不会有这种嵌套行为吧,这里只是为了测试多样化)。

<script setup>
import { reactive,ref } from "vue";
const data = reactive({
  name: "测试",
  age: 10,
});
const numRef = ref(1)
const dataRef = ref({
  name: "测试2",
  age: 20,
})
const num = reactive(numRef)
const dataReactive = reactive(dataRef)
console.log(&#39;data&#39;,data)
console.log(&#39;numRef&#39;,numRef)
console.log(&#39;num&#39;,num)
console.log(&#39;dataRef&#39;,dataRef)
console.log(&#39;dataReactive&#39;,dataReactive)
</script>
Nach dem Login kopieren

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

Map类型和Set类型

  • Map 类型是键值对的有序列表,而键和值都可以是任意类型。
  • SetMap类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
<script setup>
import { reactive } from "vue";
const mapData = new Map();
mapData.set(&#39;name&#39;,&#39;张三&#39;)
const setData = new Set([1,2,3,1,1])
console.log(mapData)
console.log(setData)
const mapReactive = reactive(mapData)
console.log(mapReactive)
</script>
Nach dem Login kopieren

Lassen Sie uns ausführlich über reactive() in vue3 sprechen

由上图可知Map结构和Set结构使用typeof判断是object,所有流程前面会跟复杂数据类型一样,知道在createReactiveObject函数的getTargetType()函数开始不同。

getTargetType函数里面toRawType()判断数据类型所用方法为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 &#39;Object&#39;:
        case &#39;Array&#39;:
            return 1 /* TargetType.COMMON */;
        case &#39;Map&#39;:
        case &#39;Set&#39;:
        case &#39;WeakMap&#39;:
        case &#39;WeakSet&#39;:
            return 2 /* TargetType.COLLECTION */;
        default:
            return 0 /* TargetType.INVALID */;
    }
}
Nach dem Login kopieren

这时候targetType=2,在createReactiveObject的函数中const proxy = new Proxy(target, targetType === 2 /* TargetType.COLLECTION */ ? collectionHandlers : baseHandlers);的三元表达式中可得知,这里的handlercollectionHandlers

网上查找可在reactive函数中return createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap);这条语句找到,当rawType=1handler是用mutableHandlers,rawType=1时是用mutableCollectionHandlers

mutableCollectionHandlers方法:

const mutableCollectionHandlers = {
    get: /*#__PURE__*/ createInstrumentationGetter(false, false)
};

//解构createInstrumentations
const [mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations] = /* #__PURE__*/ createInstrumentations();
//传入两个参数,是否为可读,是否为浅层响应
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);
    };
}
Nach dem Login kopieren
//篇幅问题以及这方面笔者并未深入,所以就大概带过
function createInstrumentations() {
//创建了四个对象,对象内部有很多方法,其他去掉了,完整可自行去调试查看
    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)
    };
    
    .................
    
    //通过createIterableMethod方法操作keys、values、entries、Symbol.iterator迭代器方法
    const iteratorMethods = [&#39;keys&#39;, &#39;values&#39;, &#39;entries&#39;, 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
    ];
}
Nach dem Login kopieren

后续比较复杂,加上笔者技术力还不够,如果想继续深入的读者,可以阅读这篇文章:Vue3响应式原理

总结:关于reactive的源码调试就到这了,这只是其中一小部分的源码,希望有兴趣的读者可以以此深入,输出文章,共同进步成长。最后,如果这篇文章对你有所收获,请点个赞,如果有写的不对的地方,请大佬们指出(* ̄︶ ̄)。

(学习视频分享:vuejs入门教程编程基础视频

Das obige ist der detaillierte Inhalt vonLassen Sie uns ausführlich über reactive() in vue3 sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage