Skip to content

Reactivity API: مفاهیم پیشرفته

shallowRef

نسخه سطحی یا Shallow از ref.

triggerRef

این تابع Effectهایی را که به یک shallow ref وابسه هستند، به صورت اجباری، اجرا می‌کند. از این تابع معمولا زمانی استفاده می‌شود که بر روی value داخلی یک shallowRef تغییرات عمیق (تغییراتی که reference را تغییر نمی‌دهند) انجام شده باشد.

  • تایپ

    ts
    function triggerRef(ref: ShallowRef): void
  • مثال

    js
    const shallow = shallowRef({
      greet: 'Hello, world'
    })
    
    // Logs "Hello, world" once for the first run-through
    watchEffect(() => {
      console.log(shallow.value.greet)
    })
    
    // This won't trigger the effect because the ref is shallow
    shallow.value.greet = 'Hello, universe'
    
    // Logs "Hello, universe"
    triggerRef(shallow)

customRef

این تابع، یک ref سفارشی با امکان کنترل صریح ردیابی وابستگی (Dependency-Tracking) و زمان بروزرسانی، ایجاد می‌کند.

  • تایپ

    ts
    function customRef<T>(factory: CustomRefFactory<T>): Ref<T>
    
    type CustomRefFactory<T> = (
      track: () => void,
      trigger: () => void
    ) => {
      get: () => T
      set: (value: T) => void
    }
  • جزئیات

customRef انتظار یک تابع Factory دارد. این تابع Factory، توابع track و trigger را به عنوان آرگومان دریافت کرده و باید یک شی را با متدهای get و set برگرداند.

به طور کلی، track باید در داخل get، و trigger باید در داخل set فراخوانی شود. با این حال، کنترل اینکه چه زمانی باید آنها را فراخوانی کرد یا اینکه آیا اصلاً نیازی به فراخوانی آنها است یا خیر، با شماست.

  • مثال

ایجاد یک ref تاخیر خورده (Debounced) که فقط پس از یک بازه زمانی مشخص از آخرین فراخوانی set، مقدار را به‌روزرسانی می‌کند:

js
import { customRef } from 'vue'

export function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()
        }, delay)
      }
    }
  })
}

استفاده از آن در کامپوننت:

vue
<script setup>
import { useDebouncedRef } from './debouncedRef'
const text = useDebouncedRef('hello')
</script>

<template>
  <input v-model="text" />
</template>

در Playground امتحان کنید

هشدار با احتیاط استفاده کنید

هنگام استفاده از customRef، باید در مورد مقدار بازگشتی getter آن محتاط باشیم، به ویژه زمانی که هر بار که getter اجرا می‌شود، تایپ داده آبجکت جدید تولید می‌کند. این امر بر رابطه بین کامپوننت‌های والد و فرزند تأثیر می‌گذارد، جایی که چنین customRef به عنوان یک prop منتقل شده است.

تابع render کامپوننت والد ممکن است توسط تغییرات در یک حالت reactive دیگر فعال شود. در طول رندر مجدد، مقدار customRef ما مجدداً ارزیابی می‌شود و یک تایپ داده آبجکت جدید را به عنوان prop به یک کامپوننت فرزند برمی‌گرداند. این prop با آخرین مقدار آن در کامپوننت فرزند مقایسه می‌شود و از آنجایی که متفاوت هستند، وابستگی‌های reactive مربوط به customRef در کامپوننت فرزند فعال می‌شوند. در همین حال، وابستگی‌های reactive در کامپوننت والد اجرا نمی‌شوند زیرا setter مربوط به customRef فراخوانی نشده و در نتیجه وابستگی‌های آن فعال نشده‌اند.

آن را در Playground ببینید

shallowReactive()

نسخه سطحی یا Shallow از reactive.

  • تایپ

    ts
    function shallowReactive<T extends object>(target: T): T
  • جزئیات

برخلاف reactive، فقط ویژگی‌های سطح اول یا ریشه، به شکل reactive هستند. مقادیر ویژگی‌ها همانطور که هست ذخیره و بازیابی می‌شود - این همچنین به این معنی است که ویژگی‌های تعریف شده با ref به‌طور خودکار Unwrap نمی‌شوند.

با احتیاط استفاده کنید

داده ساختارهای Shallow، صرفا باید برای Stateهای سطح اول یا ریشه استفاده شوند. از Nest کردن آن در یک شی که به شکل عمیق Reactive است (همانند ref)، پرهیز کنید، چراکه با این کار، درخت Dependency Tracking نا پایدار شده و باعث ایجاد مشکل در فهم و یا Debug کد می‌شود.

  • مثال

    js
    const state = shallowReactive({
      foo: 1,
      nested: {
        bar: 2
      }
    })
    
    // mutating state's own properties is reactive
    state.foo++
    
    // ...but does not convert nested objects
    isReactive(state.nested) // false
    
    // NOT reactive
    state.nested.bar++

shallowReadonly

نسخه سطحی یا Shallow از readonly.

  • تایپ

    ts
    function shallowReadonly<T extends object>(target: T): Readonly<T>
  • جزئیات

برخلاف readonly، فقط ویژگی‌های سطح اول یا ریشه، به شکل readonly هستند. مقادیر ویژگی‌ها همانطور که هست ذخیره و بازیابی می‌شود - این همچنین به این معنی است که ویژگی‌های تعریف شده با ref به‌طور خودکار Unwrap نمی‌شوند.

با احتیاط استفاده کنید

داده ساختار های Shallow، صرفا باید برای Stateهای سطح اول یا ریشه استفاده شوند. از Nest کردن آن در یک شی که به شکل عمیق Reactive است (همانند ref)، پرهیز کنید، چراکه با این کار، درخت Dependency Tracking نا پایدار شده و باعث ایجاد مشکل در فهم و یا Debug کد می‌شود.

  • مثال

    js
    const state = shallowReadonly({
      foo: 1,
      nested: {
        bar: 2
      }
    })
    
    // mutating state's own properties will fail
    state.foo++
    
    // ...but works on nested objects
    isReadonly(state.nested) // false
    
    // works
    state.nested.bar++

toRaw

شیء خام و اصلی یک پروکسی ایجاد شده از Vue را برمی‌گرداند.

  • تایپ

    ts
    function toRaw<T>(proxy: T): T
  • جزئیات

toRaw می تواند شی اصلی که به کمک یکی از توابع reactive, readonly, shallowReactive و یا shallowReadonly ایجاد شده است، برگرداند.

این یک ترفند است که می‌تواند برای خواندن موقت بدون نیاز به دسترسی به Proxy / سربار Dependency Tracking و یا نوشتن بدون ایجاد Reactive Effect استفاده شود. اینکه یک ارجاع مدارم به خروجی این تابع داشته باشید، توصیه نمی‌شود. با احتیاط استفاده کنید.

  • مثال

    js
    const foo = {}
    const reactiveFoo = reactive(foo)
    
    console.log(toRaw(reactiveFoo) === foo) // true

markRaw

یک شی را طوری علامت گذاری می‌کند که هرگز به Proxy تبدیل نشود و به عنوان خروجی، خود شی را برمی‌گرداند.

  • تایپ

    ts
    function markRaw<T extends object>(value: T): T
  • مثال

    js
    const foo = markRaw({})
    console.log(isReactive(reactive(foo))) // false
    
    // also works when nested inside other reactive objects
    const bar = reactive({ foo })
    console.log(isReactive(bar.foo)) // false

    با احتیاط استفاده کنید

    markRaw و APIهای سطحی مانند shallowReactive به شما این امکان را می‌دهند که به طور انتخابی یا Opt-in از تبدیل عمیق reactive/readonly، که به صورت پیش‌فرض انجام می شود، خودداری کنید و اشیاء Raw و غیر Proxy شده را در گراف State خود قرار کنید. دلیل استفاده از این API ها می‌تواند موارد زیر باشد:

    • برخی مقادیر، نیازی به Reactive شدن ندارند، برای مثال، یک شی پیچیده از کلاس کتابخانه Third-Party و یا یک شی کامپوننت Vue.

    • Skip کردن تبدیل Proxy، می‌تواند هنگام رندر فهرست‌های بزرگ با منابع داده Immutable و یا غیر قابل تغییر، عملکرد را بهبود بخشد.

    این APIها پیشرفته در نظر گرفته می‌شوند، چراکه Skip کردن Reactivity صرفا در سطح ریشه است، بنابراین اگر یک شی تودرتو که با markRaw علامت‌گذاری نشده را در یک شی Reactive تنظیم کنید و سپس دوباره به آن دسترسی پیدا کنید، نسخه Proxy شده آن را دریافت می‌کنید. این می‌تواند منجر به خطرات هویتی شود - یعنی انجام عملیاتی که بر هویت شی متکی است، اما از هر دو نسخه Raw و Proxy یک شی استفاده می‌کند:

    js
    const foo = markRaw({
      nested: {}
    })
    
    const bar = reactive({
      // although `foo` is marked as raw, foo.nested is not.
      nested: foo.nested
    })
    
    console.log(foo.nested === bar.nested) // false

    خطرات هویتی به طور کلی نادر هستند. با این حال، برای استفاده صحیح از این APIها و همچنین اجتناب از خطرات هویتی، نیاز به درک کاملی از نحوه عملکرد سیستم واکنش پذیری وجود دارد.

effectScope

یک شی Effect Scope ایجاد می‌کند که می‌تواند Effectهای Reactivity (همانند computed و یا watch) ایجاد شده در خود را نگه دارد تا این Effect ها با هم حذف شوند. برای موارد استفاده دقیق از این API، لطفاً به RFC مربوطه آن مراجعه کنید.

  • تایپ

    ts
    function effectScope(detached?: boolean): EffectScope
    
    interface EffectScope {
      run<T>(fn: () => T): T | undefined // undefined if scope is inactive
      stop(): void
    }
  • مثال

    js
    const scope = effectScope()
    
    scope.run(() => {
      const doubled = computed(() => counter.value * 2)
    
      watch(doubled, () => console.log(doubled.value))
    
      watchEffect(() => console.log('Count: ', doubled.value))
    })
    
    // to dispose all effects in the scope
    scope.stop()

getCurrentScope

effect scope فعال را (در صورتی که اصلا وجود داشته باشد) بر می گرداند.

  • تایپ

    ts
    function getCurrentScope(): EffectScope | undefined

onScopeDispose

ثبت یک Callback بر روی رویداد پاک شدن effect scope فعال. این Callback زمانی فراخوانی می‌شود که Effect Scope متوقف شده باشد.

این تابع می تواند به عنوان جایگزین onUnmounted، در توابع Composition استفاده شود با این تفاوت که وابستگی به خود کامپوننت نخواهد داشت. عدم این وابستی بدین خاطر است که تابع setup از کامپوننت Vue، خود در یک Effect Scope اجرا می شود.

  • تایپ

    ts
    function onScopeDispose(fn: () => void): void
Reactivity API: مفاهیم پیشرفته has loaded