Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명

青灯夜游
풀어 주다: 2023-01-10 20:28:55
앞으로
2260명이 탐색했습니다.

Vue2의 DefineProperty로 구현된 데이터 응답성에 비해 Vue3는 데이터 응답 처리에서 더 명확한 업무 분담을 가지고 있으며, 결합된 API에서 개발자에게 노출되는 ref 및 반응성이라는 두 가지 기능을 통해 데이터를 패키징하여 데이터 응답성을 달성합니다. 차이점은 무엇입니까? 예시를 통해 함께 배워보아요!

Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명

ref: 기본 데이터 유형과 참조 데이터 유형을 정의하는 응답성. 즉, ref(value), 이 값 유형은 기본 데이터 유형일 수도 있고 참조 데이터 유형일 수도 있지만, js에서 사용할 때는 property.value 형식으로 사용해야 합니다. 템플릿 통화 데이터에서 직접 사용할 수 있습니다. 定义基本数据类型、引用数据类型的响应式。也就是说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内部封装结构是什么样的?如下三图所示

Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명

Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명

Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명从上面图片可以看出,不管是什么类型的数据,对于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就是为了区分引用类型数据是否做响应式处理。

Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명

  • 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🎜: 기본 데이터 유형을 지원하지 않는 참조 유형 데이터의 응답을 정의합니다. 기본 데이터 유형을 작성해야 하는 경우 객체에만 넣을 수 있습니다. , 반응성(값), 이 값 유형은 참조 유형이어야 합니다. [관련 권장사항: vuejs 비디오 튜토리얼, 웹 프론트엔드 개발]🎜
    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);
    }
    로그인 후 복사
    로그인 후 복사
    🎜위의 두 가지 예에서 알 수 있는 것은 어떤 유형 데이터와 ref가 ref로 정의된 데이터를 호출하려면 .value를 사용해야 합니다. 참조 데이터 유형의 경우 코드가 아름답지 않다는 것을 알 수 있으므로 일반적으로 🎜참조 유형 데이터의 경우, 🎜기본 데이터 유형의 경우 ref 또는 반응형을 사용하여 🎜을 정의할 수 있습니다. 이제 우리 모두 여기서 ref와 반응 사용의 차이점을 이해했으므로 응답 원리의 차이점을 계속해서 살펴보겠습니다. 🎜

    🎜Revealing ref🎜

    🎜위의 예에서 먼저 기본 데이터 유형(strRef)과 참조 데이터 유형(arrRef)의 참조 내부를 인쇄하고 살펴봅니다. , ObjRef) 포장 구조는 어떻게 생겼나요? 다음 세 장의 사진과 같이🎜🎜Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명🎜🎜Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명🎜 🎜Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명에서 위 그림을 보면 어떤 데이터 유형이든 ref 캡슐화된 데이터는 🎜RefImpl 객체🎜라는 것을 알 수 있습니다. referenceimplement의 약어는 각 RefImpl 객체가 6개라는 의미입니다. 속성: 🎜
    • 🎜🎜dep🎜: 현재 참조 값으로 수집된 종속성을 저장하는 데 사용되는 Set 유형 데이터입니다. 🎜
    • 🎜🎜_ v _ isRef🎜: 표시 비트. ref로 정의된 한 현재 데이터를 Ref로 표시합니다. 즉, 해당 값이 true로 표시됩니다. 🎜
    • 🎜🎜_ v _ isShallow🎜:shallowRef로 정의된 데이터인지 확인합니다. 🎜
      🎜ref와는 달리,shallowRef를 사용하여 참조 유형에 대한 응답성을 생성하면 깊은 속성이 수정되고 응답성이 없습니다. .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🎜: 전달된 매개변수가 객체<인 경우 <code>현재 참조 값에 해당하는 원래 값을 저장하는 데 사용됩니다. /code> , 변환 전 원래 값을 저장하는 데 사용. 그렇지 않은 경우 _ value는 _ rawValue와 동일합니다. 🎜
    • 🎜🎜_ value🎜: ref의 현재 값을 저장하는 데 사용됩니다. 전달된 매개변수가 객체인 경우 입니다. 저장하는 데 사용됩니다. 반응 함수에 의해 변환된 값입니다. 그렇지 않으면 _값은 _rawValue와 같습니다. 위의 예에서 우리는 참조 유형 데이터의 경우 그 값이 프록시 객체라는 것을 알 수 있습니다. 이는 실제로 데이터를 캡슐화한 후의 반응형 객체입니다(나중에 설명합니다). 먼저 아래 그림을 보고 _rawValue가 반응형 처리가 되지 않은 원래 값임을 확인합니다. 그런 다음 반응형 응답으로 처리된 값인 프록시 객체인 _value를 살펴보겠습니다. 🎜
      🎜🎜_ rawValue🎜 및 🎜_ value🎜는 참조 유형 데이터를 응답적으로 처리해야 하는지 여부를 구분하기 위한 것입니다. 🎜
    🎜이미지. png🎜
    • 🎜🎜value🎜: 현재 값이 저장됩니다. 🎜
    🎜이제 ref가 데이터에 대해 어떤 속성을 캡슐화하는지 알았으니 소스 코드가 위의 6가지 속성에 값을 할당하는 방법을 살펴보겠습니다. 🎜
    • 🎜🎜ref 함수 🎜: Vue3가 개발자에게 공개하는 것은 실제로 createRef 함수를 캡슐화하는 ref 함수입니다. 🎜
      // 源码位置: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 함수🎜: 두 개의 매개변수가 있습니다. 하나는 처리에 대해 응답할 데이터이고, 다른 하나는 데이터가 다음과 같은지 여부를 결정하는 입니다. shallowRef< /code>로 정의된 데이터입니다. 주로 하는 일은 현재 rawValue(아직 응답 처리되지 않은 데이터)가 ref 타입 데이터인지 판단하고 RefImpl 인스턴스 객체를 생성하는 것입니다. 🎜<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 createRef(rawValue: unknown, shallow: boolean) { if (isRef(rawValue)) { return rawValue } return new RefImpl(rawValue, shallow) }</pre><div class="contentsignin">로그인 후 복사</div></div><div class="contentsignin">로그인 후 복사</div></div></li><li><p><strong>RefImpl类</strong>:创建RefImpl类给_ rawValue和_ value属性赋值,判断当前定义的ref数据是否为shallowRef定义的数据,然后获取响应性值时对数据依赖进行收集并返回_ value,修改响应式值时修改并通知依赖更新。</p><blockquote><p><strong>ref定义的数据为什么需要带.value调用数据?</strong> 就是<code>因为RefImpl类暴露给实例对象的get、set方法是value,所以在调用的时候,需要带上。

    Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명

    其实,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);
    }
    로그인 후 복사
    로그인 후 복사

    Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명从上图可以看出,使用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函数:

    Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명

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

      위 내용은 Vue3의 두 가지 주요 반응형 도구인 ref 및 반응성에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿