Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive

Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive

青灯夜游
青灯夜游nach vorne
2023-01-09 18:32:552258Durchsuche

Verglichen mit der von Vue2 implementierten Datenreaktionsfähigkeit verfügt Vue3 über eine klarere Arbeitsteilung bei der Datenantwortverarbeitung. Die Daten werden den Entwicklern in der kombinierten API zur Verfügung gestellt, wodurch eine Datenreaktionsfähigkeit erreicht wird der Unterschied? Lasst uns gemeinsam anhand von Beispielen lernen!

Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive

ref: Reaktionsfähigkeit, die grundlegende Datentypen und Referenzdatentypen definiert. Mit anderen Worten, ref(value), dieser Werttyp kann ein Basisdatentyp oder ein Referenzdatentyp sein, aber wenn er in js verwendet wird, muss er im Format property.value verwendet werden direkt in der Vorlage Anrufdaten verwendet werden. 定义基本数据类型、引用数据类型的响应式。也就是说ref(value),这个value类型可以是基本数据类型,也可以是引用数据类型,但是在js中使用时必须以属性.value格式使用,在template中可以直接调用数据。

<template>
  <div>
    <div><button @click="changeValue">修改</button></div>
    <div>
      <p>当前strRef:{{ strRef }}</p>
      <p>当前objRef:姓名:{{ objRef.name }} 爱好:{{ objRef.hobboy }}</p>
      <p>当前arrRef:{{ arrRef }}</p>
    </div>
  </div>
</template>
<script>
import { defineComponent, ref, shallowRef } from &#39;vue&#39;
export default defineComponent({
  setup () {
    const strRef = ref(&#39;sapper&#39;);// 基本数据类型
    const arrRef = ref([1, 3, 2]);// 数组类型
    const objRef = ref({  // 对象类型
      name: &#39;sapper&#39;,
      hobboy: [&#39;吉他&#39;, &#39;原神&#39;]
    })
    const changeValue = () => {
      strRef.value = &#39;工兵&#39;;
      arrRef.value[1] = 4;
      objRef.value.hobboy[1] = &#39;滑冰&#39;;
    }
    return {strRef,objRef,arrRef,changeValue}
  }
})
</script>

reactive定义引用类型数据的响应式,不支持基本数据类型,如果需要写基本数据类型只能是放在对象中,也就是说reactive(value),这个value类型必须是引用类型。【相关推荐:vuejs视频教程web前端开发

<template>
  <div>
    <div><button @click="changeValue">修改</button></div>
    <div>
      <div>当前objReactive:
        <br/>
        姓名:{{ objReactive.name }}<br/> 
        爱好:{{ objReactive.hobboy }}
      </div>
      <div>当前arrReactive:{{ arrReactive }}</div>
    </div>
  </div>
</template>
<script>
import { defineComponent, reactive } from &#39;vue&#39;
export default defineComponent({
  setup () {
    const arrReactive = reactive([1, 3, 2]);// 数组类型
    const objReactive = reactive({  // 对象类型
      name: &#39;sapper&#39;,
      hobboy: [&#39;吉他&#39;, &#39;原神&#39;]
    })
    const changeValue = () => {
      arrReactive[1] = 4;
      objReactive.name = &#39;工兵&#39;;
      objReactive.hobboy[1] = &#39;滑冰&#39;;
    }
    return {objReactive,arrReactive,changeValue}
  }
})
</script>

从上面两个例子中我们可以看出不管什么类型数据,ref都需要以.value来调用ref定义的数据,对于引用数据类型来看,我们可以看出代码不美观,所以一般对于引用类型数据,都推荐使用reactive来定义对于基本数据类型,可以使用ref也可以使用reactive来定义。既然到了这里我们都了解了ref和reactive的运用区别了,那么我们继续来一起探讨一下它们的响应原理又有什么区别?

揭秘ref

从上面的例子,我们先打印看一下基本数据类型(strRef)、引用数据类型(arrRef、ObjRef)的ref内部封装结构是什么样的?如下三图所示

Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive

Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive

Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive从上面图片可以看出,不管是什么类型的数据,对于ref封装数据都是一个RefImpl对象reference implement的简写,是引用实现的意思,每个RefImpl对象都有6个属性:

  • dep:是一个Set类型的数据,用来存储当前的ref值收集的依赖。

  • _ v _ isRef :标记位,只要被ref定义了,都会标识当前数据为一个Ref,也就是它的值标记为true。

  • _ v _ isShallow:判断是否是shallowRef定义的数据。

    与ref不同的是,当使用shallowRef为引用类型创建响应性时,修改深层属性,不具备响应性。只有对.value的引用时才触发。

    const state = shallowRef({ count: 1 })
    // 不会触发更改
    state.value.count = 2
    // 会触发更改
    state.value = { count: 2 }
  • _ rawValue:用于保存当前ref值对应的原始值,如果传递的参数是对象,它就是用于保存转化前的原始值,否则_ value与_ rawValue相同。

  • _ value:用于保存ref当前值,如果传递的参数是对象,它就是用于保存经过reactive函数转化后的值,否则_ value与_ rawValue相同。从上面例子我们可以发现,对于引用类型的数据,它的值就是一个proxy对象,这其实就是reactive封装数据后对象(后面会说)。我们先看一下下图,发现_ rawValue就是没有做响应性处理的原始值,在看看_ value是一个proxy对象就是做了reactive响应处理的值。

    _ rawValue_ value就是为了区分引用类型数据是否做响应式处理。

Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive

  • value:保存的是当前的值。

既然我们清楚了ref给数据封装了什么属性,接下来开始探讨源码究竟给怎么给上面6个属性进行赋值的:

  • ref函数:Vue3向开发者暴露的是ref函数,其实它就是封装了一个createRef函数。

    export function ref(value?: unknown) {
      return createRef(value, false)
    }
  • createRef函数:有两个参数,一个是要做响应处理的数据,一个是判断数据是否为shallowRef定义的数据

    function createRef(rawValue: unknown, shallow: boolean) {
      if (isRef(rawValue)) {
        return rawValue
      }
      return new RefImpl(rawValue, shallow)
    }

    🎜reaktiv🎜: Definieren Sie die Reaktion von Referenztypdaten, die keine Basisdatentypen unterstützen Wenn Sie Basisdatentypen schreiben müssen, können Sie diese nur in das Objekt einfügen , reaktiv(Wert), Dieser Werttyp muss ein Referenztyp sein. [Verwandte Empfehlungen: vuejs Video-Tutorial, Web-Frontend-Entwicklung]🎜
    const arrReactive = reactive([1, 3, 2]);// 数组类型
    const objReactive = reactive({  // 对象类型
      name: &#39;sapper&#39;,
      hobboy: [&#39;吉他&#39;, &#39;原神&#39;]
    })
    const changeValue = () => {
      arrReactive[1] = 4;
      objReactive.name = &#39;工兵&#39;;
      objReactive.hobboy[1] = &#39;滑冰&#39;;
      console.log(&#39;arrReactive&#39;,arrReactive);
      console.log(&#39;objReactive&#39;,objReactive);
    }
    🎜Anhand der beiden oben genannten Beispiele können wir das erkennen Egal welche Typdaten und Referenzen Sie benötigen, um .value zum Aufrufen der durch Referenzdatentypen definierten Daten zu verwenden. Wir können sehen, dass der Code nicht schön ist, also im Allgemeinen 🎜Für Referenztypdaten, Es wird empfohlen, reaktiv zu verwenden, um 🎜 zu definieren. Für grundlegende Datentypen können Sie ref oder reaktiv verwenden, um 🎜 zu definieren. Nachdem wir nun alle den Unterschied zwischen der Verwendung von ref und reactive hier verstanden haben, wollen wir die Unterschiede in ihren Antwortprinzipien weiter untersuchen. 🎜

    🎜Revealing ref🎜

    🎜Lassen Sie uns im obigen Beispiel zunächst die Ref-Interna des Basisdatentyps (strRef) und des Referenzdatentyps (arrRef) ausdrucken und betrachten , ObjRef) Wie sieht die Verpackungsstruktur aus? Wie in den folgenden drei Bildern gezeigt: 🎜🎜 faul" />🎜🎜Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive🎜 🎜Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und ReactiveVon der Im obigen Bild können Sie sehen, dass es sich bei den ref-gekapselten Daten um ein 🎜RefImpl-Objekt handelt. Die Abkürzung für Referenzimplementierung bedeutet, dass jedes RefImpl-Objekt 6 hat Attribute: 🎜
    • 🎜🎜dep🎜: Es handelt sich um Daten vom Typ „Set“, die zum Speichern der vom aktuellen Referenzwert erfassten Abhängigkeiten verwendet werden. 🎜
    • 🎜🎜_ v _ isRef🎜: Markieren Sie das Bit. Solange es durch ref definiert ist, werden die aktuellen Daten als Ref markiert, dh sein Wert wird als wahr markiert. 🎜
    • 🎜🎜_ v _ isShallow🎜: Bestimmen Sie, ob es sich um durch ShallowRef definierte Daten handelt. 🎜
      🎜Im Gegensatz zu ref werden tiefe Attribute geändert und es gibt keine Reaktionsfähigkeit, wenn flacheRef verwendet wird, um Reaktionsfähigkeit für einen Referenztyp zu erzeugen. Wird nur für Verweise auf .value ausgelöst. 🎜
      const houseOwner = {home:&#39;房源&#39;,price:1200,type:&#39;一房一厅&#39;};
      const proxyOwner = new Proxy(houseOwner,{
        get:function (target,key){
          console.log(`${key}属性被访问!`)
          return target[key];
        },
        set:function(target,key,value){
          if(target[key]===value){
            return;
          }
          target[key] = value;
          return target[key];
        },
      })
      console.log(proxyOwner);
      proxyOwner.price = 1300;// 对被代理对象的修改
      proxyOwner.remark = &#39;采光点好!&#39;;
      console.log(proxyOwner.price);// price属性被访问
    • 🎜🎜_ rawValue🎜: wird verwendet, um den ursprünglichen Wert entsprechend dem aktuellen Referenzwert zu speichern, wenn der übergebene Parameter ein Objekt , der <code>zum Speichern des ursprünglichen Werts vor der Konvertierung verwendet wird, andernfalls ist _ value dasselbe wie _ rawValue. 🎜
    • 🎜🎜_ Wert🎜: Wird zum Speichern des aktuellen Werts von ref verwendet. Wenn der übergebene Parameter ein Objekt ist, ist er Wird zum Speichern verwendet Der von der reaktiven Funktion konvertierte Wert, andernfalls ist _ value dasselbe wie _ rawValue. Aus dem obigen Beispiel können wir erkennen, dass der Wert von Referenztypdaten ein Proxy-Objekt ist, das nach der Kapselung der Daten tatsächlich das reaktive Objekt ist (später besprochen). Schauen wir uns zuerst das Bild unten an und stellen Sie fest, dass _ rawValue der ursprüngliche Wert ohne reaktionsfähige Verarbeitung ist. Dann schauen wir uns _ value an, ein Proxy-Objekt, das den Wert darstellt, der mit reaktiver Reaktion verarbeitet wurde. 🎜
      🎜🎜_ rawValue🎜 und 🎜_ value🎜 sollen unterscheiden, ob Referenztypdaten responsiv verarbeitet werden sollen. 🎜
    🎜image. png🎜
    • 🎜🎜value🎜: Der aktuelle Wert wird gespeichert. 🎜
    🎜Da wir nun wissen, welche Attribute ref für Daten kapselt, beginnen wir zu untersuchen, wie der Quellcode den oben genannten 6 Attributen Werte zuweist: 🎜
    • 🎜🎜ref-Funktion 🎜: Was Vue3 Entwicklern zur Verfügung stellt, ist die Ref-Funktion. Tatsächlich kapselt es eine createRef-Funktion. 🎜
      // 源码位置:core-main/packages/reactivity/src/reactive.ts
      // Vue3中暴露给开发者的是reactive方法
      export function reactive(target: object) {
        // 判断target是否只读,是就不做处理
        if (isReadonly(target)) {
          return target
        }
        return createReactiveObject(
          target,
          false,
          mutableHandlers,
          mutableCollectionHandlers,
          reactiveMap
        )
      }
    • 🎜🎜createRef-Funktion🎜: Es gibt zwei Parameter, einer sind die Daten, auf die zur Verarbeitung geantwortet wird, und der andere ist um zu bestimmen, ob die Daten verarbeitet werden Daten, die durch ShallowRef definiert werden. Hauptsächlich wird ermittelt, ob es sich bei dem aktuellen Rohwert (Daten, die noch nicht für die Antwort verarbeitet wurden) um Daten vom Typ „ref“ handelt, und es wird ein RefImpl-Instanzobjekt erstellt. 🎜<pre class="brush:js;toolbar:false;">function createRef(rawValue: unknown, shallow: boolean) { if (isRef(rawValue)) { return rawValue } return new RefImpl(rawValue, shallow) }</pre>
    • RefImpl类:创建RefImpl类给_ rawValue和_ value属性赋值,判断当前定义的ref数据是否为shallowRef定义的数据,然后获取响应性值时对数据依赖进行收集并返回_ value,修改响应式值时修改并通知依赖更新。

      ref定义的数据为什么需要带.value调用数据? 就是因为RefImpl类暴露给实例对象的get、set方法是value,所以在调用的时候,需要带上。

    Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive

    其实,RefImpl实例关键就在于trackRefValue(this)triggerRefValue(this, newVal)的两个函数的处理,我们大概也知道它们就是依赖收集、依赖更新。这里就不一一探讨。

    揭秘Reactive

    上面也说了reactive封装数据的用法,它只支持传入引用类型数据(数组、对象),如果需要在reactive中使用基础类型只能放在对象中。既然这样我们来探讨一下reactive函数究竟做了什么?

    const arrReactive = reactive([1, 3, 2]);// 数组类型
    const objReactive = reactive({  // 对象类型
      name: &#39;sapper&#39;,
      hobboy: [&#39;吉他&#39;, &#39;原神&#39;]
    })
    const changeValue = () => {
      arrReactive[1] = 4;
      objReactive.name = &#39;工兵&#39;;
      objReactive.hobboy[1] = &#39;滑冰&#39;;
      console.log(&#39;arrReactive&#39;,arrReactive);
      console.log(&#39;objReactive&#39;,objReactive);
    }

    Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive从上图可以看出,使用reactive封装的数据返回的都是一个proxy对象,proxy就是代理,如把一个对象代理到另一个对象,好比如房子所有者,代理房子给二手房东销售,二手房东就可以拥有房子销售权利。从上图我们可以看到Proxy对象有三个属性:

    • [[Handler]]: 创建Proxy对象传入的第二个参数,是对当前需要代理的目标target进行一些相关配置处理。

    • [[Target]]:需要代理的目标target,也就是被代理的目标。

    • [[IsRevoked]]:表示是否可撤销,生成可撤销的proxy对象用Proxy.revocable()方法。 那么Proxy对象可以做什么?我们看看下面例子:

      const houseOwner = {home:&#39;房源&#39;,price:1200,type:&#39;一房一厅&#39;};
      const proxyOwner = new Proxy(houseOwner,{
        get:function (target,key){
          console.log(`${key}属性被访问!`)
          return target[key];
        },
        set:function(target,key,value){
          if(target[key]===value){
            return;
          }
          target[key] = value;
          return target[key];
        },
      })
      console.log(proxyOwner);
      proxyOwner.price = 1300;// 对被代理对象的修改
      proxyOwner.remark = &#39;采光点好!&#39;;
      console.log(proxyOwner.price);// price属性被访问

    从这个例子,可以看出Proxy对象的第二个参数给代理目标带上相关属性:set方法、get方法,再回到reactive封装的数据中,数据的Handler属性给数据带上了五个属性:deletePropertygetsethasownKeys。这五个属性怎么来的?我们一起探讨一下Vue3的源码实现:

    // 源码位置:core-main/packages/reactivity/src/reactive.ts
    // Vue3中暴露给开发者的是reactive方法
    export function reactive(target: object) {
      // 判断target是否只读,是就不做处理
      if (isReadonly(target)) {
        return target
      }
      return createReactiveObject(
        target,
        false,
        mutableHandlers,
        mutableCollectionHandlers,
        reactiveMap
      )
    }

    createReactiveObject函数主要为了创建Proxy实例对象,参数传了五个属性: target(目标数据)、isReadonly(target是否只读)、mutableHandlers(ProxyHandler)、mutableCollectionHandlers(ProxyHandler类型)、proxyMap(数据集合)。我们先了解一波createReactiveObject函数:

    Detaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive

    • ReactiveFlags:响应式数据标记。

      export const enum ReactiveFlags {
        SKIP = &#39;__v_skip&#39;,// 标记对象不可进行代理
        IS_REACTIVE = &#39;__v_isReactive&#39;,// 是否是Reactive封装的
        IS_READONLY = &#39;__v_isReadonly&#39;,// 是否只读
        IS_SHALLOW = &#39;__v_isShallow&#39;,// 是否是shallowRef封装的
        RAW = &#39;__v_raw&#39;// 是否是proxy原始的target
      }
    • TargetType:target的数据类型。

      const enum TargetType {
        INVALID = 0,
        COMMON = 1,// Array、Object类型
        COLLECTION = 2 // Set、Map、WaekMap、WeakSet类型
      }
    • baseHandlers:对于Array、Object类型数据,Proxy实例的第二个参数。传入的baseHandlers就是mutableHandlers。这个函数主要是为了给Proxy对象带上五个属性。

      // 源码位置:core-main/packages/reactivity/src/baseHandlers.ts
      export const mutableHandlers: ProxyHandler<object> = {
        // createGetter() 主要实现依赖收集和Reflect.set(target, key, value, receiver)
        get,
        // createSetter() 主要实现通知依赖更新和Reflect.get(target, key, receiver)
        set,
        // deleteProperty() 主要是删除target的指定key的属性Reflect.deleteProperty(target, key)
        deleteProperty,
        // has() 主要是判断target是否存在指定key的属性,Reflect.has(target, key)
        has,
        // ownKeys() 主要是获取target的key数组,Reflect.ownKeys(target)
        ownKeys
      }
    • collectionHandlers:对于Set、Map、WaekMap、WeakSet类型数据,Proxy实例的第二个参数。传入的baseHandlers就是mutableCollectionHandlers。mutableCollectionHandlers主要是对 set、map、weakSet、weakMap 四种类型的对象进行劫持。

      // 源码位置:core-main/packages/reactivity/src/mutableCollectionHandlers.ts
      export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
        get: /*#__PURE__*/ createInstrumentationGetter(false, false)
      }
      function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
        const instrumentations = shallow? 
          isReadonly? shallowReadonlyInstrumentations: shallowInstrumentations
          : isReadonly? readonlyInstrumentations: mutableInstrumentations
      
        return (target: CollectionTypes,key: string | symbol,receiver: CollectionTypes) => {
          ...
          return Reflect.get(
            hasOwn(instrumentations, key) && key in target? instrumentations:target,
            key,
            receiver
          )
        }
      }

    总结

    • ref:定义基本数据类型、引用数据类型的响应式。封装数据类型为ref类型,主要就是创建了RefImpl实例对象
    • reactive:定义引用类型数据的响应式,不支持基本数据类型,如果需要写基本数据类型只能是放在对象中。封装数据为reactive类型,主要是创建了Proxy实例对象,通过Reflect实现数据的获取与修改。

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

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der beiden wichtigsten Responsive-Tools von Vue3: Ref und Reactive. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen