Maison  >  Article  >  interface Web  >  Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif

Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif

青灯夜游
青灯夜游avant
2023-01-09 18:32:552174parcourir

Par rapport à la réactivité des données implémentée par DefineProperty de Vue2, Vue3 a une division du travail plus claire dans le traitement des réponses aux données. Les données sont regroupées via les deux fonctions ref et réactive exposées aux développeurs dans l'API combinée, obtenant ainsi une réactivité des données. la différence ? Apprenons ensemble avec des exemples !

Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif

ref : Réactivité qui définit les types de données de base et les types de données de référence. En d'autres termes, ref(value), ce type de valeur peut être un type de données de base ou un type de données de référence, mais lorsqu'il est utilisé dans js, il doit être utilisé au format property.value. être utilisé directement dans le modèle Données d'appel. 定义基本数据类型、引用数据类型的响应式。也就是说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内部封装结构是什么样的?如下三图所示

Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif

Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif

Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif从上面图片可以看出,不管是什么类型的数据,对于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就是为了区分引用类型数据是否做响应式处理。

Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif

  • 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)
    }

    🎜reactive🎜 : Définissez la réponse des données de type référence, qui ne prennent pas en charge les types de données de base Si vous devez écrire des types de données de base, vous ne pouvez les mettre que dans l'objet, c'est-à-dire. , reactive(value), Ce type de valeur doit être un type de référence. [Recommandations associées : tutoriel vidéo vuejs, développement web front-end]🎜
    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);
    }
    🎜À partir des deux exemples ci-dessus, nous pouvons voir que peu importe ce que Type data et ref doivent utiliser .value pour appeler les données définies par ref. Pour les types de données de référence, nous pouvons voir que le code n'est pas beau, donc généralement 🎜Pour les données de type référence, il est recommandé d'utiliser réactif Pour définir 🎜;Pour les types de données de base, vous pouvez utiliser ref ou réactif pour définir 🎜. Maintenant que nous comprenons tous la différence entre l'utilisation de ref et reactive ici, continuons à explorer les différences dans leurs principes de réponse ? 🎜

    🎜Révéler la référence🎜

    🎜À partir de l'exemple ci-dessus, imprimons et examinons d'abord les éléments internes de référence du type de données de base (strRef) et du type de données de référence (arrRef , ObjRef) À quoi ressemble la structure du packaging ? Comme le montrent les trois images suivantes🎜🎜Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif🎜🎜Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif🎜 🎜Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactifDepuis le Dans l'image ci-dessus, vous pouvez voir que quel que soit le type de données, les données encapsulées par la référence sont un 🎜Objet RefImpl🎜 L'abréviation de implémentation de référence signifie implémentation de référence. attributs : 🎜
    • 🎜🎜dep🎜 : Il s'agit d'une donnée de type Set, utilisée pour stocker les dépendances collectées par la valeur de référence actuelle. 🎜
    • 🎜🎜_ v _ isRef🎜 : Mark bit, tant qu'il est défini par ref, il marquera les données actuelles comme Ref, c'est-à-dire que sa valeur est marquée comme vraie. 🎜
    • 🎜🎜_ v _ isShallow🎜 : Déterminez s'il s'agit de données définies par ShallowRef. 🎜
      🎜Différent de ref, lorsque ShallowRef est utilisé pour créer une réactivité pour un type de référence, les attributs profonds sont modifiés et il n'y a pas de réactivité. Déclenché uniquement pour les références à .value. 🎜
      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🎜 : utilisé pour enregistrer la valeur d'origine correspondant à la valeur de référence actuelle, si le paramètre passé est un objet , qui est <code>utilisé pour enregistrer la valeur d'origine avant la conversion, sinon _ value est la même que _ rawValue. 🎜
    • 🎜🎜_ value🎜 : utilisé pour enregistrer la valeur actuelle de ref Si le paramètre passé est un objet, c'est . utilisé pour enregistrer La valeur convertie par la fonction réactive, sinon _ value est la même que _ rawValue. À partir de l'exemple ci-dessus, nous pouvons constater que pour les données de type référence, leur valeur est un objet proxy, qui est en fait l'objet réactif après encapsulation des données (discuté plus tard). Regardons d'abord l'image ci-dessous et constatons que _ rawValue est la valeur d'origine sans traitement réactif. Ensuite, nous examinons _ value qui est un objet proxy qui a été traité avec une réponse réactive. 🎜
      🎜🎜_ rawValue🎜 et 🎜_value🎜 servent à distinguer si les données de type référence doivent être traitées de manière réactive. 🎜
    🎜image. png🎜
    • 🎜🎜value🎜 : La valeur actuelle est enregistrée. 🎜
    🎜Maintenant que nous savons quels attributs ref encapsule pour les données, commençons à explorer comment le code source attribue des valeurs aux 6 attributs ci-dessus : 🎜
    • 🎜🎜fonction ref 🎜 : Ce que Vue3 expose aux développeurs, c'est la fonction ref. En fait, elle encapsule une fonction createRef. 🎜
      // 源码位置: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
        )
      }
    • 🎜🎜fonction createRef🎜 : il y a deux paramètres, l'un est les données auxquelles répondre pour le traitement, et l'autre est pour déterminer si les données sont données définies par ShallowRef. Ce qu'il fait principalement est de déterminer si la valeur brute actuelle (données qui n'ont pas encore été traitées pour la réponse) est une donnée de type ref et de créer un objet d'instance RefImpl. 🎜<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,所以在调用的时候,需要带上。

    Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif

    其实,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);
    }

    Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif从上图可以看出,使用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函数:

    Explication détaillée des deux principaux outils réactifs de Vue3 : ref et réactif

    • 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入门教程编程基础视频

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer